From b5cbc32261b363ba18c7bfccf22841035d18b6a6 Mon Sep 17 00:00:00 2001 From: Sevenfm Date: Fri, 16 Oct 2020 19:52:57 +0000 Subject: [PATCH] EstimateStabDamage: updated code. Black AI: - reduce stab attack attractiveness if target is not very close - reduce stab attack attractiveness if soldier sees more than one opponent - for HTH attacks, skills don't have effect on choosing attack type for boxers - reduce HTH attack attractiveness if soldier sees more than one opponent - allow to increase autofire bullets by reducing aim in NCTH if LIMIT_MAX_DEVIATION option is enabled git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8902 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- TacticalAI/Attacks.cpp | 193 +++++++++++++++++++----------------- TacticalAI/DecideAction.cpp | 119 ++++++++++++++-------- 2 files changed, 178 insertions(+), 134 deletions(-) diff --git a/TacticalAI/Attacks.cpp b/TacticalAI/Attacks.cpp index 248b86033..7fc250d29 100644 --- a/TacticalAI/Attacks.cpp +++ b/TacticalAI/Attacks.cpp @@ -33,6 +33,7 @@ #include "Structure Wrap.h" // IsRoofPresentAtGridNo #include "Render Fun.h" #include "worldman.h" +#include "WCheck.h" #endif // anv: for enemy taunts @@ -1710,6 +1711,7 @@ void CalcBestStab(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestStab, BOOLEAN fBladeAt //InitAttackType(pBestStab); // set all structure fields to defaults//dnl ch69 150913 pSoldier->usAttackingWeapon = pSoldier->inv[HANDPOS].usItem; + pSoldier->bWeaponMode = WM_NORMAL; // sevenfm: initialize pBestStab->ubPossible = FALSE; @@ -1872,7 +1874,6 @@ void CalcBestStab(SOLDIERTYPE *pSoldier, ATTACKTYPE *pBestStab, BOOLEAN fBladeAt // NOTE: ignore my cover! By the time I run beside him I won't have any! iThreatValue = CalcManThreatValue(pOpponent,pSoldier->sGridNo,FALSE,pSoldier); - // estimate the damage this stab would do to this opponent iEstDamage = EstimateStabDamage(pSoldier,pOpponent,ubBestChanceToHit, fBladeAttack ); //NumMessage("STAB EstDamage = ", iEstDamage); @@ -2399,155 +2400,167 @@ INT32 EstimateThrowDamage( SOLDIERTYPE *pSoldier, UINT8 ubItemPos, SOLDIERTYPE * return( iDamage); } -INT32 EstimateStabDamage( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, - INT16 ubChanceToHit, BOOLEAN fBladeAttack ) +INT32 EstimateStabDamage( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT16 ubChanceToHit, BOOLEAN fBladeAttack ) { - INT32 iImpact, iFluke, iBonus; + INT32 iImpact, iBonus; + UINT16 usItem; - UINT16 usItem; + CHECKF(pSoldier); + CHECKF(pOpponent); usItem = pSoldier->inv[HANDPOS].usItem; if (fBladeAttack) { - iImpact = GetDamage(&pSoldier->inv[HANDPOS]); - iImpact += EffectiveStrength( pSoldier, FALSE ) / 20; // 0 to 5 for strength, adjusted by damage taken + iImpact = GetDamage(&(pSoldier->inv[HANDPOS])); + iImpact += EffectiveStrength(pSoldier, FALSE) / 20; // 0 to 5 for strength, adjusted by damage taken + + if (AM_A_ROBOT(pOpponent)) + { + iImpact /= 4; + } } else { - // this all was a little changed for enhanced close combat system - SANDRO - if ( gGameExternalOptions.fEnhancedCloseCombatSystem ) - { - iImpact = EffectiveStrength( pSoldier, FALSE ) / 5; // 0 to 20 for strength, adjusted by damage taken + iImpact = EffectiveStrength(pSoldier, FALSE) / 5; // 0 to 20 for strength, adjusted by damage taken - if ( pSoldier->usAttackingWeapon ) + // NB martial artists don't get a bonus for using brass knuckles! - oh, they do in STOMP - SANDRO + if (pSoldier->usAttackingWeapon) + { + if (gGameOptions.fNewTraitSystem) { - iImpact += GetDamage(&pSoldier->inv[HANDPOS]); + iImpact += GetDamage(&(pSoldier->inv[HANDPOS])); + + if (AM_A_ROBOT(pOpponent)) + { + iImpact /= 2; + } } else { - // base HTH damage - iImpact += 4; // Slightly reduced for we can now attack to head for bigger damage - SANDRO - // Add melee damage multiplier to bare HtH attacks as well - SANDRO - // actually I make the influence a little lesser, because to the blades and so, - // only the item impact is multiplied, not the level and strength bonus, but here it does - iImpact += iImpact * gGameExternalOptions.iMeleeDamageModifier / 120; + if (!HAS_SKILL_TRAIT(pSoldier, MARTIALARTS_OT)) + { + iImpact += GetDamage(&(pSoldier->inv[HANDPOS])); + } + if (AM_A_ROBOT(pOpponent)) + { + iImpact /= 2; + } } } else { - - // NB martial artists don't get a bonus for using brass knuckles! - if (pSoldier->usAttackingWeapon && !( HAS_SKILL_TRAIT( pSoldier, MARTIALARTS_OT ) ) ) + // base HTH damage + // Enhanced Close Combat System - Slightly reduced for we can now attack to head for bigger damage + if (gGameExternalOptions.fEnhancedCloseCombatSystem) { - iImpact = GetDamage(&pSoldier->inv[HANDPOS]); + iImpact += 4; } else { - // base HTH damage - iImpact = 5; + iImpact += 5; + } + + // Add melee damage multiplier to HtH attacks as well - SANDRO + iImpact = (INT32)(iImpact * gGameExternalOptions.iMeleeDamageModifier / 100); + + if (AM_A_ROBOT(pOpponent)) + { + iImpact = 0; } - iImpact += EffectiveStrength( pSoldier, FALSE ) / 5; // 0 to 20 for strength, adjusted by damage taken } } + iImpact += EffectiveExpLevel(pSoldier) / 2; - iImpact += (EffectiveExpLevel( pSoldier ) / 2); // 0 to 4 for level // SANDRO - added effective level calc + // up to 25% extra impact for accurate attacks + iImpact = iImpact * (100 + ubChanceToHit / 4) / 100; - iFluke = 0; - iBonus = ubChanceToHit / 4; // up to 50% extra impact for accurate attacks - - iImpact = iImpact * (100 + iFluke + iBonus) / 100; + iBonus = 0; if (!fBladeAttack) { - // add bonuses for hand-to-hand and martial arts - // Check for new traits - SANDRO - if ( gGameOptions.fNewTraitSystem ) + if (gGameOptions.fNewTraitSystem) { - if (!pSoldier->usAttackingWeapon || Item[pSoldier->inv[HANDPOS].usItem].brassknuckles) + if (!pSoldier->usAttackingWeapon || Item[usItem].brassknuckles) { - // add bonuses for martial arts - if ( HAS_SKILL_TRAIT( pSoldier, MARTIAL_ARTS_NT ) ) + // add bonus for martial arts + if (HAS_SKILL_TRAIT(pSoldier, MARTIAL_ARTS_NT)) { - // also add breath damage bonus into consideration - iImpact = (INT32)(iImpact * ( 100 + (gSkillTraitValues.ubMABonusDamageHandToHand + gSkillTraitValues.ubMABonusBreathDamageHandToHand) * NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT ) ) / 100.0f + 0.5f); - - - // The Spinning kicks or aimed punch bonus - SANDRO - //if (pSoldier->usAnimState == NINJA_SPINKICK || (pSoldier->aiData.bAimTime > 0)) - //{ - // iImpact += (iImpact * gSkillTraitValues.usMAAimedPunchDamageBonus * NUM_SKILL_TRAITS( pSoldier, MARTIAL_ARTS_NT ) ) / 100; // +75% damage per trait - //} + iBonus += (gSkillTraitValues.ubMABonusDamageHandToHand * NUM_SKILL_TRAITS(pSoldier, MARTIAL_ARTS_NT)); } } else { - // +30% damage of blunt weapons for melee character - if (HAS_SKILL_TRAIT( pSoldier, MELEE_NT )) + // bonus damage of blunt weapons for melee character + if (HAS_SKILL_TRAIT(pSoldier, MELEE_NT)) { - iImpact = (INT32)(iImpact * (100 + gSkillTraitValues.ubMEDamageBonusBlunt) / 100.0f + 0.5f); - - //if (pSoldier->aiData.bAimTime > 0) - //{ - // iImpact += (iImpact * (100 + gSkillTraitValues.usMEAimedMeleeAttackDamageBonus) / 100); // 50% incresed damage if focused melee attack - //} + iBonus += gSkillTraitValues.ubMEDamageBonusBlunt; } } } - else + else // original code { - if ( HAS_SKILL_TRAIT( pSoldier, MARTIALARTS_OT ) ) + // add bonuses for hand-to-hand and martial arts + if (HAS_SKILL_TRAIT(pSoldier, MARTIALARTS_OT)) { - iImpact = (INT32)(iImpact * (100 + gbSkillTraitBonus[MARTIALARTS_OT] * NUM_SKILL_TRAITS( pSoldier, MARTIALARTS_OT ) ) / 100.0f + 0.5f); + iBonus += gbSkillTraitBonus[MARTIALARTS_OT] * NUM_SKILL_TRAITS(pSoldier, MARTIALARTS_OT); + + if (pSoldier->usAnimState == NINJA_SPINKICK) + { + iBonus += 100; + } } - if ( HAS_SKILL_TRAIT( pSoldier, HANDTOHAND_OT ) ) + + if (HAS_SKILL_TRAIT(pSoldier, HANDTOHAND_OT)) { - iImpact = (INT32)(iImpact * (100 + gbSkillTraitBonus[HANDTOHAND_OT] * NUM_SKILL_TRAITS( pSoldier, HANDTOHAND_OT ) ) / 100.0f + 0.5f); + // SPECIAL - give TRIPLE bonus for damage for hand-to-hand trait + // because the HTH bonus is half that of martial arts, and gets only 1x for to-hit bonus + iBonus += 3 * gbSkillTraitBonus[HANDTOHAND_OT] * NUM_SKILL_TRAITS(pSoldier, HANDTOHAND_OT); } } - // SANDRO - Enhanced Close Combat System - aiming at body parts makes difference - if (gGameExternalOptions.fEnhancedCloseCombatSystem && (gAnimControl[ pOpponent->usAnimState ].ubEndHeight == ANIM_PRONE)) - { - iImpact = (INT32)(iImpact * 150 / 100); // 50% incresed damage to lying characters - } - // Here, if we're doing a bare-fisted attack, - // we want to pay attention just to wounds inflicted - // SANDRO - No, we may consider the breath damage as asort of "real" damage too, so only reduce it by half - iImpact = iImpact / 2; - //iImpact = iImpact / PUNCH_REAL_DAMAGE_PORTION; - } - // SANDRO - damage bonus to melee trait - else - { - if ( HAS_SKILL_TRAIT( pSoldier, MELEE_NT ) && (gGameOptions.fNewTraitSystem) ) - { - iImpact += (iImpact * (100 + gSkillTraitValues.ubMEDamageBonusBlades ) / 100); // +30% damage - - //if (pSoldier->aiData.bAimTime > 0) - //{ - // iImpact += (iImpact * ( 100 + gSkillTraitValues.usMEAimedMeleeAttackDamageBonus ) / 100); // 50% incresed damage if focused melee attack - //} - } - // SANDRO - Enhanced Close Combat System + if (gGameExternalOptions.fEnhancedCloseCombatSystem) { - if (gAnimControl[ pOpponent->usAnimState ].ubEndHeight == ANIM_PRONE) + if (gAnimControl[pOpponent->usAnimState].ubEndHeight == ANIM_PRONE) { - iImpact = (INT32)(iImpact * 140 / 100); // 40% incresed damage to lying characters + iBonus += 30; // 30% increased damage to lying characters + } + } + } + // DAMAGE BONUS TO KNIFE ATTACK WITH MELEE SKILL + else + { + if (gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT(pSoldier, MELEE_NT)) + { + iBonus += gSkillTraitValues.ubMEDamageBonusBlades; // +30% damage + } + + // Enhanced Close Combat System + if (gGameExternalOptions.fEnhancedCloseCombatSystem) + { + if (gAnimControl[pOpponent->usAnimState].ubEndHeight == ANIM_PRONE) + { + iBonus += 30; // increased damage to lying characters } } } - // Flugente: Add personal damage bonus - iImpact = (iImpact * (100 + pSoldier->GetMeleeDamageBonus() ) / 100); + // apply all bonuses + iImpact = (iImpact * (100 + iBonus) + 50) / 100; // round it properly - if (iImpact < 1) + // Flugente: moved the damage calculation into a separate function + iImpact = max(1, (INT32)(iImpact * (100 - pOpponent->GetDamageResistance(FALSE, FALSE)) / 100)); + + // Flugente: Add personal damage bonus + if (fBladeAttack) { - iImpact = 1; + iImpact = (iImpact * (100 + pSoldier->GetMeleeDamageBonus()) / 100); } - return( iImpact ); + iImpact = max(1, iImpact); + + return iImpact; } INT8 TryToReload( SOLDIERTYPE * pSoldier ) diff --git a/TacticalAI/DecideAction.cpp b/TacticalAI/DecideAction.cpp index 597cde532..1777ee15b 100644 --- a/TacticalAI/DecideAction.cpp +++ b/TacticalAI/DecideAction.cpp @@ -5559,6 +5559,26 @@ INT16 ubMinAPCost; if (BestStab.ubPossible) { + INT32 sAttackDist = PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget); + INT32 sMaxStabAttackDist = TACTICAL_RANGE / 8; + // sevenfm: limit stab attacks when target is not very close + if (sAttackDist > sMaxStabAttackDist) + { + BestStab.iAttackValue = BestStab.iAttackValue * sMaxStabAttackDist / sAttackDist; + } + + if (!(gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT(pSoldier, MELEE_NT)) && + !(!gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT(pSoldier, KNIFING_OT))) + { + BestStab.iAttackValue /= 4; + } + + // sevenfm: reduce stab attack attractiveness depending on number of seen opponents + if (pSoldier->aiData.bOppCnt > 1) + { + BestStab.iAttackValue /= pSoldier->aiData.bOppCnt; + } + // now we KNOW FOR SURE that we will do something (stab, at least) NPCDoesAct(pSoldier); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"NPC decided to stab"); @@ -5604,62 +5624,72 @@ INT16 ubMinAPCost; if (BestStab.ubPossible) { - // if we have not enough APs to deal at least two or three punches, - // reduce the attack value as one punch ain't much - if( gGameOptions.fNewTraitSystem ) + if (!(pSoldier->flags.uiStatusFlags & SOLDIER_BOXER)) { - // if we are not specialized, reduce the attack attractiveness generaly - if ( !HAS_SKILL_TRAIT( pSoldier, MARTIAL_ARTS_NT ) ) + // if we have not enough APs to deal at least two or three punches, + // reduce the attack value as one punch ain't much + if (gGameOptions.fNewTraitSystem) { - BestStab.iAttackValue /= 4; - // if too far and not having APs for at least 3 hits no way to attack - if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + (2 * (ApsToPunch( pSoldier )))) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) ) + // if we are not specialized, reduce the attack attractiveness generally + if (!HAS_SKILL_TRAIT(pSoldier, MARTIAL_ARTS_NT)) { - BestStab.ubPossible = 0; - BestStab.iAttackValue = 0; + BestStab.iAttackValue /= 4; + // if too far and not having APs for at least 3 hits no way to attack + if (((CalcTotalAPsToAttack(pSoldier, BestStab.sTarget, ADDTURNCOST, 0) + (2 * (ApsToPunch(pSoldier)))) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1)) + { + BestStab.ubPossible = 0; + BestStab.iAttackValue = 0; + } + } + else + { + if (PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) + { + BestStab.iAttackValue = (BestStab.iAttackValue * 2); + } + // if too far and not having APs for at least 2 hits + else if (((CalcTotalAPsToAttack(pSoldier, BestStab.sTarget, ADDTURNCOST, 0) + ApsToPunch(pSoldier)) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1)) + { + BestStab.iAttackValue /= 3; + } } } else { - if (PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) + if (!HAS_SKILL_TRAIT(pSoldier, MARTIALARTS_OT) && !HAS_SKILL_TRAIT(pSoldier, HANDTOHAND_OT)) { - BestStab.iAttackValue = (BestStab.iAttackValue * 2); + // if we are not specialized, reduce the attack attractiveness generally + BestStab.iAttackValue /= 4; + // if too far and not having APs for at least 3 hits + if (((CalcTotalAPsToAttack(pSoldier, BestStab.sTarget, ADDTURNCOST, 0) + (2 * (ApsToPunch(pSoldier)))) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget <= 1))) + { + BestStab.ubPossible = 0; + BestStab.iAttackValue = 0; + } } - // if too far and not having APs for at least 2 hits - else if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + ApsToPunch( pSoldier )) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1)) + else { - BestStab.iAttackValue /= 3; - } - } - } - else - { - if ( !HAS_SKILL_TRAIT( pSoldier, MARTIALARTS_OT ) && !HAS_SKILL_TRAIT( pSoldier, HANDTOHAND_OT ) ) - { - // if we are not specialized, reduce the attack attractiveness generaly - BestStab.iAttackValue /= 4; - // if too far and not having APs for at least 3 hits - if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + (2 * (ApsToPunch( pSoldier )))) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget <= 1)) ) - { - BestStab.ubPossible = 0; - BestStab.iAttackValue = 0; - } - } - else - { - BestStab.iAttackValue = ((BestStab.iAttackValue * 3)/2); + BestStab.iAttackValue = ((BestStab.iAttackValue * 3) / 2); - if (PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) - { - BestStab.iAttackValue = ((BestStab.iAttackValue * 3)/2); - } - // if too far and not having APs for at least 2 hits - else if (((CalcTotalAPsToAttack( pSoldier,BestStab.sTarget,ADDTURNCOST, 0 ) + ApsToPunch( pSoldier )) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget <= 1)) ) - { - BestStab.iAttackValue /= 3; + if (PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget) <= 1) + { + BestStab.iAttackValue = ((BestStab.iAttackValue * 3) / 2); + } + // if too far and not having APs for at least 2 hits + else if (((CalcTotalAPsToAttack(pSoldier, BestStab.sTarget, ADDTURNCOST, 0) + ApsToPunch(pSoldier)) > pSoldier->bActionPoints) && !(PythSpacesAway(pSoldier->sGridNo, BestStab.sTarget <= 1))) + { + BestStab.iAttackValue /= 3; + } } } - } + + // sevenfm: reduce HTH attack attractiveness depending on number of seen opponents + if (pSoldier->aiData.bOppCnt > 1) + { + BestStab.iAttackValue /= pSoldier->aiData.bOppCnt; + } + } + // now we KNOW FOR SURE that we will do something (stab, at least) NPCDoesAct(pSoldier); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"NPC decided to punch"); @@ -6156,7 +6186,8 @@ L_NEWAIM: pSoldier->bDoAutofire--; //dnl ch69 130913 let try increase autofire rate for aim cost - if(!UsingNewCTHSystem() && + // sevenfm: LIMIT_MAX_DEVIATION option increases effectiveness of suppression + if ((!UsingNewCTHSystem() || gGameCTHConstants.LIMIT_MAX_DEVIATION) && pSoldier->bDoAutofire < 3 && pSoldier->aiData.bAimTime > 0 && pSoldier->inv[BestAttack.bWeaponIn][0]->data.gun.ubGunShotsLeft >= 3 &&