From 2196a73b1acc6640049bb23872d2646b6acdad3e Mon Sep 17 00:00:00 2001 From: silversurfer Date: Tue, 30 Oct 2018 18:42:51 +0000 Subject: [PATCH] Fix (by Sevenfm) - penalty for shooting with a height difference was wrong git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8632 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameVersion.cpp | 2 +- Tactical/Weapons.cpp | 1274 +----------------------------------------- 2 files changed, 2 insertions(+), 1274 deletions(-) diff --git a/GameVersion.cpp b/GameVersion.cpp index b5ef233a..da6cfe39 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -57,6 +57,6 @@ CHAR8 czVersionNumber[16] = { "Build 18.30.10" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; -CHAR16 zRevisionNumber[16] = { L"Revision 8630" }; +CHAR16 zRevisionNumber[16] = { L"Revision 8632" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index fd678941..ade66a37 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -6678,1278 +6678,6 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun: ichance = %d",iChance)); return (iChance); - /*CHRISL: I've tried to sort and reorganize this function to make it a little easier to follow. All of this is the original function - //SOLDIERTYPE *vicpSoldier; - SOLDIERTYPE * pTarget; - INT32 iChance; //CTH - INT32 iRange; //Actual range to target - INT32 iSightRange; //LOS range - INT32 iTileRange; //LOS range to the tile target is in - in case we can't see the target - INT32 iVisionRange; //Effective visual range - INT32 iMaxRange; //Weapon maximum range - INT32 iGunCondition; //Weapons effective condition - INT32 iMarksmanship; //Mercs effective marksmanship - INT32 iBonus, iPenalty; - //rain - //INT32 iScopeBonus; - //end rain - UINT16 usInHand; - OBJECTTYPE * pInHand; -// INT8 bAttachPos; - INT8 bBandaged; - INT16 sDistVis; - UINT8 ubAdjAimPos; - UINT8 ubTargetID; - bool highPowerScope = false; - UINT32 pScope; - UINT8 bLightLevel = LightTrueLevel(sGridNo, pSoldier->bTargetLevel); - - // HEADROCK HAM 3.5: Variable holds total autofire penalty. - INT16 sTotalAutofirePenalty = 0; - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun")); - - if ( pSoldier->stats.bMarksmanship == 0 ) - { - // HEADROCK: (HAM) Altered to accept external arguments - // return( MINCHANCETOHIT ); - return( gGameExternalOptions.ubMinimumCTH ); - } - - // make sure the guy's actually got a weapon in his hand! - pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]); - usInHand = pSoldier->usAttackingWeapon; - //CHRISL: We have new modifiers when using a high power scope so find out if we have one from the start - for(attachmentList::iterator iter = (*pInHand)[0]->attachments.begin(); iter != (*pInHand)[0]->attachments.end(); iter++) - { - if(Item[iter->usItem].aimbonus >= gGameExternalOptions.sHighPowerScope && iter->exists()) - { - highPowerScope = true; - pScope = iter->usItem; - break; - } - } - - ///////////////////////////////////////////////////////////////////////////////////// - // DETERMINE BASE CHANCE OF HITTING - iGunCondition = WEAPON_STATUS_MOD( (*pInHand)[0]->data.gun.bGunStatus ); - - if (Item[usInHand].rocketlauncher ) - { - // use the same calculation as for mechanical thrown weapons - iMarksmanship = ( EffectiveDexterity( pSoldier ) + EffectiveMarksmanship( pSoldier ) + EffectiveWisdom( pSoldier ) + (10 * EffectiveExpLevel( pSoldier ) )) / 4; - // heavy weapons trait helps out - if (HAS_SKILL_TRAIT( pSoldier, HEAVY_WEAPS_OT ) && !( gGameOptions.fNewTraitSystem )) // SANDRO - old/new traits - { - iMarksmanship += gbSkillTraitBonus[HEAVY_WEAPS_OT] * NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPS_OT ); - } - } - else - { - iMarksmanship = EffectiveMarksmanship( pSoldier ); - - if ( AM_A_ROBOT( pSoldier ) ) - { - SOLDIERTYPE * pSoldier2; - - pSoldier2 = pSoldier->GetRobotController( ); - if ( pSoldier2 ) - { - iMarksmanship = __max( iMarksmanship, EffectiveMarksmanship( pSoldier2 ) ); - } - } - } - - // modify chance to hit by morale - iMarksmanship += GetMoraleModifier( pSoldier ); - - // penalize marksmanship for fatigue - iMarksmanship -= GetSkillCheckPenaltyForFatigue( pSoldier, iMarksmanship ); - - //if ( PTR_OURTEAM ) - //{ - // iChance = ( iMarksmanship * iGunCondition ) / 100; - //} - //else - { - if (iGunCondition >= iMarksmanship) - // base chance is equal to the shooter's marksmanship skill - iChance = iMarksmanship; - else - // base chance is equal to the average of marksmanship & gun's condition! - iChance = (iMarksmanship + iGunCondition) / 2; - } - ///////////////////////////////////////////////////////////////////////////////////// - - // if shooting same target as the last shot - if (sGridNo == pSoldier->sLastTarget ) - iChance += AIM_BONUS_SAME_TARGET; // give a bonus to hit - - ///////////////////////////////////////////////////////////////////////////////////// - // SANDRO - STOMP traits - //////////////////////////////////////////////////// - // For every type of gun, there was added a penalty for untrained mercs. This penalty - // is rather small and is often compensated by single trait of that type - if( gGameOptions.fNewTraitSystem ) - { - // Bonus for heavy weapons moved here from above to get instant CtH bonus and not marksmanship bonus, - // which is supressed by weapon condition - if (Item[usInHand].rocketlauncher || Item[usInHand].singleshotrocketlauncher) - { - iChance += gSkillTraitValues.bCtHModifierRocketLaunchers; // -25% for untrained mercs !!! - - if (HAS_SKILL_TRAIT( pSoldier, HEAVY_WEAPONS_NT )) - iChance += gSkillTraitValues.ubHWBonusCtHRocketLaunchers * NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPONS_NT ); // +25% per trait - } - // Added CtH bonus for Gunslinger trait on pistols and machine-pistols - else if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL ) - { - iChance += gSkillTraitValues.bCtHModifierPistols; // -5% for untrained mercs. - - // this bonus is applied only on single shots! - if ( HAS_SKILL_TRAIT( pSoldier, GUNSLINGER_NT ) && pSoldier->bDoBurst == 0 && pSoldier->bDoAutofire == 0 ) - iChance += gSkillTraitValues.ubGSBonusCtHPistols * NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT ); // +10% per trait - } - else if ( Weapon[usInHand].ubWeaponType == GUN_M_PISTOL ) - { - iChance += gSkillTraitValues.bCtHModifierMachinePistols; // -5% for untrained mercs. - - // this bonus is applied only on single shots! - if ( HAS_SKILL_TRAIT( pSoldier, GUNSLINGER_NT ) && ((pSoldier->bDoBurst == 0 && pSoldier->bDoAutofire == 0) || !gSkillTraitValues.ubGSCtHMPExcludeAuto)) - iChance += gSkillTraitValues.ubGSBonusCtHMachinePistols * NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT ); // +5% per trait - } - // Added CtH bonus for Machinegunner skill on assault rifles, SMGs and LMGs - else if ( Weapon[usInHand].ubWeaponType == GUN_AS_RIFLE ) - { - iChance += gSkillTraitValues.bCtHModifierAssaultRifles; // -5% for untrained mercs. - - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) - iChance += gSkillTraitValues.ubAWBonusCtHAssaultRifles * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT ); // +5% per trait - } - else if ( Weapon[usInHand].ubWeaponType == GUN_SMG ) - { - iChance += gSkillTraitValues.bCtHModifierSMGs; // -5% for untrained mercs. - - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) - iChance += gSkillTraitValues.ubAWBonusCtHSMGs * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT ); // +5% per trait - } - else if ( Weapon[usInHand].ubWeaponType == GUN_LMG ) - { - iChance += gSkillTraitValues.bCtHModifierLMGs; // -10% for untrained mercs. - - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) - iChance += gSkillTraitValues.ubAWBonusCtHLMGs * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT ); // +5% per trait - } - // Added CtH bonus for Gunslinger trait on pistols and machine-pistols - else if ( Weapon[usInHand].ubWeaponType == GUN_SN_RIFLE ) - { - iChance += gSkillTraitValues.bCtHModifierSniperRifles; // -5% for untrained mercs. - - // this bonus is applied only on single shots! - if ( HAS_SKILL_TRAIT( pSoldier, SNIPER_NT ) && pSoldier->bDoBurst == 0 && pSoldier->bDoAutofire == 0 ) - iChance += gSkillTraitValues.ubSNBonusCtHSniperRifles * NUM_SKILL_TRAITS( pSoldier, SNIPER_NT ); // +5% per trait - } - // Added CtH bonus for Ranger skill on rifles and shotguns - else if ( Weapon[usInHand].ubWeaponType == GUN_RIFLE ) - { - iChance += gSkillTraitValues.bCtHModifierRifles; // -5% for untrained mercs. - - // this bonus is applied only on single shots! - if ( HAS_SKILL_TRAIT( pSoldier, RANGER_NT ) && pSoldier->bDoBurst == 0 && pSoldier->bDoAutofire == 0 ) - iChance += gSkillTraitValues.ubRABonusCtHRifles * NUM_SKILL_TRAITS( pSoldier, RANGER_NT ); // +5% per trait - //CHRISL: Why wouldn't sniper training include standard rifles which are often used as "poor-man sniper rifles" - // this bonus is applied only on single shots! - if ( HAS_SKILL_TRAIT( pSoldier, SNIPER_NT ) && pSoldier->bDoBurst == 0 && pSoldier->bDoAutofire == 0 ) - iChance += gSkillTraitValues.ubSNBonusCtHRifles * NUM_SKILL_TRAITS( pSoldier, SNIPER_NT ); // +5% per trait - } - else if ( Weapon[usInHand].ubWeaponType == GUN_SHOTGUN ) - { - iChance += gSkillTraitValues.bCtHModifierShotguns; // -5% for untrained mercs. - - if ( HAS_SKILL_TRAIT( pSoldier, RANGER_NT ) ) - iChance += gSkillTraitValues.ubRABonusCtHShotguns * NUM_SKILL_TRAITS( pSoldier, RANGER_NT ); // +10% per trait - } - - // Added small CtH penalty for robot if controller hasn't the Technician trait - if( AM_A_ROBOT( pSoldier ) ) - { - iChance += gSkillTraitValues.bCtHModifierRobot; // -10% - - if ( HAS_SKILL_TRAIT( pSoldier->GetRobotController(), TECHNICIAN_NT ) ) - iChance += gSkillTraitValues.ubTECtHControlledRobotBonus * NUM_SKILL_TRAITS( pSoldier->GetRobotController(), TECHNICIAN_NT ); // +10% per trait - } - - // Added character traits influence - if ( pSoldier->ubProfile != NO_PROFILE ) - { - // Sociable - better performance in groups - if ( gMercProfiles[ pSoldier->ubProfile ].bCharacterTrait == CHAR_TRAIT_SOCIABLE ) - { - INT8 bNumMercs = CheckMercsNearForCharTraits( pSoldier->ubProfile, CHAR_TRAIT_SOCIABLE ); - if ( bNumMercs > 2 ) - iChance += 5; - else if ( bNumMercs > 0 ) - iChance += 2; - } - // Loner - better performance when alone - else if ( gMercProfiles[ pSoldier->ubProfile ].bCharacterTrait == CHAR_TRAIT_LONER ) - { - INT8 bNumMercs = CheckMercsNearForCharTraits( pSoldier->ubProfile, CHAR_TRAIT_LONER ); - if ( bNumMercs == 0 ) - iChance += 5; - else if ( bNumMercs <= 1 ) - iChance += 2; - } - // Aggressive - bonus on bursts/autofire - else if ( gMercProfiles[ pSoldier->ubProfile ].bCharacterTrait == CHAR_TRAIT_AGGRESSIVE ) - { - if (( pSoldier->bDoBurst || pSoldier->bDoAutofire ) && !ubAimTime ) - iChance += 5; - } - // Show-off - better performance if some babes around to impress - else if ( gMercProfiles[ pSoldier->ubProfile ].bCharacterTrait == CHAR_TRAIT_SHOWOFF ) - { - INT8 bNumMercs = CheckMercsNearForCharTraits( pSoldier->ubProfile, CHAR_TRAIT_SHOWOFF ); - if ( bNumMercs > 1 ) - iChance += 5; - else if ( bNumMercs > 0 ) - iChance += 2; - } - // Added disabilities - if ( pSoldier->ubProfile != NO_PROFILE ) - { - // Heat intolerant penalty - if ( MercIsHot( pSoldier ) ) - { - iChance -= 15; - } - // Small penalty for fear of insects in tropical sectors - else if ( (gMercProfiles[ pSoldier->ubProfile ].bDisability == FEAR_OF_INSECTS) && MercIsInTropicalSector( pSoldier ) ) - { - // fear of insects, and we are in tropical sector - iChance -= 5; - } - } - } - // Dauntless - penalty for not taking proper cover - pTarget = SimpleFindSoldier( sGridNo, pSoldier->bTargetLevel ); - if (pTarget != NULL) - { - if ( pTarget->ubProfile != NO_PROFILE ) - { - if ( gMercProfiles[ pTarget->ubProfile ].bCharacterTrait == CHAR_TRAIT_DAUNTLESS ) - iChance += 5; - } - } - } - else - { - // this rather unlogical bonus for psychotic characters applies only with old traits - if ( pSoldier->ubProfile != NO_PROFILE && gMercProfiles[ pSoldier->ubProfile ].bDisability == PSYCHO ) - { - iChance += AIM_BONUS_PSYCHO; - } - } - - // calculate actual range (in units, 10 units = 1 tile) - iRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ); - - //CHRISL: Incur a penalty if using a highPowerScope - if(highPowerScope == true && iRange > Item[pScope].minrangeforaimbonus) - { - iPenalty = (Item[pScope].aimbonus * (iRange-Item[pScope].minrangeforaimbonus)) / 1000; - if (iPenalty > AIM_BONUS_PRONE) - { - iPenalty = AIM_BONUS_PRONE; - } - iChance -= iPenalty; - } - // if shooter is crouched, he aims slightly better (to max of AIM_BONUS_CROUCHING) - if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH ) - { - iBonus = iRange / 10; - if (iBonus > AIM_BONUS_CROUCHING) - { - iBonus = AIM_BONUS_CROUCHING; - } - iChance += iBonus; - } - // if shooter is prone, he aims even better, except at really close range - else if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE ) - { - if (iRange > MIN_PRONE_RANGE) - { - iBonus = iRange / 10; - if (iBonus > AIM_BONUS_PRONE) - { - iBonus = AIM_BONUS_PRONE; - } - // Snap: bipod bonus is now calculated in GetToHitBonus - //bAttachPos = FindAttachment( pInHand, BIPOD ); - //if (bAttachPos != ITEM_NOT_FOUND) - //{ // extra bonus to hit for a bipod, up to half the prone bonus itself - // iBonus += (iBonus * WEAPON_STATUS_MOD(GetBipodStatus(pInHand)) / 100) / 2; - //} - iChance += iBonus; - } - } - - //Madd: inherent weapon accuracy bonus - iChance += Weapon[usInHand].bAccuracy; - -// if ( !(Item[ usInHand ].fFlags & ITEM_TWO_HANDED) ) - if ( !(Item[ usInHand ].twohanded ) ) - { - if (pSoldier->inv[SECONDHANDPOS].exists() == false) - { - // firing with gun in right hand, and second hand empty (ie: no grenade, med kit or anything there) - // Madd: easier to fire pistol/smg w/one hand free, essentially this will make pistols a little bit more accurate, and hopefully still useful later in the game - if (Weapon[usInHand].ubWeaponClass == HANDGUNCLASS) - iChance += AIM_BONUS_TWO_HANDED_PISTOL; - } - else - { - // Madd: harder to fire smgs w/something in other hand - // SMGs are treated as pistols for these purpose except there is a -5 penalty; - if (Weapon[usInHand].ubWeaponClass == SMGCLASS) - { - iChance -= AIM_PENALTY_SMG; - } - - ////////////////////////////////////////////////////////////////// - // SANDRO - old/new traits - if (gGameOptions.fNewTraitSystem) - { - // Okay, we will shoot from both weapons, calculate penalty to CtH - if ( pSoldier->IsValidSecondHandShot() ) - { - // penaly to shoot from dual weapons - iChance -= (INT32)(gSkillTraitValues.ubCtHPenaltyDualShot * (HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT ) ? ((100 - gSkillTraitValues.ubAMPenaltyDoubleReduction)/100) : 1 )); - } - } - else // original code - { - if ( !HAS_SKILL_TRAIT( pSoldier, AMBIDEXT_OT ) ) - { - if ( pSoldier->IsValidSecondHandShot( ) ) - { - // penalty to aim when firing two pistols - iChance -= AIM_PENALTY_DUAL_PISTOLS; - } -// else -// { -// // penalty to aim with pistol being fired one-handed -// iChance -= AIM_PENALTY_ONE_HANDED_PISTOL; -// } - } - } - ////////////////////////////////////////////////////////////////// - } - } - - // If in burst mode, deduct points for chance to hit for each shot after the first - if ( pSoldier->bDoBurst && pSoldier->bDoAutofire == 0 ) - { -// CHRISL: At this point in the calculation, Bipods, Foregrips and other "recoil stabalizing" bonuses should reduce the weapons "recoil penalty" -// (BurstPenalty) by a percentage. This reduction should apply the the recoil per shot and not as a "flat" initial reduction. Later in the code, -// Bipods also grant their flat CTH bonus so we don't need to mess this that here - if(gGameExternalOptions.ubFlatAFTHBtoPrecentMultiplier) - { - iBonus = GetBurstToHitBonus(pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE) * gGameExternalOptions.ubFlatAFTHBtoPrecentMultiplier; - iPenalty = Weapon[pInHand->usItem].ubBurstPenalty * (pSoldier->bDoBurst - 1) * (gGameExternalOptions.bAimedBurstEnabled?gGameExternalOptions.uAimedBurstPenalty:1); - iPenalty = max(0, (iPenalty * (100 - iBonus))/100 ); - } - else - { - // Snap: bipod may reduce burst penalty - iPenalty = GetBurstPenalty(pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE) * (pSoldier->bDoBurst - 1); - if(gGameExternalOptions.bAimedBurstEnabled) - iPenalty += Weapon[usInHand].ubBurstPenalty * (pSoldier->bDoBurst - 1) * gGameExternalOptions.uAimedBurstPenalty; - } - - // halve the penalty for people with the autofire trait - ////////////////////////////////////////////////////////////////// - if ( gGameOptions.fNewTraitSystem ) // old/new traits - SANDRO - { - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) - { - iPenalty = max(0, ((iPenalty * (100 - gSkillTraitValues.ubAWAutoFirePenaltyReduction * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT )))/100)); - } - } - else - { - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPS_OT ) ) - { - iPenalty /= 2 * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPS_OT ); - } - } - ////////////////////////////////////////////////////////////////// - iChance -= iPenalty; - } - else if ( pSoldier->bDoAutofire > 0 ) - { -// CHRISL: At this point in the calculation, Bipods, Foregrips and other "recoil stabalizing" bonuses should reduce the weapons "recoil penalty" -// (AutoPenalty) by a percentage. This reduction should apply the the recoil per shot and not as a "flat" initial reduction. Later in the code, -// Bipods also grant their flat CTH bonus so we don't need to mess this that here - if(gGameExternalOptions.ubFlatAFTHBtoPrecentMultiplier) - { - iBonus = GetAutoToHitBonus(pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE) * gGameExternalOptions.ubFlatAFTHBtoPrecentMultiplier; - iPenalty = Weapon[pInHand->usItem].AutoPenalty * (pSoldier->bDoBurst - 1) * (gGameExternalOptions.bAimedBurstEnabled?gGameExternalOptions.uAimedBurstPenalty:1); - iPenalty = max(0, (iPenalty * (100 - iBonus))/100 ); - } - else - { - // Snap: bipod may reduce auto penalty - iPenalty = GetAutoPenalty(pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE) * (pSoldier->bDoBurst - 1); - if(gGameExternalOptions.bAimedBurstEnabled) - iPenalty += Weapon[usInHand].AutoPenalty * (pSoldier->bDoBurst - 1) * gGameExternalOptions.uAimedBurstPenalty; - } - - // halve the penalty for people with the autofire trait - ////////////////////////////////////////////////////////////////// - if ( gGameOptions.fNewTraitSystem ) // old/new traits - SANDRO - { - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) - { - iPenalty = max(0, ((iPenalty * (100 - gSkillTraitValues.ubAWAutoFirePenaltyReduction * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT )))/100)); - } - } - else - { - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPS_OT ) ) - { - iPenalty /= 2 * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPS_OT ); - } - } - ////////////////////////////////////////////////////////////////// - - iChance -= iPenalty; - // HEADROCK HAM 3.5: Store the penalty - sTotalAutofirePenalty = iPenalty; - } - - //ADB we need to calculate the distance visible and SoldierTo...LOSTests that we want to - //calculate it when firing, so the scope can be accounted for when the weapon is raised - //I consider this a hack to change the global, but passing a var alllll the way down to GetVisionBonus was worse - //if anyone wants to change it go for it, I don't know what usAnimState should be set - - //store old flag - UINT32 oldFlag = gAnimControl[ pSoldier->usAnimState ].uiFlags; - //add the necessary bits to the flag - gAnimControl[ pSoldier->usAnimState ].uiFlags |= (ANIM_FIREREADY | ANIM_FIRE); - - // 0verhaul: Changed to take expanded range from shooting at different levels into account - //ADB this change does nothing - either way it is random - we don't know what level we are shooting to, which is - //what the last parameter is, and the soldier's current level is as good a guess as ground level. - //so if you really want to fix this, pass in a value - sDistVis = pSoldier->GetMaxDistanceVisible(sGridNo, pSoldier->bTargetLevel, CALC_FROM_ALL_DIRS ); - - // CJC August 13 2002: Wow, this has been wrong the whole time. bTargetCubeLevel seems to be generally set to 2 - - // but if a character is shooting at an enemy in a particular spot, then we should be using the target position on the body. - - // CJC August 13, 2002 - // If the start soldier has a body part they are aiming at, and know about the person in the tile, then use that height instead - iSightRange = -1; - - // give some leeway to allow people to spot for each other... - //so make the range to calculate the bullet at 255+ - ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); - // best to use team knowledge as well, in case of spotting for someone else - // 0verhaul: Why not use the distance visible as the max for line of sight testing? - //ADB because A) the bullet can travel farther than I can see and B) I might have a spotter - - //We are firing a gun, and so the gun will be pointed and the scope will be used, even if it isn't now, so don't forget that we are in a firing animation - if (ubTargetID != NOBODY && pSoldier->aiData.bOppList[ubTargetID] == SEEN_CURRENTLY || gbPublicOpplist[pSoldier->bTeam][ubTargetID] == SEEN_CURRENTLY) - { - iSightRange = SoldierToSoldierLineOfSightTest( pSoldier, MercPtrs[ubTargetID], TRUE, NO_DISTANCE_LIMIT, pSoldier->bAimShotLocation, false ); - } - //CHRISL: This is useful if we're trying to target an opponent we can't actually see - iTileRange = SoldierTo3DLocationLineOfSightTest( pSoldier, sGridNo, pSoldier->bTargetLevel, pSoldier->bTargetCubeLevel, TRUE, NO_DISTANCE_LIMIT, false ); - if (iSightRange == -1) // didn't do a bodypart-based test - { - iSightRange = iTileRange; - } - // WANNE: Changed this, because RPGs are not in the calculation, only guns - //if ( Item[ usInHand ].usItemClass == IC_GUN ) - if ( Item[ usInHand ].usItemClass == IC_GUN || Item[ usInHand ].usItemClass == IC_LAUNCHER) - { - iMaxRange = GunRange( pInHand, pSoldier ); // SANDRO - added argument - } - else - { - iMaxRange = CELL_X_SIZE; // one tile - } - - if(iSightRange > 0){ - //Madd: Some gear can affect the to-hit bonus and the bonus from aiming - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun: before bonus - ubAimTime = %d, iSightRange = %d, iChance = %d ", ubAimTime, iSightRange, iChance)); - //CHRISL: This is supposed to be a percentage increase/decrease - //iSightRange -= GetGearAimBonus ( pSoldier, iRange, ubAimTime ); - iSightRange -= GetGearAimBonus ( pSoldier, iRange, ubAimTime ) * iSightRange / 100; - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun: after gear bonus - ubAimTime = %d, iSightRange = %d, iChance = %d ", ubAimTime, iSightRange, iChance)); - - //CHRISL: We want our AimBonus to be based on our visual range, not our actual range - // Reduce effective range by the aiming bonus (e.g. from sniper scope) - //iSightRange -= GetAimBonus( pInHand, iRange, ubAimTime ); - iSightRange -= GetAimBonus( pInHand, iSightRange, ubAimTime ); - //CHRISL: If we have the Sniper skill, reduce effective range - ////////////////////////////////////////////////////////////////// - // SANDRO - old/new traits - if ( gGameOptions.fNewTraitSystem ) - { - if ( HAS_SKILL_TRAIT( pSoldier, SNIPER_NT ) ) - { - iSightRange -= ((gSkillTraitValues.ubSNEffRangeToTargetReduction * NUM_SKILL_TRAITS( pSoldier, SNIPER_NT )) * iSightRange) /100; - } - } - else if ( HAS_SKILL_TRAIT( pSoldier, PROF_SNIPER_OT ) ) - { - iSightRange -= ((gbSkillTraitBonus[ PROF_SNIPER_OT ] * NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT )) * iSightRange) /100; - } - ////////////////////////////////////////////////////////////////// - if (iSightRange < 1) - { - iSightRange = 1; - } - } - - //restore old flag - gAnimControl[ pSoldier->usAnimState ].uiFlags = oldFlag; - - //CHRISL: Because of the changes I've made to how scopes modify iSightRange, this penalty makes it basically impossible - // to shot at targets you can't see yourself. While this isn't an issue for most weapons, it overly restricts the - // effectiveness of sniper rifles. So pull this penalty but leave the one that comes later in the code. - //if ( iSightRange > (sDistVis * CELL_X_SIZE) ) - //{ - // shooting beyond max normal vision... penalize such distance at double (also later we halve the remaining chance) - //iSightRange += (iSightRange - sDistVis * CELL_X_SIZE); - //} - - //CHRISL: Not being able to actually see the target should not restrict out ability to aim a weapon. Aiming is more then just sighting a specific target. It's - // also steadying the weapon and aiming where you think the target is. We will eventually get hit with a serious "Can't See the Target" penalty and aiming is - // the only way to even attempt to counter that. This is one reason for "Kevlar Grass" - // if shooter spent some extra time aiming and can see the target - //if (iSightRange > 0 && ubAimTime && !pSoldier->bDoBurst ) //comm by ddd - //if (iSightRange > 0 && ubAimTime ) - ////////////////////////////////////////////////////////////////// - if(ubAimTime) - { - // CHRISL: Rather then a flat +10/click bonus, we're going to try a bonus that's based on MRK and Lvl which gets - // progressivly less the more we aim. Everything is based on the maxBonus that a merc can possibly get which - // uses the equation: 20+(MRK/20*LVL)+Accuracy+(Sniper trait * 10). This value is then split between the 8 - // possible AimTime's using a max aimTime bonus of 10. - INT16 bonusProgression[8] = {500,500,600,600,750,750,750,1000}; - //FLOAT maxBonus = 20+((FLOAT)iMarksmanship/20*pSoldier->stats.bExpLevel)+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER )*10); - - ////////////////////////////////////////////////////////////////// - // SANDRO - old/new traits - FLOAT maxBonus; - if ( gGameOptions.fNewTraitSystem ) - { - // bonus to snipers and gunslingers - if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL || Weapon[usInHand].ubWeaponType == GUN_PISTOL ) - maxBonus = 20+((FLOAT)iMarksmanship/20*(EffectiveExpLevel(pSoldier)))+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT )*gSkillTraitValues.ubGSAimingBonusPerClick); - else - maxBonus = 20+((FLOAT)iMarksmanship/20*(EffectiveExpLevel(pSoldier)))+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, SNIPER_NT )*gSkillTraitValues.ubSNAimingBonusPerClick); - } - else - { - if(gGameExternalOptions.bAltAimEnabled) - maxBonus = (20*(iMarksmanship/100))+((FLOAT)iMarksmanship/20*pSoldier->stats.bExpLevel)+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT )*10); - else - maxBonus = 20+((FLOAT)iMarksmanship/20*pSoldier->stats.bExpLevel)+(Weapon[Item[pInHand->usItem].ubClassIndex].bAccuracy*2)+(NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT )*10); - } - //}ddd - ////////////////////////////////////////////////////////////////// - INT8 maxClickBonus = 10; - FLOAT aimTimeBonus; - for(int i = 0; i < ubAimTime; i++) - { - aimTimeBonus = __min((maxBonus*bonusProgression[i]/1000),maxClickBonus); - maxBonus -= aimTimeBonus; - iChance += (INT32)floor(aimTimeBonus+.5); - } - ///ddd{ - //zilpin: aimed burst - //Reduce the aim time bonus based on weapon burst penalty, after the first shot. - //The burst penalty was already applied, but it also reduces the benefit of aiming. -// CHRISL: This is actaully applying the weapons AutoPenalty a second time. We already apply AutoPenalty/round (after the first) in the autofire -// section earlier in this code. We shouldn't be applying this again here. Plus, this modification should occur regardless of whether we aim -// or not. I've moved this condition to the burst/autofire portion of the code above. -// if(gGameExternalOptions.bAimedBurstEnabled) -// { -// //if( pSoldier->bDoAutofire ) -// // iChance -= Weapon[usInHand].AutoPenalty * pSoldier->bDoBurst * gGameExternalOptions.uAimedBurstPenalty; -// //else if( pSoldier->bDoBurst>1 ) -// // iChance -= Weapon[usInHand].ubBurstPenalty * pSoldier->bDoBurst * gGameExternalOptions.uAimedBurstPenalty; -// if( pSoldier->bDoAutofire>0 && pSoldier->bDoBurst>1) -// iChance -= Weapon[usInHand].AutoPenalty * pSoldier->bDoBurst * gGameExternalOptions.uAimedBurstPenalty; -// //if( pSoldier->bDoAutofire>10 ) -// // iChance -= Weapon[usInHand].AutoPenalty * pSoldier->bDoBurst * gGameExternalOptions.uAimedBurstPenalty; -// //else -// if( pSoldier->bDoBurst>1 && pSoldier->bDoAutofire==0) -// iChance -= Weapon[usInHand].ubBurstPenalty * pSoldier->bDoBurst * gGameExternalOptions.uAimedBurstPenalty; -// -// } - ///ddd} - -// if(highPowerScope == true) -// { -// { -// if ( NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER ) == 2 ) -// aimTimeBonus = -((__max(0,75-pSoldier->stats.bMarksmanship))/(pSoldier->stats.bExpLevel*2)); -// else if( NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER ) == 1 ) -// aimTimeBonus = -((__max(0,85-pSoldier->stats.bMarksmanship))/(pSoldier->stats.bExpLevel*2)); -// else -// aimTimeBonus = -((__max(0,100-pSoldier->stats.bMarksmanship))/(pSoldier->stats.bExpLevel)); -// aimTimeBonus = __max(-9, aimTimeBonus); -// aimTimeBonus = __min(0, aimTimeBonus); -// aimTimeBonus += AIM_BONUS_PER_AP; -// } -// else -// aimTimeBonus = AIM_BONUS_PER_AP; -// iChance += (aimTimeBonus * ubAimTime); // bonus for every pt of aiming - } - ////////////////////////////////////////////////////////////////// - - if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_PC ) ) // if this is a computer AI controlled enemy - { - if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_EASY ) - { - // On easy, penalize all enemies by 5% - iChance -= 5; - } - else - { - // max with 0 to prevent this being a bonus, for JA2 it's just a penalty to make early enemies easy - // CJC note: IDIOT! This should have been a min. It's kind of too late now... - // CJC 2002-05-17: changed the max to a min to make this work. - iChance += __min( 0, gbDiff[ DIFF_ENEMY_TO_HIT_MOD ][ SoldierDifficultyLevel( pSoldier ) ] ); - } - } - - // if shooter is being affected by gas - if ( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED ) - { - iChance -= AIM_PENALTY_GASSED; - } - - // if shooter is being bandaged at the same time, his concentration is off - if (pSoldier->ubServiceCount > 0) - iChance -= AIM_PENALTY_GETTINGAID; - - // if shooter is still in shock - if (pSoldier->aiData.bShock) - { - // HEADROCK HAM B2.8: Placed a maximum here, as shock is now also used in suppression. - UINT16 usShockPenalty; - - usShockPenalty = pSoldier->aiData.bShock * AIM_PENALTY_PER_SHOCK; - - if (gGameExternalOptions.usMaxShooterCoweringPenalty > 0) - { - if ( usShockPenalty > gGameExternalOptions.usMaxShooterCoweringPenalty ) - usShockPenalty = gGameExternalOptions.usMaxShooterCoweringPenalty; - } - if ( usShockPenalty < 1 ) - usShockPenalty = 1; - - iChance -= usShockPenalty; - } - - //CHRISL: Just because we can't see a target, doesn't mean these armor/helmet modifiers should be ignored - iChance += GetGearToHitBonus ( pSoldier ); - - if ( iSightRange > 0 ) - { - //CHRISL: Moved iSightRange modifications to the range section above - // SANDRO - DELETED POINTLESS UNUSED PARTS OF CODE - - // HEADROCK HAM B2.1 : The TARGET's shock now also affects aiming. If the target is prone - // and in shock, they are harder to hit! This represents a target that's cowering as close - // to the ground (and as close to any possible cover, like a small dune or a fold of earth - // or anything like that). - if ( gGameExternalOptions.ubAimPenaltyPerTargetShock > 0 ) - { - // HEADROCK HAM B2.1 : This value determines how much penalty the target's shock-value gives the shooter. - // As of HAM B2.3: There's a maximum range at which 100% penalty is given. - // As of HAM B2.8: Target's stance and the targetted bodypart will affect the end result. - - INT8 AIM_PENALTY_PER_TARGET_SHOCK; - INT16 sCoweringPenalty = 0; - UINT8 ubCoweringDivisor; - UINT16 MIN_RANGE_FOR_FULL_COWER; - UINT16 MAX_TARGET_COWERING_PENALTY; - - AIM_PENALTY_PER_TARGET_SHOCK = gGameExternalOptions.ubAimPenaltyPerTargetShock; - MIN_RANGE_FOR_FULL_COWER = gGameExternalOptions.usMinRangeForFullCoweringPenalty; - MAX_TARGET_COWERING_PENALTY = gGameExternalOptions.usMaxTargetCoweringPenalty; - - pTarget = SimpleFindSoldier( sGridNo, pSoldier->bTargetLevel ); - if (pTarget != NULL) - { - if (pTarget->aiData.bShock ) - { - if (gAnimControl[ pTarget->usAnimState ].ubHeight == ANIM_PRONE) - { - ubCoweringDivisor = gGameExternalOptions.ubCoweringPenaltyDivisorProne; - - sCoweringPenalty = (pTarget->aiData.bShock * AIM_PENALTY_PER_TARGET_SHOCK); - sCoweringPenalty = sCoweringPenalty / ubCoweringDivisor; - sCoweringPenalty = (sCoweringPenalty * __min(iSightRange,MIN_RANGE_FOR_FULL_COWER)) / MIN_RANGE_FOR_FULL_COWER; - - if (gGameExternalOptions.usMaxTargetCoweringPenalty > 0) - { - if ( sCoweringPenalty > gGameExternalOptions.usMaxTargetCoweringPenalty ) - sCoweringPenalty = gGameExternalOptions.usMaxTargetCoweringPenalty; - } - if ( sCoweringPenalty < 1 ) - sCoweringPenalty = 1; - - iChance -= sCoweringPenalty; - } - else if (gAnimControl[ pTarget->usAnimState ].ubHeight == ANIM_CROUCH) - { - switch ( ubAimPos ) - { - case AIM_SHOT_HEAD: - ubCoweringDivisor = gGameExternalOptions.ubCoweringPenaltyDivisorCrouchedHead; - break; - case AIM_SHOT_TORSO: - case AIM_SHOT_RANDOM: - case AIM_SHOT_GLAND: - ubCoweringDivisor = gGameExternalOptions.ubCoweringPenaltyDivisorCrouchedTorso; - break; - case AIM_SHOT_LEGS: - ubCoweringDivisor = gGameExternalOptions.ubCoweringPenaltyDivisorCrouchedLegs; - break; - } - - sCoweringPenalty = (pTarget->aiData.bShock * AIM_PENALTY_PER_TARGET_SHOCK); - sCoweringPenalty = sCoweringPenalty / ubCoweringDivisor; - sCoweringPenalty = (sCoweringPenalty * __min(iSightRange,MIN_RANGE_FOR_FULL_COWER)) / MIN_RANGE_FOR_FULL_COWER; - - if (gGameExternalOptions.usMaxTargetCoweringPenalty > 0) - { - if ( sCoweringPenalty > gGameExternalOptions.usMaxTargetCoweringPenalty ) - sCoweringPenalty = gGameExternalOptions.usMaxTargetCoweringPenalty; - } - if ( sCoweringPenalty < 1 ) - sCoweringPenalty = 1; - - // HEADROCK HAM B2.8.1: Added formula to make sure that cowering target penalties - // are not given when on the roof. - - if (pSoldier->pathing.bLevel == pTarget->pathing.bLevel && pSoldier->pathing.bLevel > 0) - sCoweringPenalty = 0; // No cowering penalties when both are on the roof! - else if (pSoldier->pathing.bLevel < pSoldier->pathing.bLevel && gAnimControl[ pTarget->usAnimState ].ubHeight == ANIM_PRONE) - sCoweringPenalty *= 2; // Much harder to shoot at anyone cowering above you. - else if (pSoldier->pathing.bLevel > pSoldier->pathing.bLevel) - sCoweringPenalty /= 2; // Much easier to shoot at cowerers below you. - iChance -= sCoweringPenalty; - } - } - } - } - } - - //CHRISL: This should effect us regardless of whether we can see the target or not - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun: after scope bonus - ubAimTime = %d, iSightRange = %d, iChance = %d ", ubAimTime, iSightRange, iChance)); - // Apply flat to-hit bonus (e.g. from laser scope) - // Snap: If prone and range is good, apply bipod bonus here as well - iChance += GetToHitBonus( pInHand, iRange, bLightLevel,gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE && iRange > MIN_PRONE_RANGE ); - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun: after to hit bonus - ubAimTime = %d, iSightRange = %d, iChance = %d ", ubAimTime, iSightRange, iChance)); - - //NumMessage("EFFECTIVE RANGE = ",range); - - // ADJUST FOR RANGE - // bonus if range is less than normal range, penalty if it's more - //iChance += (NORMAL_RANGE - iRange) / (CELL_X_SIZE / 5); // 5% per tile - - // Effects of actual gun max range... the numbers are based on wanting -40% - // at range 26for a pistol with range 13, and -0 for a sniper rifle with range 80 - iPenalty = ((iMaxRange - iRange * 3) * 10) / (17 * CELL_X_SIZE); - if ( iPenalty < 0 ) - { - // No penalty on tanks - //if (!ARMED_VEHICLE(pSoldier)) - iChance += iPenalty; - } - //iChance -= 20 * iRange / iMaxRange; - - if ( ARMED_VEHICLE( pSoldier ) && ( iRange / CELL_X_SIZE < MaxNormalDistanceVisible() ) ) - { - // tank; penalize at close range! - // 2 percent per tile closer than max visible distance - iChance -= 2 * ( MaxNormalDistanceVisible() - (iRange / CELL_X_SIZE) ); - } - - ///////////////////////////////////////////////////////////////////////////////////////// - // Target Size Penalties - //if (iSightRange == 0) - //{ - // firing blind! - //iChance -= AIM_PENALTY_BLIND; - //} - if ( pSoldier->aiData.bOppList[ubTargetID] != SEEN_CURRENTLY &&(pSoldier->flags.uiStatusFlags & SOLDIER_PC) ) - iChance -=gGameExternalOptions.iPenaltyShootUnSeen; - else - { - // Effects based on aiming & sight - // From for JA2.5: 3% bonus/penalty for each tile different from range NORMAL_RANGE. - // This doesn't provide a bigger bonus at close range, but stretches it out, making medium - // range less penalized, and longer range more penalized - - // WANNE: No penalty on the tank - if (!ARMED_VEHICLE(pSoldier)) - iChance += 3 * ( NORMAL_RANGE - iSightRange ) / CELL_X_SIZE; - // if aiming at the head, reduce chance to hit - if (ubAimPos == AIM_SHOT_HEAD) - { - // penalty of 3% per tile - //iPenalty = 3 * iSightRange / 10; //comm by ddd - iPenalty = INT32(gGameExternalOptions.uShotHeadPenalty * iSightRange / 10); - iChance -= iPenalty; - } - else if (ubAimPos == AIM_SHOT_LEGS) - { - // penalty of 1% per tile - iPenalty = iSightRange / 10; - iChance -= iPenalty; - } - -// if (iSightRange < NORMAL_RANGE) -// { -// // bonus to hit of 20% at point blank (would be 25% at range 0); -// //at NORMAL_RANGE, bonus is 0 -// iChance += 25 * (NORMAL_RANGE - iSightRange) / NORMAL_RANGE; -// } -// else -// { -// // penalty of 2% / tile -// iChance -= (iSightRange - NORMAL_RANGE) / 5; -// } - } - ///////////////////////////////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////////////////////////////// - // Tracer effects - // HEADROCK HAM 3.5: Moved this here for now. - // HEADROCK HAM B2.5: One of every X bullets in a tracer magazine is a tracer round, which will - // bump the CTH up by a certain amount. - - if (AmmoTypes[(*pInHand)[0]->data.gun.ubGunAmmoType].tracerEffect == 1 && gGameExternalOptions.ubRealisticTracers > 0 ) - { - UINT16 iBulletsLeft, iTracersFired, iBulletsPerTracer, iBulletsSinceLastTracer=0, iRoundsFiredPreviously; - UINT8 cnt; - UINT8 ubAutoPenaltySinceLastTracer=0; - //UINT16 iAutoPenaltySinceLastTracer; - iTracersFired = 0; - iBulletsPerTracer = gGameExternalOptions.ubNumBulletsPerTracer; - - // Calculate number of bullets left right before firing this burst - if (fCalculateCTHDuringGunfire) - { - iBulletsLeft = (*pInHand)[0]->data.gun.ubGunShotsLeft + (pSoldier->bDoBurst - 1); - } - else - { - iBulletsLeft = (*pInHand)[0]->data.gun.ubGunShotsLeft; - } - -// CHRISL: What we want to know here is how many tracers have been fired during this autofire sequence. But we also want to figure that based on how -// much ammo we've used from the current load. If every 3rd round is a tracer, and we previously fired 5 rounds from our "mag", then the first -// round of this burst is a tracer, the 2nd & 3rd are normal, the 4th is a tracer, 5th & 6th are normal, etc. - iRoundsFiredPreviously = GetMagSize(pInHand) - iBulletsLeft; //How many rounds had been fired before this autofire sequence - for( cnt = 1; cnt <= pSoldier->bDoBurst; cnt++ ){ - if((iRoundsFiredPreviously+cnt)%iBulletsPerTracer == 0) - iTracersFired++; - } - -// iBulletsSinceLastTracer = 0; -// for (cnt=0;cntbDoBurst;cnt++) -// { -// iBulletsSinceLastTracer++; -// if ((( iBulletsLeft - (cnt - 1) ) / iBulletsPerTracer) - ((iBulletsLeft - cnt) / iBulletsPerTracer) == 1) -// { -// iBulletsSinceLastTracer = 0; -// } -// } -// -// iTracersFired = ((iBulletsLeft+1) / iBulletsPerTracer) - (((iBulletsLeft+1) - (pSoldier->bDoBurst)) / iBulletsPerTracer); - - if ( iTracersFired > 0 ) - { - // HEADROCK HAM 3.5: I'm going to revise this - my current system makes no sense at all. What was I - // thinking?! -// CHRISL: Headrock may come around later and revise this again, but for the time being, I'm making some adjustments. With the current code -// the number of tracer rounds fired doesn't really matter. In most cases, the range is going to limit the max bonus so much that 1 tracer or -// 20 will have the same effect because we currently look at the "bump" value times the number of rouns and THEN modify for range. What we -// should be doing is modify the bump for range, multiply that by the number of tracer rounds fired, then make sure the bonus isn't more then -// our combined autofire penalty. - - // Correct all autofire penalty so far - //iBonus = iPenalty; - - // Add Tracer Bump if previous bullet was a tracer - //iBonus += (gGameExternalOptions.ubCTHBumpPerTracer * iTracersFired); - //iBonus = (gGameExternalOptions.ubCTHBumpPerTracer * iTracersFired); - - // Calculate penalty since last tracer was fired - // CHRISL: We already look at GetAutoPenalty earlier in the code, and that result was stored as sTotalAutofirePenalty so why this wonky recalculation? - //UINT8 ubAutoPenaltySinceLastTracer = GetAutoPenalty(pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE) * iBulletsSinceLastTracer; - ////////////////////////////////////////////////////////////////// -// if ( gGameOptions.fNewTraitSystem ) // old/new traits - SANDRO -// { -// if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPONS_NT ) ) -// { -// ubAutoPenaltySinceLastTracer = max(0, ((ubAutoPenaltySinceLastTracer * (100 - gSkillTraitValues.ubAWAutoFirePenaltyReduction * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPONS_NT )))/100)); -// } -// } -// else -// { -// if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPS_OT ) ) -// { -// ubAutoPenaltySinceLastTracer /= 2 * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPS_OT ); -// } -// } -// ////////////////////////////////////////////////////////////////// -// // Add penalty to bonus. -// //iBonus -= iAutoPenaltySinceLastTracer; - - /////////////////////////////////////////////////// - // HEADROCK HAM 3.5: Limit maximum bonus by range. -// CHRISL: We want to limit the maximum bonus "per round" by range. The way we have been doing it, it's max bonus by range, regardless of the -// number of rounds fired. This means firing 1 tracer or 20 makes no difference. And we can run this calculation directly again -// sTotalAutofirePenalty. - - if(sTotalAutofirePenalty > 0){ - INT16 sChanceLimit = min(gGameExternalOptions.ubCTHBumpPerTracer, ((iRange-(gGameExternalOptions.ubMinRangeTracerEffect*CELL_X_SIZE)) / CELL_X_SIZE) * gGameExternalOptions.ubRangeDifficultyAimingWithTracers); - sChanceLimit = max(0, sChanceLimit); - iBonus = sChanceLimit * iTracersFired; - iBonus = min(iBonus, sTotalAutofirePenalty); - } - - //INT16 sChanceLimit = __min(0, sTotalAutofirePenalty+(((iRange-100) / CELL_X_SIZE) * gGameExternalOptions.ubRangeDifficultyAimingWithTracers)); - - //INT16 sBaseChance = iChance + sTotalAutofirePenalty; - - // We don't want to enforce a limit unless the tracers have actually put us over the original CtH. -// if (sBaseChance <= iChance+iBonus) // Base_Chance without AutoPen, less or equal to Current_Chance plus tracer bumps -// { -// // store lowest: Chance+Tracer bumps, or Range-enforced limit -// INT16 sChanceLimit = __min(iChance+iBonus, sBaseChance+(((iRange-100) / CELL_X_SIZE) * gGameExternalOptions.ubRangeDifficultyAimingWithTracers)); -// // store highest: Chance Delta or base CtH -// //sChanceDelta = __max(sChanceDelta, sBaseChance); -// -// // iBonus is the distance between the enforced limit (if any) and the current chance with all penalties so far. -// // But it can't be negative, 'cause it's a bonus. -// iBonus = __max(0,sChanceLimit - iChance); -// // Add autopenalty since last tracer -// if (iBulletsSinceLastTracer < iBulletsPerTracer) -// { -// iBonus -= ubAutoPenaltySinceLastTracer; -// } -// -// } - iChance += iBonus; - } - } - ///////////////////////////////////////////////////////////////////////////////////////// - - // adjust for roof/not on roof - if ( pSoldier->pathing.bLevel == 0 ) - { - if ( pSoldier->bTargetLevel > 0 ) - { - // penalty for firing up - iChance -= AIM_PENALTY_FIRING_UP; - } - } - else // pSoldier->pathing.bLevel > 0 ) - { - if ( pSoldier->bTargetLevel == 0 ) - { - iChance += AIM_BONUS_FIRING_DOWN; - } - } - - - pTarget = SimpleFindSoldier( sGridNo, pSoldier->bTargetLevel ); - //CHRISL: A target's stance should have no impact on an aimed, headshot. The head doesn't get any smaller just because the target is crouching down. - if (pTarget != NULL && ubAimPos != AIM_SHOT_HEAD) - { - // targeting a merc - // adjust for crouched/prone target - switch( gAnimControl[ pTarget->usAnimState ].ubHeight ) - { - case ANIM_CROUCH: - if ( TANK( pSoldier ) && iRange < MIN_TANK_RANGE ) - { - // 13% penalty per tile closer than min range - iChance -= 13 * ( ( MIN_TANK_RANGE - iRange ) / CELL_X_SIZE ); - } - else - { - // at anything other than point-blank range - if (iRange > POINT_BLANK_RANGE + 10 * (AIM_PENALTY_TARGET_CROUCHED / 3) ) - { - iChance -= AIM_PENALTY_TARGET_CROUCHED; - } - else if (iRange > POINT_BLANK_RANGE) - { - // at close range give same bonus as prone, up to maximum of AIM_PENALTY_TARGET_CROUCHED - iChance -= 3 * ((iRange - POINT_BLANK_RANGE) / CELL_X_SIZE); // penalty -3%/tile - } - } - break; - case ANIM_PRONE: - if ( TANK( pSoldier ) && iRange < MIN_TANK_RANGE ) - { - // 25% penalty per tile closer than min range - iChance -= 25 * ( ( MIN_TANK_RANGE - iRange ) / CELL_X_SIZE ); - } - else - { - // at anything other than point-blank range - if (iRange > POINT_BLANK_RANGE) - { - // reduce chance to hit with distance to the prone/immersed target - iPenalty = 3 * ((iRange - POINT_BLANK_RANGE) / CELL_X_SIZE); // penalty -3%/tile - iPenalty = __min( iPenalty, AIM_PENALTY_TARGET_PRONE ); - - iChance -= iPenalty; - } - } - break; - case ANIM_STAND: - // if we are prone and at close range, then penalize shots to the torso or head! - if ( iRange <= MIN_PRONE_RANGE && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE ) - { - if ( ubAimPos == AIM_SHOT_RANDOM || ubAimPos == AIM_SHOT_GLAND ) - { - ubAdjAimPos = AIM_SHOT_TORSO; - } - else - { - ubAdjAimPos = ubAimPos; - } - // lose 10% per height difference, lessened by distance - // e.g. 30% to aim at head at range 1, only 10% at range 3 - // or 20% to aim at torso at range 1, no penalty at range 3 - // NB torso aim position is 2, so (5-aimpos) is 3, for legs it's 2, for head 4 - iChance -= (5 - ubAdjAimPos - iRange / CELL_X_SIZE) * 10; - } - break; - default: - break; - } - - // penalty for amount that enemy has moved - // HEADROCK HAM B2.6: Externalized the value - iPenalty = __min( (UINT16)((float)pTarget->bTilesMoved * (float)gGameExternalOptions.iMovementEffectOnAiming), 30 ); - /////////////////////////////////////////////////////////////////////////////////// - // SANDRO - fearless characters do not even take their head down no matter what - if ( gGameOptions.fNewTraitSystem && pTarget->ubProfile != NO_PROFILE ) - { - if ( gMercProfiles[ pTarget->ubProfile ].bCharacterTrait == CHAR_TRAIT_DAUNTLESS ) - iPenalty -= (iPenalty * 2 / 3); // two thirds only - } - /////////////////////////////////////////////////////////////////////////////////// - iChance -= iPenalty; - - // if target sees us, he may have a chance to dodge before the gun goes off - // but ability to dodge is reduced if crouched or prone! - if (pTarget->aiData.bOppList[pSoldier->ubID] == SEEN_CURRENTLY && !TANK( pTarget ) && !(pSoldier->ubBodyType != QUEENMONSTER) ) - { - iPenalty = ( EffectiveAgility( pTarget ) / 5 + EffectiveExpLevel( pTarget ) * 2); - switch( gAnimControl[ pTarget->usAnimState ].ubHeight ) - { - case ANIM_CROUCH: - iPenalty = iPenalty * 2 / 3; - break; - case ANIM_PRONE: - iPenalty /= 3; - break; - } - - // reduce dodge ability by the attacker's stats - iBonus = ( EffectiveDexterity( pSoldier ) / 5 + EffectiveExpLevel( pSoldier ) * 2); - if ( TANK( pTarget ) || (pSoldier->ubBodyType != QUEENMONSTER) ) - { - // reduce ability to track shots - iBonus = iBonus / 2; - } - - if ( iPenalty > iBonus ) - { - iChance -= (iPenalty - iBonus); - } - } - } - else if ( TANK( pSoldier ) && iRange < MIN_TANK_RANGE ) - { - // 25% penalty per tile closer than min range - iChance -= 25 * ( ( MIN_TANK_RANGE - iRange ) / CELL_X_SIZE ); - } - - - // add camo effects - -#if 0 - if ((victim = WhoIsThere(sGridNo)) < TOTAL_SOLDIERS) - { - - // if victim is 5 or more tiles away and camouflaged, reduce - // chance to hit by 5% (ALREADY HAVE THIS INFO) - if (range > 75 && vicpSoldier->camouflage) - { - switch(vicpSoldier->terrtype) - { - case GROUNDTYPE: - case SANDTYPE : - case GRASSTYPE : - case TGRASSTYPE: - case DGRASSTYPE: - case ROUGHTYPE : iChance += CAMOUFLAGE_TO_HIT_PENALTY; - break; - - case FLOORTYPE : - case LAKETYPE : - case OCEANTYPE : break; - -#ifdef BETAVERSION - default : NumMessage("CHANCE TO HIT ERROR: Unknown camo terrtype ",vicpSoldier->terrtype); -#endif - } - } - } -#endif - - // IF CHANCE EXISTS, BUT SHOOTER IS INJURED - if ((iChance > 0) && (pSoldier->stats.bLife < pSoldier->stats.bLifeMax)) - { - // if bandaged, give 1/2 of the bandaged life points back into equation - bBandaged = pSoldier->stats.bLifeMax - pSoldier->stats.bLife - pSoldier->bBleeding; - - // injury penalty is based on % damage taken (max 2/3rds chance) - iPenalty = (iChance * 2 * (pSoldier->stats.bLifeMax - pSoldier->stats.bLife + (bBandaged / 2))) / - (3 * pSoldier->stats.bLifeMax); - - // reduce injury penalty due to merc's experience level (he can take it!) - iChance -= (iPenalty * (100 - (10 * ( EffectiveExpLevel( pSoldier ) - 1)))) / 100; - } - - // IF CHANCE EXISTS, BUT SHOOTER IS LOW ON BREATH - if ((iChance > 0) && (pSoldier->bBreath < 100)) - { - // breath penalty is based on % breath missing (max 1/2 chance) - iPenalty = (iChance * (100 - pSoldier->bBreath)) / 200; - // reduce breath penalty due to merc's dexterity (he can compensate!) - iChance -= (iPenalty * (100 - ( EffectiveDexterity( pSoldier ) - 10))) / 100; - } - - - // CHECK IF TARGET IS WITHIN GUN'S EFFECTIVE MAXIMUM RANGE - if ( iRange > iMaxRange ) - { - // a bullet WILL travel that far if not blocked, but it's NOT accurate, - // because beyond maximum range, the bullet drops rapidly - - // This won't cause the bullet to be off to the left or right, only make it - // drop in flight. - //iChance /= 2; //ddd - iChance /= gGameExternalOptions.fOutOfGunRangeOrSight; - } - if ( iSightRange > (sDistVis * CELL_X_SIZE) ) - { - // penalize out of sight shots, cumulative to effective range penalty - iChance /= 2; - } - - //////////////////////////////////////////////////////////////////////////////////// - // SANDRO - Bonus CtH for Militia - if (pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA && gGameExternalOptions.sGreenMilitiaCtHBonusPercent != 0) - iChance += ((iChance * gGameExternalOptions.sGreenMilitiaCtHBonusPercent) /100); - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA && gGameExternalOptions.sRegularMilitiaCtHBonusPercent != 0) - iChance += ((iChance * gGameExternalOptions.sRegularMilitiaCtHBonusPercent) /100); - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaCtHBonusPercent != 0) - iChance += ((iChance * gGameExternalOptions.sVeteranMilitiaCtHBonusPercent) /100); - // bonus for enemy - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) - iChance += ((iChance * gGameExternalOptions.sEnemyAdminCtHBonusPercent) /100); - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY && gGameExternalOptions.sEnemyRegularCtHBonusPercent != 0) - iChance += ((iChance * gGameExternalOptions.sEnemyRegularCtHBonusPercent) /100); - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE && gGameExternalOptions.sEnemyEliteCtHBonusPercent != 0) - iChance += ((iChance * gGameExternalOptions.sEnemyEliteCtHBonusPercent) /100); - ////////////////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////////////////// - // SANDRO - option to make special NPCs stronger - chance to hit - if (gGameExternalOptions.usSpecialNPCStronger > 0) - { - switch( pSoldier->ubProfile ) - { - case CARMEN: - case QUEEN: - case JOE: - case ANNIE: - case CHRIS: - case KINGPIN: - case TIFFANY: - case T_REX: - case DRUGGIST: - case GENERAL: - case JIM: - case JACK: - case OLAF: - case RAY: - case OLGA: - case TYRONE: - case MIKE: - iChance += (iChance * gGameExternalOptions.usSpecialNPCStronger / 100); - break; - } - } - //////////////////////////////////////////////////////////////////////////////////// - - // MAKE SURE CHANCE TO HIT IS WITHIN DEFINED LIMITS - // HEADROCK: (HAM) Altered so called "Defined Limits" to accept external arguments. - // The divisor argument only works when the minimum is set to 0. It has a chance of 1 in X to - // bump the minimum back to 1, where X = the Divisor value. So a divisor value of 50 gives a 1/50 - // chance of getting some actual chance to hit despite the 0 minimum. The overall total would then - // be an effective CTH of only 1/5000 (50 chances to get a 1 out of 100 CTH, hehehe) - if (iChance <= gGameExternalOptions.ubMinimumCTH) - { - if ( TANK( pSoldier ) ) - { - // allow absolute minimums - iChance = 0; - } - else - { - iChance = gGameExternalOptions.ubMinimumCTH; - if ( gGameExternalOptions.ubMinimumCTH == 0 ) - { - if ( PreRandom( gGameExternalOptions.usMinimumCTHDivisor ) == (gGameExternalOptions.usMinimumCTHDivisor - 1) ) - { - iChance = 1; - } - } - } - } - else - { - // HEADROCK (HAM): Externalized maximum to JA2_OPTIONS.INI - // if (iChance > MAXCHANCETOHIT) - // iChance = MAXCHANCETOHIT; - if (iChance > gGameExternalOptions.ubMaximumCTH) - iChance = gGameExternalOptions.ubMaximumCTH; - } - -// NumMessage("ChanceToHit = ",chance); - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun: ichance = %d",iChance)); - return (iChance);*/ } UINT32 AICalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, UINT8 ubAimPos, INT8 bTargetLevel, UINT16 usAnimState)//dnl ch59 180813 @@ -12197,7 +10925,7 @@ FLOAT CalcNewChanceToHitAimTargetBonus(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTarg if (iRange > 0 && iHeightDifference > 0) { FLOAT fTempPenalty = gGameCTHConstants.AIM_SHOOTING_UPWARDS * iHeightDifference; - fTempPenalty /= iRange; + fTempPenalty = fTempPenalty * CELL_X_SIZE / iRange; fAimModifier += fTempPenalty; }