Advertisement
VVish

yoboa

Aug 16th, 2018
186
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 52.49 KB | None | 0 0
  1. // ///////////////////////////////////////////////////////////////////
  2. // Комбат сперва собирает массив данных. В дальнейшем он по минимуму обращается к криттерам или чему-то еще.
  3. // Например многочисленный (valid(target)) заменяется tr.Valid, который по умолчанию false.
  4. // Добавление констант осуществляется в структуры attack. at. и tr. сверху. В самой функции они вызываются с параметрами по дефолту,
  5. // а потом заполняются. Если позиция структуры заполняется не сразу, а после каких-то вычислений, то в этом месте стоит указатель !!!STRUCK UPDATE!!!
  6. // 90% переменных функции дефайнится еще до первого просчета на toHit.
  7. // Струк таргета заполняется !ТОЛЬКО! при наличии валидного таргета.
  8. // Струк attack. также была значительно расширен в хранимых параметрах.
  9. // Хранимое в структуре ВСЕГДА должно быть приоритетнее временных значений.
  10. // Если вызывается итоговый ApplyDamage для нескольких криттеров в массиве, то перед отправкой ОБЯЗАТЕЛЬНО вносить валидного криттера[i] в @attack.Target;
  11.  
  12. void CombatAttack( Critter& cr, Critter@ target, ProtoItem& weapon, uint8 weaponMode, ProtoItem@ ammo, uint16 hexX, uint16 hexY, int slot ) // Export
  13. {
  14. // ///////////////////////////////////////////////////////////////////
  15. // --------------------------------------------------------- MODULATED
  16. // ----------------------------------------------------------- RETURNS
  17. if( !valid( target ) && hexX == 0 && hexY == 0 ) return;
  18. if( valid( target ) )
  19. {
  20. hexX = target.HexX;
  21. hexY = target.HexY;
  22. }
  23. if (valid(target) && valid(cr))
  24. {
  25. if ((target.StatBase[ST_FACTION]==16) && (cr.StatBase[ST_FACTION]==16)) return;
  26. }
  27.  
  28. Map@ map = cr.GetMap();
  29. if(!valid(map)) return;
  30. bool DebugCom = (cr.IsPlayer() && cr.StatBase[ST_NPC_ROLE]>0 && (cr.Name=="Admin"|| cr.Name=="admin"));
  31.  
  32. //int mapTime = map.GetTime();
  33. //bool isGlobalTimeMap = (mapTime == -1);
  34. bool trValid = valid(target);
  35. // ///////////////////////////////////////////////////////////////////
  36. // ------------------------------------------------- CR PERKS && STATS
  37. CritterStruct at;
  38. at = _GetAtStruct(cr);
  39.  
  40.  
  41. // ///////////////////////////////////////////////////////////////////
  42. // ------------------------------------------------- TR PERKS && STATS
  43. TargetStruct tr;
  44. if(trValid) tr = _GetTrStruct(target);
  45. tr.Valid = trValid;
  46.  
  47. // ///////////////////////////////////////////////////////////////////
  48. // ------------------------------------------------------ MAIN DEFINES
  49. Critter@ realTarget;
  50. Critter@ normalTarget;
  51. CombatRes[] results;
  52. if(trValid) @realTarget = target;
  53.  
  54. bool isHit = false;
  55. bool isCritical = false;
  56. bool hitRandomly = false;
  57. bool useNormal = false;
  58. bool useHex = false;
  59. uint critfailFlags = 0;
  60. int acmod = 0;
  61.  
  62. uint16 hx = cr.HexX;
  63. uint16 hy = cr.HexY;
  64. uint16 tx = ( trValid ) ? target.HexX : hexX;
  65. uint16 ty = ( trValid ) ? target.HexY : hexY;
  66.  
  67. uint8 use = _WeaponModeUse( weaponMode );
  68. uint8 aim = _WeaponModeAim( weaponMode );
  69. Item@ realWeapon;
  70. if( slot != 5 )
  71. @realWeapon = _CritGetItemHand( cr );
  72. else
  73. @realWeapon = _CritGetItemArmorExt( cr );
  74.  
  75. bool validWeap = valid( realWeapon );
  76. bool validAmmo = valid( ammo );
  77. // cr.Say( SAY_NETMSG, "Ammo-"+validAmmo );
  78. if( cr.Damage[DAMAGE_RIGHT_ARM] != 0 && cr.Damage[DAMAGE_LEFT_ARM] != 0 )
  79. {
  80. cr.SayMsg( SAY_NETMSG, TEXTMSG_COMBAT, 105 );
  81. return;
  82. }
  83. if(validWeap && FLAG(realWeapon.Flags, ITEM_TWO_HANDS))
  84. {
  85. if(cr.Damage[DAMAGE_RIGHT_ARM] != 0 || cr.Damage[DAMAGE_LEFT_ARM] != 0 )
  86. {
  87. cr.SayMsg( SAY_NETMSG, TEXTMSG_COMBAT, 106 );
  88. return;
  89. }
  90. }
  91. if(validWeap && realWeapon.IsDeteriorable() && realWeapon.Deterioration >= MAX_DETERIORATION)
  92. {
  93. cr.SayMsg( SAY_NETMSG, TEXTMSG_COMBAT, 105 );
  94. return;
  95. }
  96. uint16 weapPid = weapon.ProtoId;
  97. if(weapPid == PID_FLARE) weapPid = PID_ACTIVE_FLARE;
  98.  
  99. int skillNum = _WeaponSkill( weapon, use );
  100.  
  101. int wpnMaxDist = _WeaponMaxDist( weapon, use )+GetBonusWp(realWeapon, 8);
  102. if( skillNum == SK_THROWING && weapPid != 898)
  103. wpnMaxDist = MIN( wpnMaxDist, 3 * MIN( int(10), ( at.Str ) ) );
  104. if( weapPid == 7960 ) wpnMaxDist = 100;
  105.  
  106. bool isAlwaysAlt = ( Present(weapPid, Pids_AlwaysAlt) );
  107.  
  108. if( validWeap )
  109. if( at.IsEngineer && Present(realWeapon.GetProtoId(), Pids_SupportPositions ) )
  110. skillNum = SK_REPAIR;
  111. if( slot == 99 )
  112. skillNum = _WeaponSkill( GetProtoItem( realWeapon.GetProtoId() ), use );
  113. int skillVal = cr.Skill[ skillNum ] + cr.Stat[ST_SKILL_MOD];
  114. int skillreal=0;
  115. // if (skillVal<151) skillreal=skillVal;
  116. // if (skillVal>150) skillreal=150+(skillVal-150)/2;
  117. //if (skillVal>200) skillreal=150+(skillVal-200)/2;
  118. int baseToHit = skillVal;
  119. int toHit = 0;
  120. int distmod1 = 0;
  121. int distmod2 = 0;
  122. int acc = 0;
  123. int accloss = 0;
  124. int sharpshooter = (at.IsSharpshooter) ? 2 : 0;
  125. int blockers = 0;
  126.  
  127. uint8 weaponSubtype = ( skillNum == SK_SMALL_GUNS || skillNum == SK_BIG_GUNS || skillNum == SK_ENERGY_WEAPONS || Present(weapon.ProtoId, Pids_WS_GUN) ) ? WS_GUN : ( ( skillNum == SK_THROWING ) ? WS_THROWING : ( skillNum == SK_MELEE_WEAPONS ) ? WS_MELEE : WS_UNARMED );
  128.  
  129. bool isRanged = ( weaponSubtype == WS_THROWING || weaponSubtype == WS_GUN );
  130. bool isUnarmed = weapon.Weapon_IsUnarmed;
  131. bool isHthAttack = ( weaponSubtype == WS_MELEE || weaponSubtype == WS_UNARMED );
  132.  
  133. int preBonusDmg = 0;
  134. int smokePen = (tr.Valid) ? (GetSmokePenalty( map, cr, target, tx, ty )) : 0;
  135. // if (smokePen>0) smokePen -=cr.StatBase[ST_PERCEPTION];
  136. int margin = 0;
  137. uint8 aimOld = aim;
  138. uint fireDamage = 5;
  139.  
  140. int skilllvl = 0;
  141. int maxBurstD = 0;
  142. int minBurstD = 0;
  143. int wpdist = 0;
  144. int perdamage = 0;
  145.  
  146. at.CriticalCh=cr.Stat[ST_CRITICAL_CHANCE] + GetHitAim(aim)+cr.Perk[PE_NCR_PERCEPTION]*4+GetBonusWp(realWeapon,4);
  147.  
  148. // ///////////////////////////////////////////////////////////////////
  149. // ---------------------------------------------------- WEAPON DEFINES
  150. int weaponPerk = weapon.Weapon_Perk;
  151. int weaponBonusPerk = 0;
  152. // int b1=item.Val3; int b2=item.Val4; int b3=item.Val8; int b4=item.Val1;
  153.  
  154. bool wpnIsRemoved = _WeaponRemove( weapon, use );
  155. int dmgType = _WeaponDmgType( weapon, use );
  156. bool isGrenade = ( weaponSubtype == WS_THROWING && ( dmgType == DAMAGE_PLASMA || dmgType == DAMAGE_EMP || dmgType == DAMAGE_ELECTR || dmgType == DAMAGE_EXPLODE || dmgType == DAMAGE_FIRE ) && weapon.ProtoId != 7960 );
  157. bool isFlamethrower = ( Present( weapPid, Pids_Flamers ) );
  158. int weapStr = weapon.Weapon_MinStrength;
  159. bool weapIsTwoHand = FLAG( weapon.Flags, ITEM_TWO_HANDS );
  160. uint weapPower = weapon.Cost / 100;
  161. bool isPlaser = ( validWeap && realWeapon.Val7 == PID_P_LASER );
  162. // ///////////////////////////////////////////////////////////////////
  163. // ------------------------------------------------------ AMMO DEFINES
  164. uint16 ammoPid = (validAmmo) ? ammo.ProtoId : 0;
  165. uint16 ammoRound = _WeaponRound( weapon, use );
  166. uint ammoCount = (validWeap) ? realWeapon.AmmoCount : 0;
  167. //uint ammoWeight = (validAmmo) ? ammo.Weight : 0;
  168. int AmmoAp = validWeap ? _WeaponApCost(weapon, use):4; AmmoAp -= (cr.Perk[PE_BONUS_RATE_OF_FIRE]!=0?1:0)+(cr.Trait[TRAIT_FAST_SHOT]!=0?1:0);
  169. if (! isHthAttack && cr.Perk[PE_ADD_ATAC]!=0 && Random(0,100)<(AmmoAp*10)) cr.StatBase[ST_CURRENT_AP]+=2*100;
  170. bool isRocket = ( Present(ammoPid, Pids_RocketAmmo) || weapPid == PID_SHAITAN || weapPid == 7960 );
  171. bool isBurst = ( ammoRound > 1 );
  172. bool isThrowRelayted = ( isGrenade || ammoPid == 651 || ammoPid == 652 || ammoPid == 653 || ammoPid == 7961 );
  173. bool isPG = (ammoPid == 651 || ammoPid == 652 || ammoPid == 653);
  174. cr.StatBase[ST_AUCTION]=0;
  175. if (isPG) cr.StatBase[ST_AUCTION]=ammoPid;
  176. int bulletPower =0;
  177. int supressRounds = 0;
  178. int ammoAC = (validAmmo) ? ammo.Ammo_ACMod : 0;
  179. if (DebugCom) cr.Say( SAY_NETMSG, "Ammo-"+ammoPid+" ПидРужья-"+weapPid+" slot-"+slot+" Dtyp-"+dmgType);
  180. // ///////////////////////////////////////////////////////////////////
  181. // ------------------------------------------------ PREWORK EXCEPTIONS
  182. if( isBurst ) aim = HIT_LOCATION_UNCALLED;
  183. uint trPidArmor=0; uint crPidArmor=0;
  184. Item@ armorcr =_CritGetItemArmor(cr); if(valid(armorcr)) crPidArmor = armorcr.GetProtoId();
  185. if( tr.Valid )
  186. {
  187. Item@ armortr =_CritGetItemArmor(target);
  188. if(valid(armortr)) trPidArmor = armortr.GetProtoId();
  189. }
  190. if( slot == 99 && isPG) dmgType = ammo.Weapon_DmgType_0;
  191. if( weapPid == 7909 ) isBurst = false;
  192. if( isPlaser || weaponPerk == WEAPON_PERK_NIGHT_SIGHT ) smokePen = smokePen/2;
  193. if( at.PidSlot1 == PID_TEPLOVISOR) smokePen = 0;
  194. if (DebugCom) cr.Say( SAY_NETMSG, "Ammo-"+ammoPid+" round-"+ammoRound+" slot-"+slot+" Dtyp-"+dmgType);
  195. // ///////////////////////////////////////////////////////////////////
  196. // --------------------------------------------------- MAIN APPEARANCE
  197. if( !map.IsTurnBased() && map.IsTurnBasedAvailability() ) map.BeginTurnBased( cr );
  198.  
  199. if( at.IsHidden ) cr.ModeBase[ MODE_HIDE ] = 0;
  200. if( tr.IsHidden ) target.ModeBase[ MODE_HIDE ] = 0;
  201.  
  202. cr.SetDir( GetDirection( hx, hy, tx, ty ) );
  203.  
  204. int crTimeout = BattleTime( cr);
  205. if( weapPid == 7960 ) crTimeout=__FullSecond+ (crTimeout - __FullSecond)/2;
  206. if(cr.Timeout[ TO_BATTLE ] < crTimeout - __FullSecond) cr.TimeoutBase[ TO_BATTLE ] = crTimeout;
  207. cr.TimeoutBase[ TO_SNEAK ] = SNEAK_TIMEOUT( cr );
  208. if( tr.Valid )
  209. {
  210. int targetTimeout = BattleTime(target);
  211. if( target.Timeout[ TO_BATTLE ] < targetTimeout - __FullSecond ) target.TimeoutBase[ TO_BATTLE ] = targetTimeout;
  212. target.TimeoutBase[ TO_SNEAK ] = SNEAK_TIMEOUT( target );
  213. target.EventAttacked( cr );
  214. }
  215. // Add scores
  216. if( at.IsPlayer )
  217. {
  218. if( weaponSubtype == WS_GUN ) cr.AddScore( SCORE_SHOOTER, 1 );
  219. else if( skillNum == SK_MELEE_WEAPONS || skillNum == SK_THROWING ) cr.AddScore( SCORE_MELEE, 1 );
  220. else if( skillNum == SK_UNARMED ) cr.AddScore( SCORE_UNARMED, 1 );
  221. }
  222. // Npc attack text
  223. if( !at.IsPlayer ) AI_TrySayCombatText( cr, COMBAT_TEXT_ATTACK );
  224.  
  225.  
  226. // ///////////////////////////////////////////////////////////////////
  227. // ------------------------------------------------ AMMO MANIPULATIONS
  228.  
  229. if( validAmmo )
  230. {
  231. if(weaponSubtype == WS_GUN)
  232. {
  233. uint ammoDT=ammo.SoundId;
  234. if (ammoDT<8) dmgType = ammoDT;
  235. if (ammoPid==PID_NAPALM) weaponBonusPerk == WEAPON_PERK_FLAMEBOY;
  236. }
  237. }
  238. // ///////////////////////////////////////////////////////////////////
  239. // -------------------------------------------------- ATTACK STRUCTURE
  240. AttackStruct attack;
  241. @attack.Attacker = cr;
  242. @attack.Target = realTarget;
  243. @attack.RealWeapon = realWeapon;
  244. @attack.RealMap = map;
  245. attack.IsNight =(slot==99);
  246. attack.Hx = hx;
  247. attack.Hy = hy;
  248. attack.Aim = aim;
  249. attack.SkillNum = skillNum;
  250. attack.SkillVal = skillVal;
  251. attack.IsBurst = isBurst;
  252. attack.IsFlamethrower = isFlamethrower;
  253. attack.IsRocket = isRocket;
  254. attack.IsGrenade = isGrenade;
  255. attack.IsHthAttack = isHthAttack;
  256. attack.IsSingleAttack = (!attack.IsBurst && !attack.IsFlamethrower && !attack.IsRocket && !attack.IsGrenade && !attack.IsHthAttack);
  257. attack.IsAlwaysAlt = isAlwaysAlt;
  258. attack.BloodyMess = at.IsBloody;
  259. attack.CombatMessage = true;
  260. attack.scoreUnarmed = ( weaponSubtype == WS_UNARMED );
  261. attack.WeaponPerk = ( isUnarmed && weapon.Weapon_UnarmedArmorPiercing ) ? WEAPON_PERK_PENETRATE : weaponPerk;
  262. attack.WeaponBonusPerk = weaponBonusPerk;
  263. attack.WeaponUpgrade = 0;
  264. attack.WeaponSubtype = weaponSubtype;
  265. attack.Profficiency = 0;
  266. attack.DmgMin = ( skillNum == SK_REPAIR && !at.IsEngineer ) ? _WeaponDmgMin( weapon, use ) - _WeaponDmgMin( weapon, use ) / 4 + attack.Profficiency: _WeaponDmgMin( weapon, use ) + attack.Profficiency;
  267. attack.DmgMax = ( skillNum == SK_REPAIR && !at.IsEngineer ) ? _WeaponDmgMax( weapon, use ) - _WeaponDmgMax( weapon, use ) / 4: _WeaponDmgMax( weapon, use );
  268. attack.DmgType = dmgType;
  269. attack.BonusDmg = preBonusDmg;
  270. attack.PlasmaBurn = 8;
  271. attack.FireDamage = fireDamage;
  272. attack.Dist = GetDistantion( hx, hy, tx, ty );
  273. attack.ACMod = ammoAC;
  274. at.MasterPerk = (at.IsPlayer) ? cr.Perk[ attack.DmgType + 450 ] : 0;
  275. cr.StatBase[ST_EMP_RESIST_EXT]=0;
  276.  
  277. attack.DmgMin+=GetBonusWp(realWeapon, 0);
  278. attack.DmgMax+=GetBonusWp(realWeapon, 1);
  279.  
  280.  
  281. if( attack.IsHthAttack )
  282. {
  283. if(weaponSubtype == WS_MELEE)
  284. {
  285. attack.DmgMax += at.MeleeDmg/2; // !!!STRUCK UPDATE!!!
  286. attack.DmgMin += at.MeleeDmg/2; // !!!STRUCK UPDATE!!!
  287. }
  288. else
  289. {
  290. attack.DmgMax += at.MeleeDmg; // !!!STRUCK UPDATE!!!
  291. attack.DmgMin += at.MeleeDmg; // !!!STRUCK UPDATE!!!
  292. }
  293. }
  294. // if( weaponSubtype == WS_GUN && isBurst) attack.BonusDmg += at.BRD * ((attack.DmgMin+attack.DmgMax)*at.BaseLuck/50+1);
  295. if(attack.DmgType == DAMAGE_FIRE)
  296. {
  297. if( at.IsPyro || ammoPid == PID_NAPALM)
  298. {
  299. if(at.IsPyro) attack.BonusDmg +=8 ;
  300. attack.BonusDmg += 7;
  301. attack.ExplodeRad += 1; // !!!STRUCK UPDATE!!!
  302. }
  303.  
  304. if( !at.IsKind) attack.FireDamage = attack.BonusDmg + at.MasterPerk*2;
  305. else attack.FireDamage = attack.BonusDmg;
  306. }
  307.  
  308. attack.DmgMul = 1; // !!!STRUCK UPDATE!!!
  309. if( valid( ammo ) )
  310. {
  311. attack.DRMod = ammo.Ammo_DRMod; // !!!STRUCK UPDATE!!!
  312. attack.DMMod = ammo.Ammo_DmgMult; // !!!STRUCK UPDATE!!!
  313. attack.DDMod = ammo.Ammo_DmgDiv; // !!!STRUCK UPDATE!!!
  314. // cr.Say( SAY_NETMSG, "Дмод-"+attack.DDMod);
  315. if( attack.DMMod == 0 )
  316. attack.DMMod = 1; // !!!STRUCK UPDATE!!!
  317. if( attack.DDMod == 0 )
  318. attack.DDMod = 1; // !!!STRUCK UPDATE!!!
  319. }
  320.  
  321. // ///////////////////////////////////////////////////////////////////
  322. // ----------------------------- STRUCK >> DIRECT LINK FROM THIS POINT
  323. // ///////////////////////////////////////////////////////////////////
  324. // ----------------------------------------------- AFTERATTACK DEFINES
  325. if( weapPid == 350 || weapPid == 873)
  326. attack.WeaponBonusPerk = WEAPON_PERK_NIGHT_SIGHT;
  327. //if (weapPid==PID_LIGHT_SUPPORT_WEAPON || weapPid==PID_AVENGER_MINIGUN) attack.WeaponBonusPerk=WEAPON_PERK_LONG_RANGE;
  328. //if( weapPid == PID_SOLAR_SCORCHER) attack.WeaponBonusPerk = WEAPON_PERK_FLAMEBOY;
  329.  
  330. int dist = attack.Dist;
  331.  
  332. int cover = 0;
  333. uint attackDir = GetDirection( hx, hy, tx, ty );
  334. uint16 coverX = tx, coverY = ty;
  335. uint coverstep = 1;
  336. if( dist > 1 && !isThrowRelayted && tr.Valid)
  337. {
  338. attack.RealMap.MoveHexByDir( coverX, coverY, ( ( attackDir + 3 ) % 6 ), coverstep );
  339. if( attack.RealMap.IsHexPassed( coverX, coverY ) ) cover = 0;
  340. if( attack.RealMap.IsHexRaked( coverX, coverY ) && !attack.RealMap.IsHexPassed( coverX, coverY ) )
  341. {
  342. cover = 2;
  343. if( tr.IsSupporter ) cover++;
  344. if( tr.IsNoKnock ) cover++;
  345. }
  346. }
  347. bool isShotgunMechanics = (validWeap && !isPG && ammoPid != PID_12MM_FIRE && Present(weapPid, Pids_WeaponShotguns) );
  348. bool isKnockMechanics = (validWeap&& !isPG && ( ammoPid == PID_14MM_EXPL || weapPid == 875) );
  349.  
  350. // ///////////////////////////////////////////////////////////////////
  351. // -------------------------------------------------------- HERE WE GO
  352. if( aim == HIT_LOCATION_EYES )
  353. if( (tr.Valid && ( at.Dir + 3 ) % 6 != tr.Dir ) || tr.PidSlot1 == PID_PASGT_HELM ) aim = HIT_LOCATION_HEAD;
  354.  
  355. attack.Aim = aim; // !!!STRUCK UPDATE!!!
  356. bool handlau=(validWeap && weapPid == 898);
  357. if( !tr.Valid && valid(map) && (smokePen==0))
  358. {
  359. if(valid( cr ) && hexX != 0 && hexY != 0 && valid( map.GetItem( hexX, hexY, PID_SMOKE ))) smokePen =60;
  360. if(hexX != 0 && hexY != 0 && valid( map.GetItem( hexX, hexY, PID_MUSTARD_GAS ))) smokePen =25;
  361. if( valid( cr ) && valid( map.GetItem( cr.HexX, cr.HexY, PID_SMOKE ))) smokePen +=90;
  362. if( valid( cr ) && valid( map.GetItem( cr.HexX, cr.HexY, PID_MUSTARD_GAS ))) smokePen +=25;
  363. if( isPlaser || weaponPerk == WEAPON_PERK_NIGHT_SIGHT ||(attack.IsGrenade && !handlau)) smokePen = smokePen/2;
  364. if( at.PidSlot1 == PID_TEPLOVISOR) smokePen = 0;
  365. }
  366.  
  367. // РАССЧЕТ TOHIT
  368. acc = attack.Dist;
  369. uint KPers=5; uint KDist=3; acmod =(tr.Valid ?tr.AC:25);
  370. if (attack.WeaponPerk==WEAPON_PERK_LONG_RANGE || attack.WeaponBonusPerk==WEAPON_PERK_LONG_RANGE) KPers=8;
  371. if (attack.WeaponPerk==WEAPON_PERK_SCOPE_RANGE || attack.WeaponBonusPerk==WEAPON_PERK_SCOPE_RANGE) KDist=2;
  372. if (attack.WeaponPerk==WEAPON_PERK_NIGHT_SIGHT || attack.WeaponBonusPerk==WEAPON_PERK_NIGHT_SIGHT) KPers=6;
  373.  
  374. //if (valid(map.GetItem(hexX, hexY, PID_FOXHOLE))) acmod += 20;
  375. toHit = skillVal+(at.Perc+sharpshooter-2)*KPers-acc*KDist-acmod;
  376. if (cr.Perk[PE_MYSTERIOUS_STRANGER]==0) toHit -= attack.IsHthAttack ? ( GetHitAim( aim ) / 2 ) : GetHitAim( aim );
  377. //if( at.IsPlayer && weaponUpgrade == WEAPON_ACCURATE ) toHit += 20;
  378. if (weaponPerk==WEAPON_PERK_ACCURATE) toHit+=20;
  379. if( at.IsOneHand ) toHit += ( ( weapIsTwoHand ) ? -40 : 60 );
  380. if( at.HandlStr < weapStr )
  381. {
  382. if( (at.IsOneHand && weapIsTwoHand) || !at.IsOneHand )
  383. toHit -= ( weapStr - at.HandlStr ) * 20;
  384. }
  385. if( (at.IsOneHand && weapIsTwoHand==false)) attack.ClearDamageBonus += 7;
  386. if (tr.Valid && tr.IsKnockout) toHit += 30;
  387. if (tr.Valid && target.GetMultihex() > 0 ) toHit += 10;
  388. toHit -= 5 * at.SuppRate;
  389. if( at.eyeDamage ) toHit -= 50;
  390. if( at.IsNearOfficer ) toHit += 5;
  391. if( at.IsStable ) toHit += 35;
  392. toHit += cr.Stat[ST_HIT_MOD];
  393. if( weapPid == 7960 ) toHit = 95;
  394. if (!attack.IsFlamethrower && !isHthAttack) toHit -= smokePen;
  395. if( cr.PerkBase[ PE_IMP1]>0 && cr.PerkBase[ PE_IMP1]!=4) toHit += 3;
  396. int BaseHit= toHit;
  397. toHit = CLAMP( toHit, 5, 95-(10*cover) );
  398. // toHit -= 10 * cover;
  399. bool trowknife=(validWeap && (weapPid==PID_THROWING_KNIFE || weapPid==PID_THROWING_KNIFE_MK2));
  400. if (cr.Trait[TRAIT_HEAVY_HANDED]!=0 && (weapIsTwoHand || (weaponSubtype!=WS_MELEE &&!trowknife))) toHit = CLAMP( toHit/2, 5, 50 );
  401.  
  402. // ///////////////////////////////////////////////////////////////////
  403. // ------------------------------------------------------------ MARGIN
  404. uint bonusth=GetBonusWp(realWeapon, 7);
  405. margin =toHit-Random(0,100)+bonusth;
  406. if (tr.Valid && target.Trait[TRAIT_HEAVY_HANDED]!=0 && trPidArmor==265) bonusth+=10;
  407. tr.DodgeChance = CLAMP( tr.DodgeChance-(sharpshooter>0?5:0), 0, 50 );
  408. if (cr.StatBase[ST_NEXT_REPLICATION_ENTIRE]!=0) cr.Say (SAY_NETMSG, "tohit-"+ toHit+"% "+" hit-"+margin);
  409. //cr.Say (SAY_NETMSG, "hit-"+ margin);
  410. // ------------------------------------------------------------ DODGES
  411. if (at.IsSniper && at.WasNotInCombat && attack.IsSingleAttack && attack.SkillVal >= 100) margin = 10;
  412. if (cr.Trait[TRAIT_KAMIKAZE]!=0 && at.WasNotInCombat) cr.StatBase[ST_CURRENT_AP]+=200;
  413. if( margin >= 0 && !attack.IsBurst && !attack.IsFlamethrower && !attack.IsRocket && !attack.IsGrenade && tr.Valid)
  414. {
  415. // cr.Say( SAY_NETMSG, "Dodge-"+tr.DodgeChance);
  416. if((Random(0,99)<tr.DodgeChance))
  417. {
  418. margin = -100;
  419. target.SayMsg( SAY_NETMSG, TEXTMSG_COMBAT, STR_COMBAT_YOU_DODGED);
  420. if (tr.IsFortuneFinder) target.StatBase[ ST_CURRENT_AP ] += 100;
  421. }
  422. if (Random(0,100)<tr.BaseLuck && target.Perk[PE_HEAVE_HO]>0 && target.CountItem(PID_BOTTLE_CAPS)>0)
  423. {
  424. margin = -10;
  425. _CritDeleteItem( target, PID_BOTTLE_CAPS, 1 );
  426. }
  427. }
  428. else if( margin >= 0 && tr.Valid)
  429. {
  430. if((Random(0,99)<tr.DodgeChance))
  431. {
  432. attack.PercentDamage = attack.PercentDamage/2;
  433. margin = 177;
  434. target.SayMsg( SAY_NETMSG, TEXTMSG_COMBAT, STR_COMBAT_PARTIALLY_DODGED );
  435. if (tr.IsFortuneFinder) target.StatBase[ ST_CURRENT_AP ] += 100;
  436. }
  437.  
  438. }
  439. /* if(attack.IsAlwaysAlt)
  440. {
  441. if(Random(0,100) <= toHit)
  442. margin = 10;
  443. else
  444. margin = -10;
  445. } */
  446. // ------------------------------------------------------------ MISSES
  447. if( margin < 0)
  448. {
  449. if( tr.Valid && tr.IsAnimFriend && margin!=-100)
  450. {
  451. if (Random(0,100)<(tr.BaseAgil + tr.BaseChar+tr.BasePerc)) target.StatBase[ST_CURRENT_AP]=toHit*10;
  452. else target.StatBase[ ST_CURRENT_AP ] += 100;
  453. }
  454. if (cr.Perk[PE_CAUTIOUS_NATURE] != 0) cr.StatBase[ ST_CURRENT_AP ] += 100;
  455.  
  456. // ------------------------------------------------------------ CRITICAL FAILURE
  457. if( !at.IsGod )
  458. {
  459. isCritical = _ChanceR( RANDOM(Random(1, 100), 20, 5), 2);
  460. if( !isCritical ) isCritical = ( _Chance( 2, 1 ) && ( at.IsJinxed || ( _Chance( 2, 1 ) && tr.IsJinxed ) ) );
  461. if( at.IsStable || cr.Trait[TRAIT_HEAVY_HANDED]!=0) isCritical = false;
  462.  
  463. if( isCritical )
  464. {
  465. int luckmod = at.Luck;
  466. if( at.PidSlot2 == PID_TALISMAN ) luckmod += 3;
  467. if( luckmod > 10 )
  468. luckmod = 10;
  469. int roll = Random( 1, 100 ) - 5 * ( luckmod - 5 );
  470. if( roll <= 20 )
  471. roll = 0;
  472. else if( roll <= 50 )
  473. roll = 1;
  474. else if( roll <= 75 )
  475. roll = 2;
  476. else if( roll <= 95 )
  477. roll = 3;
  478. else
  479. roll = 4;
  480.  
  481.  
  482. critfailFlags = CriticalFailureTable[ 5 * weapon.Weapon_CriticalFailture + roll ];
  483. if( critfailFlags == 0 )
  484. isCritical = false;
  485. hitRandomly = FLAG( critfailFlags, MF_HIT_RANDOMLY );
  486. }
  487. }
  488. }
  489. // ------------------------------------------------------------ HIT APPLIES
  490. else
  491. {
  492. isHit = true;
  493. if(at.IsAdrenaline && tr.Valid) cr.StatBase[ST_CURRENT_HP] += (cr.StatBase[ ST_PERCEPTION ]*2);
  494. if( attack.IsHthAttack && at.IsSlayer ) at.CriticalCh = 100; // !!!STRUCK UPDATE!!!
  495. if( at.IsStable || cr.Trait[TRAIT_HEAVY_HANDED]!=0) at.CriticalCh = 0; // !!!STRUCK UPDATE!!!
  496. at.CriticalCh = CLAMP( at.CriticalCh, 0, 100 );
  497. uint critrnd=Random(0,99); bool Critup=(critrnd<at.CriticalCh?true:false);
  498. if (tr.Valid) cr.StatBase[ST_EMP_RESIST_EXT]=(Critup?target.Id:0); uint anticrrnd=Random(0,100);
  499. if (tr.Valid && Critup && (tr.CritResist<anticrrnd) ) isCritical = true;
  500. if(tr.Valid && tr.IsKnockout && Random(0, 3) > 0) isCritical = false; // Персонаж в отключке. Безвылазное критование в минуса отменяется.
  501. if(!attack.IsSingleAttack && !attack.IsBurst && !attack.IsHthAttack && !attack.IsFlamethrower) isCritical=false;
  502. if(!tr.Valid) isCritical = false;
  503. if (tr.IsFortuneFinder && target.Stat[ST_CURRENT_HP]<target.Stat[ST_MAX_LIFE]/3) isCritical = false;
  504. // ///////////////////////////////////////////////////////////////////
  505. // --------------------------------------------- DAMAGE MANIPULATIONS
  506.  
  507. // if( !at.IsKind && validWeap && at.MasterPerk > 0 ) attack.PercentDamage += at.MasterPerk;
  508.  
  509.  
  510. // ///////////////////////////////////////////////////////////////////
  511. // ------------------------------------------------- ALL MANIPULATIONS
  512.  
  513. if( tr.Valid )
  514. {
  515. attack.TargetId = target.Id;
  516. @attack.Target = target; // !!!STRUCK UPDATE!!!
  517. }
  518.  
  519. // ///////////////////////////////////////////////////////////////////
  520. // ---------------------------------------------------------- SPECIALS
  521. if( weapPid == 7960 )
  522. attack.RealMap.PlaySound( "MORTIER_SHOT.ogg", cr.HexX, cr.HexY, 0 );
  523.  
  524. if( attack.WeaponPerk == WEAPON_PERK_FLAMEBOY || attack.WeaponBonusPerk == WEAPON_PERK_FLAMEBOY || ammoPid==PID_NAPALM)
  525. {
  526. if((attack.RealMap.IsHexRaked( attack.Tx, attack.Ty )) || ( tr.Valid && _ChanceR(RANDOM(Random(1, 100), 20, 5), int(10 + ( (at.IsPyro) ? 20 : 0)) ) ) )
  527. {
  528. SmokeBlast( attack.RealMap, hexX, hexY, PID_FIRE, ( valid( attack.Attacker ) ? attack.Attacker.Id : 0 ), 1 + ( ( at.IsPyro || ammoPid == PID_NAPALM) ? 1 : 0 ) );
  529. }
  530. }
  531.  
  532. }
  533. if( hitRandomly && tr.Valid )
  534. {
  535. Critter@ randomTarget = ChooseRandomTarget( attack.RealMap, cr, target, wpnMaxDist );
  536. if( @randomTarget != null )
  537. {
  538. tx = randomTarget.HexX;
  539. ty = randomTarget.HexY;
  540. attack.Dist = GetDistantion( hx, hy, tx, ty ); // !!!STRUCK UPDATE!!!
  541. attack.Aim = HIT_LOCATION_UNCALLED; // !!!STRUCK UPDATE!!!
  542. // ------------------------------------------- Перезаписываем класс TR
  543. tr = _GetTrStruct(randomTarget); // !!!STRUCK UPDATE!!!
  544.  
  545. if(tr.IsHidden) randomTarget.ModeBase[ MODE_HIDE ] = 0;
  546. NotifyOops( cr, target, attack.Target, results );
  547. }
  548. }
  549. else
  550. {
  551. @realTarget = target;
  552. }
  553.  
  554. @attack.Target = realTarget; // !!!STRUCK UPDATE!!!
  555. tr.Valid = valid(attack.Target); // !!!STRUCK UPDATE!!!
  556. if(tr.Valid)
  557. attack.TargetId = realTarget.Id; // !!!STRUCK UPDATE!!!
  558.  
  559. // ------------------------------------------ // -- BELOW THIS POINT --
  560. // ------------------------------------------ // target validation = if(tr.Valid)
  561. // ------------------------------------------ // target handle = attack.Target
  562. // ------------------------------------------ // target checks from class tr.[CHECK]
  563.  
  564. // ///////////////////////////////////////////////////////////////////
  565. // ------------------------------------------ IMPORTANT - CR ANIMATION
  566. cr.Action( ACTION_USE_WEAPON, slot << 16 | ( ( ( !isHit && isCritical && !hitRandomly ) ? 1 : 0 ) << 8 ) | ( aim << 4 ) | use, realWeapon );
  567. bool changedTarget = ( hitRandomly && tr.Valid );
  568.  
  569. // ------------------------------ IMPORTANT - RETURN : CriticalFailure
  570. if( !isHit && isCritical && ( !hitRandomly || !tr.Valid ) )
  571. {
  572. CriticalFailure( cr, weapon, use, ammo, critfailFlags, results, slot );
  573. return;
  574. }
  575.  
  576. // ///////////////////////////////////////////////////////////////////
  577. // ///////////////// -------- TARGET WAS HIT -------- ////////////////
  578. // ///////////////////////////////////////////////////////////////////
  579. // ----------------------------------------------- UNARMED AND MELEE ATTACK
  580. uint ClearDB = 0;
  581. uint ClearDR = 0;
  582. uint PercentD = 0;
  583. uint PercentR = 0;
  584. bool criticalHit = isHit && isCritical;
  585. if( attack.IsHthAttack )
  586. {
  587. if( isHit )
  588. {
  589. if( weapPid == 115 && tr.Valid && !tr.IsNoKnock && Random(0,1)==1)
  590. _KnockBack_Light( weapPid, Random(4, 8), map, cr, attack.Target );
  591. ApplyDamage( attack, at, tr, attack.Target, 1, criticalHit, true, results );
  592. }
  593. else
  594. {
  595. if( changedTarget )
  596. ApplyDamage( attack, at, tr, attack.Target, 1, false, false, results );
  597. else
  598. NotifyMiss( cr, results );
  599. }
  600. }
  601.  
  602. // ---------------------------------------------- WS_GUN SINGLE ATTACK
  603. else if( ( weaponSubtype == WS_GUN ) && !attack.IsBurst && !attack.IsRocket && !attack.IsFlamethrower )
  604. {
  605. if( isHit || changedTarget )
  606. {
  607. // ----------------------------------------------- WS_GUN SINGLE ATTACK AMMO MANIPULATIONS ONHIT ONLY
  608. // ----------------------------- Гаусс - Особенность - прошивает все цели на линии атаки. С каждой прошитой целью мин и макс атака
  609. // ----------------------------- уменьшается на 5-9. Если мин или макс. атака опускается ниже 10 - нанесение урона прерывается.
  610. if( ammoPid == PID_2MM_EC_AMMO || ammoPid == PID_2MM_MAG )
  611. {
  612. aimOld = attack.Aim;
  613.  
  614. Critter@[] critsLine;
  615. attack.RealMap.GetCrittersPath( hx, hy, tx, ty, 0.0f, wpnMaxDist, FIND_LIFE_AND_KO, critsLine );
  616. if(critsLine.length() == 0) return;
  617. AdvEffect( weapon, cr, attack.Target );
  618. for( int i = 0, j = critsLine.length(); ( i < j ); i++ )
  619. {
  620. if(attack.DmgMin < 10 || attack.DmgMax < 10)
  621. continue;
  622. if( critsLine[ i ].Id == realTarget.Id )
  623. continue;
  624. else
  625. {
  626. attack.Aim = HIT_LOCATION_UNCALLED;
  627. @attack.Target = critsLine[ i ];
  628. attack.TargetId = critsLine[ i ].Id;
  629.  
  630. ClearDB = attack.ClearDamageBonus;
  631. ClearDR = attack.ClearDamageReduction;
  632. PercentD = attack.PercentDamage;
  633. PercentR = attack.PercentReduction;
  634.  
  635. ApplyDamage( attack, at, tr, attack.Target, 1, false, false, results );
  636.  
  637. attack.ClearDamageBonus = ClearDB;
  638. attack.ClearDamageReduction = ClearDR;
  639. attack.PercentDamage = PercentD;
  640. attack.PercentReduction = PercentR;
  641.  
  642. attack.DmgMin -= Random(5,9); // !!!STRUCK UPDATE!!!
  643. attack.DmgMax -= Random(5,9); // !!!STRUCK UPDATE!!!
  644.  
  645. @attack.Target = realTarget;
  646. attack.TargetId = realTarget.Id;
  647. attack.Aim = aimOld;
  648. }
  649.  
  650. }
  651. }
  652. // ----------------------------- Остальные типы спец.патронов.
  653. if( isKnockMechanics && tr.Valid && !tr.IsNoKnock)
  654. _KnockBack_Light( weapPid, Random(6, 10), map, cr, attack.Target );
  655. if( isShotgunMechanics && tr.Valid && !tr.IsNoKnock)
  656. _KnockBack_Shotgun( weapPid, ammoPid, attack.Dist, wpnMaxDist, weapStr, at.HandlStr, map, cr, attack.Target );
  657. if( ( ammoPid == PID_12MM_FIRE || ammoPid == PID_10MM_APFIRE || attack.WeaponPerk == WEAPON_PERK_FLAMEBOY || attack.WeaponBonusPerk == WEAPON_PERK_FLAMEBOY) && tr.Valid )
  658. AffectFire( attack.Target, (15+attack.Attacker.PerkBase[PE_PYROMANIAC]*20+attack.Attacker.Perk[PE_DA_FIRE]*2), attack.Attacker.Id );
  659. if( ammoPid == PID_HK_NEEDLER_POISON )
  660. AffectPos( attack.Target, 16, ( valid( attack.Attacker ) ? attack.Attacker.Id : 0 ) );
  661.  
  662.  
  663. // ----------------------------- SUPPRESSION
  664. if( ( at.IsSupporter ) && toHit > 75)
  665. {
  666. supressRounds = 1;
  667. if( attack.Dist >= 3 && attack.Dist <= 15)
  668. {
  669. if( ammoPid == PID_SHOTGUN_SHELLS || ammoPid == PID_12MM_FIRE || ammoPid == PID_12MM_BULL )
  670. {
  671. if( ammoPid == PID_SHOTGUN_SHELLS )
  672. supressRounds = Random( 13, 20 );
  673. if( ammoPid == PID_12MM_FIRE )
  674. supressRounds = Random( 20, 34 );
  675. if( ammoPid == PID_12MM_BULL )
  676. supressRounds = Random( 24, 28 );
  677. SuppressCritter( attack.Target, cr, supressRounds );
  678. }
  679. }
  680. else if( ( attack.WeaponPerk == WEAPON_PERK_LONG_RANGE || attack.WeaponBonusPerk == WEAPON_PERK_LONG_RANGE) && skillNum == SK_SMALL_GUNS && attack.Dist >= 25 )
  681. {
  682. supressRounds = Random( attack.Dist/2 - 5, attack.Dist/2 + 10 );
  683. SuppressCritter( attack.Target, cr, supressRounds );
  684. }
  685. }
  686. /* if(attack.Aim != HIT_LOCATION_UNCALLED && attack.Aim != HIT_LOCATION_TORSO && _ChanceR(RANDOM(Random(1, 100), 20, 5), at.Luck*3) && attack.IsSingleAttack && (attack.WeaponPerk == WEAPON_PERK_SCOPE_RANGE || attack.WeaponBonusPerk == WEAPON_PERK_SCOPE_RANGE) )
  687. {
  688. SETFLAG(attack.ForceFlags, HF_BYPASS_ARMOR);
  689. } */
  690. ApplyDamage( attack, at, tr, attack.Target, 1, criticalHit, !changedTarget, results );
  691. AdvEffect( weapon, cr, attack.Target );
  692. }
  693. // ----------------------------------------------- WS_GUN SINGLE ATTACK AMMO MANIPULATIONS MISS ONLY
  694. else
  695. {
  696. Critter@[] critsLine;
  697. attack.Aim = HIT_LOCATION_UNCALLED;
  698. attack.RealMap.GetCrittersPath( hx, hy, tx, ty, 0.0f, wpnMaxDist, FIND_LIFE_AND_KO, critsLine );
  699. if(critsLine.length() == 0) return;
  700. int bl = 0;
  701. bool anyHit = false;
  702. for( int i = 0, j = critsLine.length(); ( i < j ) && !anyHit; i++ )
  703. {
  704. if( critsLine[ i ].Id == realTarget.Id )
  705. {
  706. bl++;
  707. continue;
  708. } // skip the primary target
  709.  
  710. // adjust tohit
  711. dist = GetDistantion( hx, hy, critsLine[ i ].HexX, critsLine[ i ].HexY );
  712. toHit = getFirstToHit(baseToHit, bl, dist, acc, distmod2, accloss, at.Perc, sharpshooter, at.eyeDamage, at.IsBloody, at.IsStable, critsLine[ i ], bulletPower, acmod);
  713. if( !critsLine[ i ].IsKnockout() )
  714. bl++;
  715. toHit /= 3;
  716. if( Random( 1, 100 ) <= toHit )
  717. {
  718. NotifyOops( cr, target, critsLine[ i ], results );
  719. @normalTarget = critsLine[ i ];
  720. useNormal = true;
  721. ApplyDamage( attack, at, tr, normalTarget, 1, criticalHit, !changedTarget, results );
  722. anyHit = true;
  723. }
  724. }
  725. if( !anyHit )
  726. NotifyMiss( cr, results );
  727. }
  728. }
  729. // ///////////////////////////////////////////////////////////////////
  730. // ---------------------------------------------------- FLAMERS ATTACK
  731. // ///////////////////////////////////////////////////////////////////
  732. else if( isFlamethrower )
  733. {
  734. if( changedTarget )
  735. {
  736. dist = GetDistantion( hx, hy, tx, ty );
  737. acc = dist;
  738. blockers = attack.RealMap.GetCrittersPath( hx, hy, tx, ty, 0.0f, dist, FIND_LIFE, null );
  739. if( !attack.Target.IsKnockout() )
  740. blockers--;
  741. toHit = getFirstToHit(baseToHit, blockers, dist, acc, distmod2, accloss, at.Perc, sharpshooter, at.eyeDamage, at.IsBloody, at.IsStable, attack.Target, bulletPower, acmod);
  742. }
  743.  
  744. // critical hit bonus, toHit can be increased over 100
  745. if( criticalHit ) toHit += 20;
  746.  
  747. // proceed with the flame attack
  748. Critter@[] critsHit( 0 );
  749. uint[] critsHitBullets( 0 );
  750. int len_ = 0;
  751.  
  752. if( _ChanceR(RANDOM(Random(1, 100), 20, 5), toHit) && tr.Valid)
  753. {
  754. critsHit.resize( 1 );
  755. @critsHit[ 0 ] = attack.Target;
  756. critsHitBullets.resize( 1 );
  757. critsHitBullets[ 0 ] += 1;
  758. len_++;
  759. }
  760.  
  761. Critter@[] lineCentral;
  762. attack.RealMap.GetCrittersPath( hx, hy, tx, ty, 0.0f, wpnMaxDist, FIND_LIFE_AND_KO, lineCentral );
  763.  
  764. int bl;
  765. if(lineCentral.length() > 0)
  766. {
  767. for( int lineCount = 0, lineMax = 1; lineCount < lineMax; lineCount++ )
  768. {
  769.  
  770. // lineCentral
  771. bl = 0; // zero blockers
  772. for( int i = 0, j = lineCentral.length(); i < j; i++ )
  773. {
  774. // adjust tohit
  775. if(!valid(lineCentral[ i ])) continue;
  776. dist = GetDistantion( attack.Hx, attack.Hy, lineCentral[ i ].HexX, lineCentral[ i ].HexY );
  777. toHit = getFirstToHit(baseToHit, bl, dist, acc, distmod2, accloss, at.Perc, sharpshooter, at.eyeDamage, at.IsBloody, at.IsStable, lineCentral[ i ], bulletPower, acmod);
  778. if( !lineCentral[ i ].IsKnockout() )
  779. bl++;
  780. if( _ChanceR(RANDOM(Random(1, 100), 20, 5), toHit) )
  781. {
  782. int crIndex = FindCritterInArray( critsHit, lineCentral[ i ] );
  783. if( crIndex == -1 )
  784. {
  785. critsHit.resize( len_ + 1 );
  786. @critsHit[ len_ ] = lineCentral[ i ];
  787. critsHitBullets.resize( len_ + 1 );
  788. crIndex = len_;
  789. len_++;
  790. }
  791. critsHitBullets[ crIndex ] += 1;
  792. }
  793. }
  794. // lineCentral end
  795. }
  796. }
  797.  
  798. uint8 leftDir = GetOffsetDir( hx, hy, tx, ty, 89.0f );
  799. uint16 sx = hx;
  800. uint16 sy = hy;
  801. uint16 ex = tx;
  802. uint16 ey = ty;
  803.  
  804. map.MoveHexByDir( sx, sy, leftDir, 1 );
  805. map.MoveHexByDir( ex, ey, leftDir, 1 );
  806.  
  807. Critter@[] lineLeft;
  808. attack.RealMap.GetCrittersPath( sx, sy, ex, ey, 0.0f, wpnMaxDist - 1, FIND_LIFE_AND_KO, lineLeft );
  809. int leftStart = 0;
  810. int leftLen = lineLeft.length();
  811. while( ( leftStart < leftLen ) && ( GetDistantion( hx, hy, lineLeft[ leftStart ].HexX, lineLeft[ leftStart ].HexY ) ) < 3 )
  812. leftStart++;
  813. if(lineLeft.length() > 0)
  814. {
  815. for( int i = leftStart, j = leftLen; i < j; i++ )
  816. {
  817. // adjust tohit
  818. if(!valid(lineLeft[ i ])) continue;
  819. dist = GetDistantion( hx, hy, lineLeft[ i ].HexX, lineLeft[ i ].HexY );
  820. bl = attack.RealMap.GetCrittersPath( hx, hy, lineLeft[ i ].HexX, lineLeft[ i ].HexY, 0.0f, dist, FIND_LIFE, null ) - 1;
  821. toHit = getFirstToHit(baseToHit, bl, dist, acc, distmod2, accloss, at.Perc, sharpshooter, at.eyeDamage, at.IsBloody, at.IsStable, lineLeft[ i ], bulletPower, acmod);
  822. if( _ChanceR( RANDOM(Random(1, 100), 20, 5), toHit ) )
  823. {
  824. int crIndex = FindCritterInArray( critsHit, lineLeft[ i ] );
  825. if( crIndex == -1 )
  826. {
  827. critsHit.resize( len_ + 1 );
  828. @critsHit[ len_ ] = lineLeft[ i ];
  829. critsHitBullets.resize( len_ + 1 );
  830. crIndex = len_;
  831. len_++;
  832. }
  833. critsHitBullets[ crIndex ] += 1;
  834. }
  835. } // left line
  836. }
  837.  
  838. uint8 rightDir = GetOffsetDir( hx, hy, tx, ty, -89.0f );
  839. sx = hx;
  840. sy = hy;
  841. ex = tx;
  842. ey = ty;
  843.  
  844. map.MoveHexByDir( sx, sy, rightDir, 1 );
  845. map.MoveHexByDir( ex, ey, rightDir, 1 );
  846.  
  847. Critter@[] lineRight;
  848. attack.RealMap.GetCrittersPath( sx, sy, ex, ey, 0.0f, wpnMaxDist - 1, FIND_LIFE_AND_KO, lineRight );
  849. int rightStart = 0;
  850. int rightLen = lineRight.length();
  851. while( ( rightStart < rightLen ) && ( GetDistantion( hx, hy, lineRight[ rightStart ].HexX, lineRight[ rightStart ].HexY ) ) < 3 )
  852. rightStart++;
  853. if(lineRight.length() > 0)
  854. {
  855. for( int i = rightStart, j = rightLen; i < j; i++ )
  856. {
  857. // adjust tohit
  858. if(!valid(lineRight[ i ])) continue;
  859. dist = GetDistantion( hx, hy, lineRight[ i ].HexX, lineRight[ i ].HexY );
  860. bl = attack.RealMap.GetCrittersPath( hx, hy, lineRight[ i ].HexX, lineRight[ i ].HexY, 0.0f, dist, FIND_LIFE, null ) - 1;
  861. toHit = getFirstToHit(baseToHit, bl, dist, acc, distmod2, accloss, at.Perc, sharpshooter, at.eyeDamage, at.IsBloody, at.IsStable, lineRight[ i ], bulletPower, acmod);
  862. if( _ChanceR( RANDOM(Random(1, 100), 20, 5), toHit ) )
  863. {
  864. int crIndex = FindCritterInArray( critsHit, lineRight[ i ] );
  865. if( crIndex == -1 )
  866. {
  867. critsHit.resize( len_ + 1 );
  868. @critsHit[ len_ ] = lineRight[ i ];
  869. critsHitBullets.resize( len_ + 1 );
  870. crIndex = len_;
  871. len_++;
  872. }
  873. critsHitBullets[ crIndex ] += 1;
  874. }
  875. } // right line
  876. }
  877. // ///////////////////////////////////////////////////////////////////
  878. // ------------------------------------------------- FLAME DEAL DAMAGE
  879. // ///////////////////////////////////////////////////////////////////
  880. for( int i = 0, j = len_; i < j; i++ )
  881. {
  882. if(!valid(critsHit[ i ])) continue;
  883. @attack.Target = critsHit[ i ];
  884. attack.TargetId = critsHit[ i ].Id;
  885. // if (critsHit[i].Id==target.Id && cover>0) attack.PercentDamage= attack.PercentDamage/2;
  886. ClearDB = attack.ClearDamageBonus;
  887. ClearDR = attack.ClearDamageReduction;
  888. PercentD = attack.PercentDamage;
  889. PercentR = attack.PercentReduction;
  890. if(attack.IsAlwaysAlt)
  891. ApplyDamage( attack, critsHit[ i ], 1, false, ( cr.IsPlayer() && !changedTarget ) || ( !at.IsPlayer && critsHit[ i ].Id == attack.TargetId ), results );
  892. else
  893. ApplyDamage( attack, at, tr, critsHit[ i ], 1, ( realTarget.Id == critsHit[ i ].Id ) && criticalHit, ( at.IsPlayer && !changedTarget ) || ( cr.IsNpc() && critsHit[ i ].Id == target.Id ), results );
  894. attack.ClearDamageBonus = ClearDB;
  895. attack.ClearDamageReduction = ClearDR;
  896. attack.PercentDamage = PercentD;
  897. attack.PercentReduction = PercentR;
  898. if( (ammoPid == PID_NAPALM || attack.WeaponPerk == WEAPON_PERK_FLAMEBOY || attack.WeaponBonusPerk == WEAPON_PERK_FLAMEBOY ) )
  899. AffectFire( critsHit[ i ], (15+attack.Attacker.PerkBase[PE_PYROMANIAC]*20+attack.Attacker.Perk[PE_DA_FIRE]*2), attack.Attacker.Id );
  900. }
  901.  
  902. if( !changedTarget && !attack.TargetHit )
  903. NotifyMiss( cr, results );
  904. }
  905. // ///////////////////////////////////////////////////////////////////
  906. // ------------------------------------------------- BURST FIRE ATTACK
  907. // ///////////////////////////////////////////////////////////////////
  908. else if((weaponSubtype==WS_GUN) && isBurst) //
  909. {
  910. int skilMod=skillVal-acmod; skilMod=(skilMod>5?skilMod:5);
  911. BaseHit = CLAMP( BaseHit, 5, skilMod);
  912. if (ammoCount<=ammoRound) ammoRound=ammoCount;
  913. int kburst=0; uint burst_accuracy =0;
  914. for( int i = 0; i < ammoRound; i++ )
  915. {
  916. kburst=BaseHit-4*attack.Dist*i;
  917. if (kburst>0) burst_accuracy += (Random(0,attack.Dist*i)<kburst?1:0);
  918. }
  919. uint const_rounds=ammoRound-burst_accuracy;
  920.  
  921. uint[] critsHitBullets;
  922. Critter@[] critsHit;
  923. attack.RealMap.GetCrittersPath( hx, hy, tx, ty, 0.0f, wpnMaxDist, FIND_LIFE_AND_KO, critsHit );
  924. critsHitBullets.resize(critsHit.length());
  925.  
  926. uint dists=0; uint mas=0; int hitto=0; uint tar=0;
  927. for( int i = 0; i < critsHit.length(); i++ )
  928. {
  929. if (target.Id==critsHit[i].Id)
  930. {critsHitBullets[i] = burst_accuracy; tar=i;}
  931. else critsHitBullets[i] = 0;
  932. dists = GetDistantion( hx, hy, critsHit[i].HexX, critsHit[i].HexY );
  933. if (attack.Dist>dists && burst_accuracy>0 && target.Id!=critsHit[i].Id)
  934. {
  935. mas=burst_accuracy/2;
  936. critsHitBullets[i] += mas;
  937. burst_accuracy-=mas;
  938. if (tar!=0) critsHitBullets[tar] = burst_accuracy;
  939. }
  940. //cr.Say( SAY_NETMSG, "Bur-"+critsHit[i].Id+"-"+critsHitBullets[i]);
  941. }
  942.  
  943. if (critsHit.length()>1)
  944. {
  945. for( int i = 0; i < const_rounds; i++ )
  946. {
  947. mas=Random(0,critsHit.length()-1);
  948. dists = GetDistantion( hx, hy, critsHit[mas].HexX, critsHit[mas].HexY );
  949. hitto=BaseHit-(attack.Dist)*5-Random(0,100);
  950. if (target.Id!=critsHit[mas].Id && hitto>0) critsHitBullets[mas] += 1;
  951. }
  952. }
  953. //cr.Say( SAY_NETMSG, "Bur-"+minBurstD+"-"+maxBurstD+" lok-"+i_lock+" bur-"+burst_accuracy);
  954. uint hitround=0;
  955. for( int i = 0, j = critsHit.length(); i < j; i++ )
  956. {
  957. if(!valid(critsHit[i])) continue;
  958. hitround+=critsHitBullets[i];
  959. if(critsHitBullets[i]==0) continue;
  960. ClearDB = attack.ClearDamageBonus;
  961. ClearDR = attack.ClearDamageReduction;
  962. PercentD = attack.PercentDamage;
  963. PercentR = attack.PercentReduction;
  964. ApplyDamage( attack, at, tr, critsHit[i], critsHitBullets[i], ( target.Id == critsHit[i].Id ) && criticalHit, (critsHit[i].Id==target.Id), results);
  965. AdvEffect( weapon, cr, critsHit[ i ] );
  966. attack.ClearDamageBonus = ClearDB;
  967. attack.ClearDamageReduction = ClearDR;
  968. attack.PercentDamage = PercentD;
  969. attack.PercentReduction = PercentR;
  970. if( validAmmo && weaponSubtype == WS_GUN )
  971. {
  972. uint Ston=(critsHit[i].PerkBase[PE_STONEWALL]==1?Random(0,2):0);
  973. //if (critsHit[ i ].PerkBase[ PE_STONEWALL ]==1) Ston=Random(0,2);
  974. bool noKnock = (critsHit[i].Mode[MODE_NO_KNOCK]!=0 || Ston==1 || !critsHit[i].IsCanWalk());
  975. if( isShotgunMechanics && !noKnock )
  976. _KnockBack_Shotgun( weapPid, ammoPid, dist, wpnMaxDist, weapStr, at.HandlStr, map, cr, critsHit[ i ] );
  977. if( isKnockMechanics && !noKnock )
  978. _KnockBack_Light( weapPid, Random(3, 7), map, cr, critsHit[ i ] );
  979. if( ammoPid == PID_12MM_FIRE || ammoPid == PID_10MM_APFIRE || attack.WeaponPerk == WEAPON_PERK_FLAMEBOY || attack.WeaponBonusPerk == WEAPON_PERK_FLAMEBOY )
  980. AffectFire( critsHit[ i ], (15+attack.Attacker.PerkBase[PE_PYROMANIAC]*20+attack.Attacker.Perk[PE_DA_FIRE]*2), attack.Attacker.Id );
  981. if( ammo.ProtoId == PID_HK_NEEDLER_POISON )
  982. AffectPos( critsHit[ i ], 16, ( valid( attack.Attacker ) ? attack.Attacker.Id : 0 ) );
  983. }
  984. if( ( at.IsSupporter ) && toHit > 75)
  985. {
  986. dist = GetDistantion( hx, hy, critsHit[ i ].HexX, critsHit[ i ].HexY );
  987. supressRounds = critsHitBullets[ i ];
  988. if( skillNum == SK_SMALL_GUNS && dist >= 3 && dist < 15)
  989. {
  990. if( ammoPid != PID_SHOTGUN_SHELLS && ammoPid != PID_12MM_FIRE && ammoPid != PID_12MM_BULL ) SuppressCritter( critsHit[ i ], cr, supressRounds );
  991. if( ammoPid == PID_SHOTGUN_SHELLS ) supressRounds = Random( 13, 20 );
  992. if( ammoPid == PID_12MM_FIRE ) supressRounds = Random( 20, 34 );
  993. if( ammoPid == PID_12MM_BULL ) supressRounds = Random( 24, 28 );
  994. SuppressCritter( critsHit[ i ], cr, supressRounds * 2 / 3);
  995. }
  996. if( skillNum == SK_SMALL_GUNS && dist >= 15)
  997. {
  998. if( ammoPid != PID_SHOTGUN_SHELLS && ammoPid != PID_12MM_FIRE && ammoPid != PID_12MM_BULL )
  999. SuppressCritter( critsHit[ i ], cr, supressRounds*2 );
  1000. }
  1001. if(skillNum == SK_BIG_GUNS && dist >= 3 && dist < 15) SuppressCritter( critsHit[ i ], cr, supressRounds*2 );
  1002. if(skillNum == SK_BIG_GUNS && dist >= 15) SuppressCritter( critsHit[ i ], cr, supressRounds*3 );
  1003. }
  1004. }
  1005. if( (!changedTarget && !attack.TargetHit) || hitround==0) NotifyMiss( cr, results );
  1006. }
  1007. // ///////////////////////////////////////////////////////////////////
  1008. // ------------------------------------------------- ROCKETS, THROWING
  1009. // ///////////////////////////////////////////////////////////////////
  1010. else if( attack.IsRocket || ( weaponSubtype == WS_THROWING && weapPid != PID_FLARE && weapPid != PID_ACTIVE_FLARE) )
  1011. {
  1012. bool exploding = (attack.IsRocket || attack.IsGrenade);
  1013. if(margin == 700)
  1014. isHit = _ChanceR(RANDOM(Random(1, 100), 20, 5), toHit);
  1015. if( isHit || changedTarget )
  1016. {
  1017. if( exploding )
  1018. {
  1019. CommenceExplosion( attack, map, tx, ty, attack.Target, weapPid, criticalHit, (tr.Valid) ? attack.Target.Id : 0, isRocket, results );
  1020. }
  1021. else
  1022. {
  1023. /*if( at.IsHidden && cr.Skill[SK_SNEAK] >= 100)
  1024. attack.DmgMul *= 2;*/
  1025. /* if( at.IsSilentDeath && ( at.Dir == tr.Dir || ( ( at.Dir + 1 ) % 6 ) == tr.Dir || ( ( at.Dir + 5 ) % 6 ) == tr.Dir ) )
  1026. criticalHit = true; */
  1027. if( attack.IsAlwaysAlt )
  1028. ApplyDamage( attack, attack.Target, 1, criticalHit, !changedTarget, results );
  1029. else
  1030. ApplyDamage( attack, at, tr, attack.Target, 1, criticalHit, !changedTarget, results );
  1031. }
  1032. }
  1033. else
  1034. {
  1035. // ///////////////////////////////////////////////////////////////////
  1036. // ---------------------------------------- ROCKETS, THROWING - MISSES
  1037. // ///////////////////////////////////////////////////////////////////
  1038. attack.Aim = HIT_LOCATION_UNCALLED;
  1039.  
  1040. if( weaponSubtype == WS_THROWING )
  1041. sharpshooter = 0;
  1042. uint16 bx = 0;
  1043. uint16 by = 0;
  1044. uint16 pbx = 0;
  1045. uint16 pby = 0;
  1046.  
  1047. Critter@[] critsLine;
  1048. attack.RealMap.GetCrittersPath( hx, hy, tx, ty, 0.0f, wpnMaxDist, FIND_LIFE_AND_KO, critsLine, pbx, pby, bx, by );
  1049.  
  1050. int bl = 0;
  1051. bool anyHit = false;
  1052. for( int i = 0, j = critsLine.length(); ( i < j ) && !anyHit; i++ )
  1053. {
  1054. // binyan - hex attack added target validation
  1055. if( tr.Valid && critsLine[ i ].Id == attack.Target.Id )
  1056. {
  1057. bl++;
  1058. continue;
  1059. } // skip the primary target
  1060. dist = GetDistantion( hx, hy, critsLine[ i ].HexX, critsLine[ i ].HexY );
  1061. toHit = getFirstToHit(baseToHit, bl, dist, acc, distmod2, accloss, at.Perc, sharpshooter, at.eyeDamage, at.IsBloody, at.IsStable, critsLine[ i ], bulletPower, acmod);
  1062. if( !critsLine[ i ].IsKnockout() )
  1063. bl++;
  1064. toHit /= 3; // after clamp
  1065. if( _ChanceR( RANDOM(Random(1, 100), 20, 5), toHit) )
  1066. {
  1067. tx = critsLine[ i ].HexX;
  1068. ty = critsLine[ i ].HexY;
  1069. @normalTarget = critsLine[ i ];
  1070. anyHit = true;
  1071. }
  1072. }
  1073.  
  1074. if( anyHit )
  1075. {
  1076. NotifyOops( cr, target, normalTarget, results );
  1077. useNormal = true;
  1078.  
  1079. if( exploding )
  1080. CommenceExplosion( attack, map, tx, ty, normalTarget, weapPid, false, valid( realTarget ) ? realTarget.Id : 0, isRocket, results );
  1081. else
  1082. ApplyDamage( attack, at, tr, normalTarget, 1, false, false, results );
  1083. }
  1084. else
  1085. {
  1086. useHex = true;
  1087. NotifyMiss( cr, results );
  1088. if( attack.IsGrenade )
  1089. {
  1090. // binyan - hex attack added target validation
  1091. tx = (tr.Valid) ? attack.Target.HexX : ( hexX + Random( -5, +5 ) );
  1092. ty = (tr.Valid) ? attack.Target.HexY : ( hexY + Random( -5, +5 ) );
  1093. int newdist = GetDistantion( hx, hy, tx, ty ) + 1;
  1094. map.MoveHexByDir( tx, ty, Random( 0, 5 ), Random( newdist / 2, newdist ) );
  1095. newdist = GetDistantion( hx, hy, tx, ty );
  1096. attack.RealMap.GetCrittersPath( hx, hy, tx, ty, 0.0f, newdist, FIND_LIFE, null, tx, ty, bx, by );
  1097. }
  1098. else
  1099. {
  1100. if( attack.IsRocket )
  1101. {
  1102. tx = hexX + Random( -5, +5 );
  1103. ty = hexY + Random( -5, +5 );
  1104. }
  1105. else
  1106. {
  1107. tx = pbx;
  1108. ty = pby;
  1109. }
  1110. }
  1111.  
  1112. if( exploding )
  1113. CommenceExplosion( attack, map, tx, ty, null, weapPid, false, (tr.Valid) ? attack.Target.Id : 0, isRocket, results );
  1114. }
  1115. }
  1116. }
  1117. // ///////////////////////////////////////////////////////////////////
  1118. // ------------------------------------------------------------ FINISH
  1119. // ///////////////////////////////////////////////////////////////////
  1120. else
  1121. {
  1122. if(weapPid != PID_FLARE && weapPid != PID_ACTIVE_FLARE)
  1123. cr.Say( SAY_NETMSG, "Combat error: weapon PID=" + weapPid + " not handled, please send bug report." );
  1124. }
  1125. FlushResults( results );
  1126. if( _WeaponEffect( weapon, use ) != 0 )
  1127. {
  1128. if( useHex || not valid( realTarget ) )
  1129. map.RunFlyEffect( _WeaponEffect( weapon, use ), cr, null, hx, hy, tx, ty ); // yeah, the target can be null (see: grenades, rocket launcher)
  1130. else
  1131. map.RunFlyEffect( _WeaponEffect( weapon, use ), cr, ( useNormal ? normalTarget : attack.Target ), hx, hy, tx, ty );
  1132. }
  1133. if( validWeap )
  1134. {
  1135. if(ammoRound > 0 && cr.Mode[ MODE_UNLIMITED_AMMO ] == 0 && slot != 99)
  1136. {
  1137. if( ammoCount <= ammoRound )
  1138. realWeapon.AmmoCount = 0;
  1139. else
  1140. realWeapon.AmmoCount -= ammoRound;
  1141. realWeapon.Update();
  1142. }
  1143. if( realWeapon.IsDeteriorable() )
  1144. {
  1145. int deter = ( MAX_SKILL_VAL - skillVal )/4 + 15;
  1146. if (FLAG(realWeapon.BrokenFlags, BI_SERVICE_EXT)) deter+=25;
  1147. DeteriorateItem( cr, realWeapon, deter);
  1148. }
  1149. if( wpnIsRemoved && cr.Mode[ MODE_UNLIMITED_AMMO ] == 0 )
  1150. {
  1151. bool placeOnHex = ( skillNum == SK_THROWING && !attack.IsGrenade );
  1152. if( realWeapon.IsStackable() )
  1153. {
  1154. Item@ tomap;
  1155. // Place on hex
  1156. if( placeOnHex )
  1157. {
  1158. if(weapPid == PID_FLARE)
  1159. @tomap = map.AddItem( tx, ty, PID_ACTIVE_FLARE, 1 );
  1160. else if (Random(0,100)>5)
  1161. @tomap = map.AddItem( tx, ty, weapPid, 1 );
  1162. }
  1163. if(valid(tomap))
  1164. {
  1165. if(tomap.GetProtoId() == PID_ACTIVE_FLARE)
  1166. {
  1167. if(not FLAG(tomap.Flags, ITEM_LIGHT)) SETFLAG(tomap.Flags,ITEM_LIGHT);
  1168. tomap.LightColor= (COLOR_RGB (200,200,25));
  1169. tomap.Update();
  1170. CreateTimeEvent(__FullSecond+REAL_SECOND(Random(120, 180)), "cte_FlareBurn", tomap.Id, false);
  1171. }
  1172. }
  1173. // Sub 1 item
  1174. if( realWeapon.GetCount() > 1 )
  1175. realWeapon.SetCount( realWeapon.GetCount() - 1 );
  1176. else
  1177. DeleteItem( realWeapon );
  1178. }
  1179. else
  1180. {
  1181. MoveItem( realWeapon, 0, map, tx, ty );
  1182. }
  1183. }
  1184. }
  1185. if( !attack.TargetHit && tr.Valid )
  1186. {
  1187. if( !tr.IsPlayer )
  1188. AI_TrySayCombatText( target, COMBAT_TEXT_MISS );
  1189. target.Action( ACTION_DODGE, 0, null ); // Todo: type front/back
  1190. }
  1191. return;
  1192. }
  1193. 14889
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement