mirror of
https://github.com/1dot13/source.git
synced 2026-07-29 13:52:17 +02:00
NCTH CalcNewChanceToHitGun refactoring / cleanup (by silversurfer)
o Externalized ja2_options.ini value if you want to use / or not the cleaned up version of CalcNewChanceToHitGun: USE_NEW_CTH_CALCULATION o The cleanup only effects the game if you play with NCTH system o The refactoring makes the code much easier to read and also shrinks the locs o Fixed some old bugs in calculation o In the future the switch USE_NEW_CTH_CALCULATION can be removed completely o more infos: http://www.ja-galaxy-forum.com/board/ubbthreads.php/topics/322986/Re_Potential_NCTH_Code_Change_.html#Post322986 git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@6235 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
@@ -693,6 +693,10 @@ void LoadGameExternalOptions()
|
||||
gGameExternalOptions.guiMaxWeaponSize = iniReader.ReadInteger("System Limit Settings","MAX_WEAPON_SIZE", 9, 0, 32000);
|
||||
gGameExternalOptions.guiOIVSizeNumber = iniReader.ReadInteger("System Limit Settings","OLD_INVENTORY_ITEM_NUMBER", 99, 0, 65535);
|
||||
|
||||
// silversurfer: this is only for the cleaned up NCTH calculation. Please remove if the new functions are accepted
|
||||
// see Tactical\Weapons.cpp funtion CalcNewChanceToHitGun()
|
||||
gGameExternalOptions.fUseNewCTHCalculation = iniReader.ReadBoolean("System Limit Settings", "USE_NEW_CTH_CALCULATION", TRUE);
|
||||
|
||||
//################# Data File Settings #################
|
||||
|
||||
// WANNE: Always use prof.dat??
|
||||
|
||||
@@ -512,6 +512,10 @@ typedef struct
|
||||
UINT16 guiMaxItemSize;
|
||||
UINT16 guiOIVSizeNumber;
|
||||
|
||||
// silversurfer: this is only for the cleaned up NCTH calculation. Please remove if the new functions are accepted
|
||||
// see Tactical\Weapons.cpp funtion CalcNewChanceToHitGun()
|
||||
BOOLEAN fUseNewCTHCalculation;
|
||||
|
||||
//Sight range
|
||||
UINT32 ubStraightSightRange;
|
||||
UINT32 ubVisDistDecreasePerRainIntensity;
|
||||
|
||||
@@ -12801,6 +12801,11 @@ FLOAT GetScopeRangeMultiplier( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, FLOAT d2
|
||||
FLOAT rangeModifier = gGameCTHConstants.SCOPE_RANGE_MULTIPLIER;
|
||||
|
||||
iScopeFactor = GetScopeMagnificationFactor( pSoldier, pObj, d2DDistance );
|
||||
|
||||
// if we are not using a scope we will not apply "SCOPE_RANGE_MULTIPLIER"
|
||||
if ( iScopeFactor <= 1.0 )
|
||||
rangeModifier = 1.0;
|
||||
|
||||
if( gGameOptions.fNewTraitSystem )
|
||||
{
|
||||
if(iScopeFactor > 5.0f)
|
||||
|
||||
+940
-8
@@ -103,6 +103,19 @@ BOOLEAN UseBlade( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo );
|
||||
BOOLEAN UseThrown( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo );
|
||||
BOOLEAN UseLauncher( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo );
|
||||
|
||||
// new subroutines for NCTH calculation
|
||||
FLOAT CalcNewChanceToHitBaseAttributeBonus(SOLDIERTYPE *pSoldier);
|
||||
FLOAT CalcNewChanceToHitBaseEffectBonus(SOLDIERTYPE *pSoldier);
|
||||
FLOAT CalcNewChanceToHitBaseWeaponBonus(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, FLOAT fGunBaseDifficulty, UINT8 stance);
|
||||
FLOAT CalcNewChanceToHitBaseSpecialBonus(SOLDIERTYPE *pSoldier);
|
||||
FLOAT CalcNewChanceToHitBaseTargetBonus(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTarget, INT32 sGridNo, INT32 iRange, UINT8 ubAimPos, BOOLEAN fCantSeeTarget);
|
||||
FLOAT CalcNewChanceToHitAimAttributeBonus(SOLDIERTYPE *pSoldier);
|
||||
FLOAT CalcNewChanceToHitAimEffectBonus(SOLDIERTYPE *pSoldier);
|
||||
FLOAT CalcNewChanceToHitAimWeaponBonus(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, FLOAT fGunAimDifficulty, UINT8 stance);
|
||||
FLOAT CalcNewChanceToHitAimSpecialBonus(SOLDIERTYPE *pSoldier);
|
||||
FLOAT CalcNewChanceToHitAimTargetBonus(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTarget, INT32 sGridNo, INT32 iRange, UINT8 ubAimPos, BOOLEAN fCantSeeTarget);
|
||||
FLOAT CalcNewChanceToHitAimTraitBonus(SOLDIERTYPE *pSoldier, FLOAT fDifference, INT32 sGridNo, INT16 ubAimTime, FLOAT fScopeMagFactor, UINT32 uiBestScopeRange);
|
||||
|
||||
INT32 HTHImpact( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pTarget, INT32 iHitBy, BOOLEAN fBladeAttack );
|
||||
|
||||
BOOLEAN gfNextShotKills = FALSE;
|
||||
@@ -4814,6 +4827,12 @@ BOOLEAN InRange( SOLDIERTYPE *pSoldier, INT32 sGridNo )
|
||||
|
||||
UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, UINT8 ubAimPos )
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// START COMMON PART OF CLEANED UP AND OLD VERSION OF NCTH CALCULATION
|
||||
// When the old code is removed some of the old variables can be removed too
|
||||
// because then they are not used anymore.
|
||||
// iChance, bBandaged, iTraitModifier
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// HEADROCK HAM 4:
|
||||
@@ -4834,8 +4853,8 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi
|
||||
INT8 bBandaged, iTraitModifier;
|
||||
INT16 sDistVis, sDistVisNoScope;
|
||||
UINT8 ubTargetID;
|
||||
bool highPowerScope = false;
|
||||
INT16 sTotalAutofirePenalty = 0;
|
||||
// bool highPowerScope = false;
|
||||
// INT16 sTotalAutofirePenalty = 0;
|
||||
bool fCantSeeTarget = false;
|
||||
FLOAT scopeRangeMod;
|
||||
|
||||
@@ -4859,12 +4878,6 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi
|
||||
ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel ); // Target ubID
|
||||
pTarget = SimpleFindSoldier( sGridNo, pSoldier->bTargetLevel ); // Target Pointer
|
||||
|
||||
// Calculate how easy it is to handle this gun.
|
||||
FLOAT iGunDifficulty = (FLOAT)(( Weapon[ usInHand ].ubHandling * (100 - GetPercentReadyTimeAPReduction(pInHand) )) / 100);
|
||||
iGunDifficulty *= (FLOAT)(100 / APBPConstants[AP_MAXIMUM]); // Adjust for 100AP/25AP
|
||||
FLOAT iGunBaseDifficulty = iGunDifficulty;
|
||||
FLOAT iGunAimDifficulty = iGunDifficulty;
|
||||
|
||||
UINT8 stance = gAnimControl[ pSoldier->usAnimState ].ubEndHeight;
|
||||
|
||||
// Flugente: new feature: if the next tile in our sight direction has a height so that we could rest our weapon on it, we do that, thereby gaining the prone boni instead. This includes bipods
|
||||
@@ -4927,6 +4940,249 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi
|
||||
iSightRange = __max((INT16)(sDistVis*1.5), iRange*2);
|
||||
if(iSightRange > sDistVis)
|
||||
fCantSeeTarget = true;
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// END COMMON PART OF CLEANED UP AND OLD VERSION OF NCTH CALCULATION
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// START CLEANED UP VERSION OF NCTH CALCULATION
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// silversurfer: This is a cleaned up version of the NCTH calculation and hopefully easier to read.
|
||||
// The if clause and the rest of the old code below its else can be removed if the new version is accepted.
|
||||
// The "if (gGameExternalOptions.fUseNewCTHCalculation)" is only there for easy switching in Ja2_Options.ini.
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
if (gGameExternalOptions.fUseNewCTHCalculation)
|
||||
{
|
||||
FLOAT fBaseChance = 0, fBaseModifier = 0, fAimChance = 0, fAimModifier = 0;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// First step: Calculate a base CTH value
|
||||
//
|
||||
// This "Base" CTH is a percentage value describing how well our gun's muzzle is aimed
|
||||
// when not spending any extra effort on aiming. In other words, it's the most basic state,
|
||||
// where the shooter only brings up the gun into the general direction of the target, and
|
||||
// pulls the trigger.
|
||||
//
|
||||
// This is calculated first by factoring in skills, and later by adding up various conditional
|
||||
// factors into a percentage which is then applied to those skills.
|
||||
//
|
||||
// First calculate the basic value for BASE CTH by factoring in all the character's skills.
|
||||
// The most important skill here is EXPERIENCE.
|
||||
fBaseChance = CalcNewChanceToHitBaseAttributeBonus(pSoldier);
|
||||
|
||||
// this shooter will never hit anything
|
||||
if ( fBaseChance <= gGameExternalOptions.ubMinimumCTH )
|
||||
return gGameExternalOptions.ubMinimumCTH;
|
||||
|
||||
// Add a flat Base bonus from the item and its attachments.
|
||||
INT32 imoda = GetFlatBaseModifier( pInHand, ANIM_PRONE );
|
||||
INT32 imodb = GetFlatBaseModifier( pInHand, stance );
|
||||
fBaseChance += (FLOAT)((gGameExternalOptions.ubProneModifierPercentage * imoda + (100 - gGameExternalOptions.ubProneModifierPercentage) * imodb)/100);
|
||||
|
||||
// get bonus from effects lasting on the shooter (morale, injury, shock etc.)
|
||||
fBaseModifier += CalcNewChanceToHitBaseEffectBonus(pSoldier);
|
||||
|
||||
// Calculate how easy it is to handle the weapon
|
||||
FLOAT fGunDifficulty = (FLOAT)(( Weapon[ usInHand ].ubHandling * (100 - GetPercentReadyTimeAPReduction(pInHand) )) / 100);
|
||||
fGunDifficulty *= (FLOAT)(100 / APBPConstants[AP_MAXIMUM]); // Adjust for 100AP/25AP
|
||||
FLOAT fGunBaseDifficulty = fGunDifficulty;
|
||||
FLOAT fGunAimDifficulty = fGunDifficulty;
|
||||
|
||||
// get bonus from weapon handling
|
||||
fBaseModifier += CalcNewChanceToHitBaseWeaponBonus(pSoldier, sGridNo, ubAimTime, fGunBaseDifficulty, stance);
|
||||
|
||||
// get special bonus (enemy/militia bonus, special characters, game difficulty bonus etc.)
|
||||
fBaseModifier += CalcNewChanceToHitBaseSpecialBonus(pSoldier);
|
||||
|
||||
// get target specific bonus
|
||||
fBaseModifier += CalcNewChanceToHitBaseTargetBonus(pSoldier, pTarget, sGridNo, iRange, ubAimPos, fCantSeeTarget);
|
||||
|
||||
//CHRISL: Applying the Gear AimBonus (penalty) here, and directly to fBaseModifier as a flat penalty, instead of altering iSightRange above. For now
|
||||
// I'm just applying this to the BaseModifier which means that aiming can overcome the Gear AimBonus (penalty).
|
||||
fBaseModifier += GetGearAimBonus ( pSoldier, iSightRange, ubAimTime );
|
||||
|
||||
////////////////////////////////////
|
||||
// Finish BASE CTH calculation:
|
||||
fBaseChance = (fBaseChance * (100+fBaseModifier) / 100);
|
||||
fBaseChance = __max( fBaseChance, 0 );
|
||||
fBaseChance = __min( fBaseChance, 100 );
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Second step: Calculate bonuses from aiming
|
||||
//
|
||||
// In this step, we figure out how much CTH we get from aiming. Naturally, we
|
||||
// need extra aiming (bAimTime) to be applied.
|
||||
//
|
||||
// CTH is capped by a value determined by the shooter's skills. The better
|
||||
// your skills, the higher CTH can go. A steady hand (DEX) and training
|
||||
// (MRK) are paramount here.
|
||||
// After that, the cap is raised or lowered by other factors, like fatigue
|
||||
// or the size of the gun.
|
||||
//
|
||||
// Finally, we determine how much each Aim Click will give us. Returns are
|
||||
// diminishing, so the more clicks we put in, the less CTH for each additional
|
||||
// click. However, the last few points can be absolutely crucial for long-range
|
||||
// shooting.
|
||||
//
|
||||
// Scope magnification can be important here, but only if the target is closer
|
||||
// than the scope's minimum aiming range. If it is closer, the shooter begins
|
||||
// to suffer aiming penalties which can render aiming almost useless.
|
||||
|
||||
if (ubAimTime > 0)
|
||||
{
|
||||
// Are we using a scope? If so, what's the range factor?
|
||||
FLOAT fScopeMagFactor, fRangeModifier;
|
||||
//CHRISL: This does make sense but it effectively makes high powered scopes worthless if a target is actually visible. As an example, a Battle Scope
|
||||
// is going to have a iScopeMagFactor of 7. With a "NORMAL_SHOOTING_DISTANCE" also of 7, we're going to end up with uiBestScopeRange of 49. That's
|
||||
// effectilvey saying that any target within 490m is "too close" for the scope to be effective. That by itself isn't realistic. But in JA2 it's also
|
||||
// a big drawback. Consider that normally (excluding terrain, lighting and environment) you can see 26 tiles. The Battle Scope increases that by 50%
|
||||
// in bright light, meaning you can see up to 39 tiles away. A target standing 49 tiles is considered "invisible" to you because the target is beyond
|
||||
// your 39 tile vision range. Sniper scopes (10 iScopeMagFactor) are even worse (uiBestScopeRange = 70 but visible range limited to 49 tiles). So the
|
||||
// only way to get the full benefit of a higher powered scope is to have a spotter AND to suffer the various "TARGET_INVISIBLE" penalties. This makes
|
||||
// no sense. Admittedly, higher powered scopes should be less efficienct at "close range" but is 490m (700m for sniper scope) really "close range"?
|
||||
// Also, we can't simply change NORMAL_SHOOTING_DISTANCE because this value also effects shooting without a scope. We don't want ot make it harder for
|
||||
// "scopeless" shooting. So to try and counter some of this, while still showing that scopes are less effecient at "close range", I'm going to cut the
|
||||
// uiBestScopeRange value in half. This should allow a Battle Scope to reach full effeciency at 24 tiles and a Sniper scope will be fully effecient at
|
||||
// 35 tiles. ACOG becomes fully effecient at 14 tiles and 2x is fully effeciency at 7 tiles (compared to 28 and 14 respectively). This does mean that a
|
||||
// 2x scope reaches full effeciency at the same point as "scopeless" shooting, but I don't think this will be a serious problem.
|
||||
|
||||
if ( !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) // ignore scopes when firing from hip/fast shot from pistol
|
||||
{
|
||||
fScopeMagFactor = GetBestScopeMagnificationFactor( pSoldier, &(pSoldier->inv[pSoldier->ubAttackingHand]), d2DDistance );
|
||||
fRangeModifier = GetScopeRangeMultiplier(pSoldier, &(pSoldier->inv[pSoldier->ubAttackingHand]), (FLOAT)iRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
fScopeMagFactor = 1.0f;
|
||||
// we are shooting from hip so SCOPE_RANGE_MULTIPLIER cannot be applied
|
||||
fRangeModifier = 1.0f;
|
||||
}
|
||||
|
||||
UINT32 uiBestScopeRange = (UINT32)(fScopeMagFactor * gGameCTHConstants.NORMAL_SHOOTING_DISTANCE * fRangeModifier);
|
||||
|
||||
// get attribute based aiming value.
|
||||
// This is also the CTH cap, the absolute maximum CTH a shooter can get.
|
||||
fAimChance = CalcNewChanceToHitAimAttributeBonus(pSoldier);
|
||||
|
||||
// Add percent-based modifier from the gun and its attachments
|
||||
FLOAT moda = (FLOAT)(fAimChance * GetPercentCapModifier( pInHand, ANIM_PRONE )) / 100;
|
||||
FLOAT modb = (FLOAT)(fAimChance * GetPercentCapModifier( pInHand, stance )) / 100;
|
||||
fAimChance += (FLOAT)((gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb)/100);
|
||||
|
||||
// get direct AimChance bonus for traits (throwing, sniper etc.)
|
||||
FLOAT fDifference = 99 - fAimChance;
|
||||
fAimChance += CalcNewChanceToHitAimTraitBonus(pSoldier, fDifference, sGridNo, ubAimTime, fScopeMagFactor, uiBestScopeRange);
|
||||
|
||||
// get aimbonus from effects lasting on the shooter
|
||||
fAimModifier += CalcNewChanceToHitAimEffectBonus(pSoldier);
|
||||
|
||||
// get aimbonus from weapon handling
|
||||
fAimModifier += CalcNewChanceToHitAimWeaponBonus(pSoldier, sGridNo, ubAimTime, fGunAimDifficulty, stance);
|
||||
|
||||
// get special aimbonus (game difficulty etc.)
|
||||
fAimModifier += CalcNewChanceToHitAimSpecialBonus(pSoldier);
|
||||
|
||||
// apply bonus from traits
|
||||
// Flugente: moved trait modifiers into a member function
|
||||
if(gGameOptions.fNewTraitSystem)
|
||||
{
|
||||
UINT8 targetprofile = NOBODY;
|
||||
if ( pTarget && pTarget->ubProfile != NOBODY )
|
||||
targetprofile = pTarget->ubProfile;
|
||||
|
||||
fAimModifier += pSoldier->GetTraitCTHModifier( usInHand, ubAimTime, targetprofile );
|
||||
}
|
||||
|
||||
// get aimbonus from target
|
||||
fAimModifier += CalcNewChanceToHitAimTargetBonus(pSoldier, pTarget, sGridNo, iRange, ubAimPos, fCantSeeTarget);
|
||||
|
||||
// silversurfer: this doesn't make sense. We always apply a penalty when we can see the target?
|
||||
// invisible targets are already taken into account one step above in aimbonus from target
|
||||
// VISIBILITY
|
||||
/* if (iRange > 0 && iSightRange > iRange)
|
||||
{
|
||||
FLOAT fTempPenalty = (FLOAT)((FLOAT)iSightRange / (FLOAT)iRange);
|
||||
fTempPenalty = (FLOAT)(100 / fTempPenalty);
|
||||
fAimModifier += ((100-fTempPenalty) * gGameCTHConstants.AIM_VISIBILITY)/100;
|
||||
fAimModifier = __max( gGameCTHConstants.AIM_TARGET_INVISIBLE, fAimModifier );
|
||||
}*/
|
||||
|
||||
// factor in scopes under their range
|
||||
if ( !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) )
|
||||
{
|
||||
if (fScopeMagFactor > 1.0 && iRange < (INT32)uiBestScopeRange)
|
||||
{
|
||||
// Calculate how much penalty this scope should give at this range
|
||||
FLOAT dScopePenaltyRatio = ((FLOAT)uiBestScopeRange / (FLOAT)iRange);
|
||||
INT32 iScopePenalty = (INT32)(dScopePenaltyRatio * gGameCTHConstants.AIM_TOO_CLOSE_SCOPE * (fScopeMagFactor /2));
|
||||
fAimModifier += iScopePenalty;
|
||||
}
|
||||
else if (fScopeMagFactor == 1.0f && GetHighestScopeMagnificationFactor( &(pSoldier->inv[pSoldier->ubAttackingHand]) ) > 1.0f )
|
||||
{
|
||||
// Not using a scope, but it's still there. Give half the penalty based on the size of the scope.
|
||||
INT32 iScopePenalty = (INT32)(((GetHighestScopeMagnificationFactor( &(pSoldier->inv[pSoldier->ubAttackingHand]) )/2) * gGameCTHConstants.AIM_TOO_CLOSE_SCOPE)/2);
|
||||
fAimModifier += iScopePenalty;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure cap is within limits
|
||||
fAimChance = __max(fAimChance, __max(0,(UINT32)fBaseChance));
|
||||
fAimChance = __min(fAimChance, gGameExternalOptions.ubMaximumCTH);
|
||||
|
||||
// Now figure out the distance between the Base CTH and the CTH Cap. This is the distance we'll potentially
|
||||
// cover when applying the maximum number of aiming clicks for this gun.
|
||||
INT32 iMaxAimBonus = (INT32)(fAimChance - fBaseChance);
|
||||
|
||||
iMaxAimBonus = (INT32)((iMaxAimBonus * (100+fAimModifier)) / 100);
|
||||
iMaxAimBonus = __max(0, iMaxAimBonus); // can't get less than 0 points for aiming...
|
||||
|
||||
// Now let's find out how many CTH points we get per aiming level.
|
||||
UINT8 ubAllowedAimingLevels = AllowedAimingLevels( pSoldier, sGridNo );
|
||||
if(ubAimTime == ubAllowedAimingLevels)
|
||||
gCTHDisplay.fMaxAimReached = TRUE;
|
||||
FLOAT dAimFractionsDivisor = 0.0;
|
||||
for (UINT8 cnt = 0; cnt < ubAllowedAimingLevels; cnt++)
|
||||
{
|
||||
dAimFractionsDivisor += (cnt+1);
|
||||
}
|
||||
|
||||
FLOAT iAimPoints = 0;
|
||||
FLOAT iAimPointFraction = (FLOAT)((FLOAT)iMaxAimBonus / dAimFractionsDivisor);
|
||||
for (UINT8 x = 0; x < ubAimTime; x++)
|
||||
{
|
||||
iAimPoints += iAimPointFraction * (ubAllowedAimingLevels-x);
|
||||
// Add Flat Modifier from the weapon and its attachments
|
||||
INT32 moda = GetFlatAimModifier( pInHand, ANIM_PRONE );
|
||||
INT32 modb = GetFlatAimModifier( pInHand, stance );
|
||||
iAimPoints += (gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb)/100;
|
||||
}
|
||||
|
||||
// Finally, add the appropriate number of CTH points to our chance-to-hit, and limit it into good values.
|
||||
fBaseChance = __max(fBaseChance + (INT32)iAimPoints, fBaseChance);
|
||||
fBaseChance = __min(fBaseChance, (INT32)fAimChance);
|
||||
}
|
||||
|
||||
// Impose global limits.
|
||||
fBaseChance = __min(fBaseChance, gGameExternalOptions.ubMaximumCTH);
|
||||
fBaseChance = __max(fBaseChance, gGameExternalOptions.ubMinimumCTH);
|
||||
|
||||
|
||||
return ((INT32)fBaseChance);
|
||||
}
|
||||
else
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// END CLEANED UP VERSION OF NCTH CALCULATION
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// START OLD VERSION OF NCTH CALCULATION
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Calculate how easy it is to handle this gun.
|
||||
FLOAT iGunDifficulty = (FLOAT)(( Weapon[ usInHand ].ubHandling * (100 - GetPercentReadyTimeAPReduction(pInHand) )) / 100);
|
||||
iGunDifficulty *= (FLOAT)(100 / APBPConstants[AP_MAXIMUM]); // Adjust for 100AP/25AP
|
||||
FLOAT iGunBaseDifficulty = iGunDifficulty;
|
||||
FLOAT iGunAimDifficulty = iGunDifficulty;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// First step: Calculate a base CTH value
|
||||
@@ -5739,6 +5995,10 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi
|
||||
|
||||
return (iChance);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// END OLD VERSION OF NCTH CALCULATION
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -11566,3 +11826,675 @@ FLOAT GetOverheatDamageThreshold( OBJECTTYPE *pObj )
|
||||
|
||||
return damagethreshold;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitBaseAttributeBonus(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
INT8 bExperience = EffectiveExpLevel( pSoldier );
|
||||
INT8 bMarksmanship = EffectiveMarksmanship( pSoldier );
|
||||
INT16 bDexterity = EffectiveDexterity( pSoldier, FALSE );
|
||||
INT16 bWisdom = EffectiveWisdom( pSoldier );
|
||||
|
||||
// this shooter will never hit anything
|
||||
if ( bMarksmanship == 0 || bDexterity == 0 )
|
||||
{
|
||||
return( gGameExternalOptions.ubMinimumCTH );
|
||||
}
|
||||
|
||||
FLOAT fCombinedSkill = (FLOAT)(gGameCTHConstants.BASE_EXP*(bExperience*10));
|
||||
fCombinedSkill += (FLOAT)(gGameCTHConstants.BASE_MARKS * bMarksmanship);
|
||||
fCombinedSkill += (FLOAT)(gGameCTHConstants.BASE_DEX * bDexterity);
|
||||
fCombinedSkill += (FLOAT)(gGameCTHConstants.BASE_WIS * bWisdom);
|
||||
|
||||
// Divide by the total coefficient value.
|
||||
fCombinedSkill /= (FLOAT)(gGameCTHConstants.BASE_EXP + gGameCTHConstants.BASE_MARKS + gGameCTHConstants.BASE_DEX + gGameCTHConstants.BASE_WIS);
|
||||
|
||||
// At this point we have a value of 1 to 100. Divide by 3.
|
||||
fCombinedSkill /= 3;
|
||||
|
||||
return fCombinedSkill;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitBaseEffectBonus(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
FLOAT fBaseModifier = 0;
|
||||
|
||||
// MORALE
|
||||
INT8 iCurrentMorale = GetMoraleModifier(pSoldier);
|
||||
if (iCurrentMorale > 0)
|
||||
{
|
||||
fBaseModifier += (FLOAT)(iCurrentMorale * gGameCTHConstants.BASE_HIGH_MORALE);
|
||||
}
|
||||
else if (iCurrentMorale < 0)
|
||||
{
|
||||
fBaseModifier += (FLOAT)(iCurrentMorale * gGameCTHConstants.BASE_LOW_MORALE);
|
||||
}
|
||||
|
||||
// INJURY
|
||||
if (pSoldier->stats.bLife < pSoldier->stats.bLifeMax)
|
||||
{
|
||||
FLOAT fTempPenalty = gGameCTHConstants.BASE_INJURY;
|
||||
|
||||
// Bleeding damage is used as a percentage off the Max Life, giving a penalty of up to BASE_INJURY.
|
||||
fBaseModifier += (FLOAT)(pSoldier->bBleeding * fTempPenalty) / pSoldier->stats.bLifeMax;
|
||||
|
||||
// Bandage damage is used similarly, but is only 1/3 as bad.
|
||||
INT8 bBandaged = pSoldier->stats.bLifeMax - pSoldier->stats.bLife - pSoldier->bBleeding;
|
||||
fBaseModifier += ((bBandaged * fTempPenalty) / pSoldier->stats.bLifeMax) / 3;
|
||||
}
|
||||
|
||||
// FATIGUE
|
||||
fBaseModifier += (gGameCTHConstants.BASE_FATIGUE * (100 - pSoldier->bBreath)) / 100;
|
||||
|
||||
// DRUNKNESS
|
||||
INT8 iDrunkness = GetDrunkLevel(pSoldier);
|
||||
if (iDrunkness > 0)
|
||||
{
|
||||
iDrunkness -= 1;
|
||||
fBaseModifier += gGameCTHConstants.BASE_DRUNK[iDrunkness];
|
||||
}
|
||||
|
||||
// GASSED
|
||||
if ( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED )
|
||||
{
|
||||
fBaseModifier += gGameCTHConstants.BASE_GASSED;
|
||||
}
|
||||
|
||||
// BEING BANDAGED
|
||||
if (pSoldier->ubServiceCount > 0)
|
||||
{
|
||||
fBaseModifier += gGameCTHConstants.BASE_BEING_BANDAGED;
|
||||
}
|
||||
|
||||
// SHOCK (SHOOTER)
|
||||
if (pSoldier->aiData.bShock)
|
||||
{
|
||||
FLOAT fTempPenalty = gGameCTHConstants.BASE_SHOCK;
|
||||
FLOAT fShockPercentage = (FLOAT)(pSoldier->aiData.bShock * 100) / gGameExternalOptions.ubMaxSuppressionShock;
|
||||
|
||||
fBaseModifier += (fTempPenalty * fShockPercentage) / 100;
|
||||
}
|
||||
|
||||
return fBaseModifier;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitBaseWeaponBonus(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, FLOAT fGunBaseDifficulty, UINT8 stance)
|
||||
{
|
||||
FLOAT fBaseModifier = 0;
|
||||
UINT16 usInHand = pSoldier->usAttackingWeapon;
|
||||
OBJECTTYPE * pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]);
|
||||
|
||||
// FIRING FROM ALTERNATIVE WEAPON HOLDING (faster shot, less accuracy)
|
||||
if ( pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) )
|
||||
{
|
||||
fBaseModifier -= gGameExternalOptions.ubAltWeaponHoldingCtHPenaly;
|
||||
}
|
||||
|
||||
// FIRING 1-HANDED WEAPONS
|
||||
if ( !(Item[ usInHand ].twohanded ) ) //JMich todo: underbarrel
|
||||
{
|
||||
if (pSoldier->inv[SECONDHANDPOS].exists() != false)
|
||||
{
|
||||
if ( pSoldier->IsValidSecondHandShot( ) )
|
||||
{
|
||||
// Penalty for shooting two pistols. Ambidextrous trait halves this.
|
||||
FLOAT fTempPenalty = (gGameCTHConstants.BASE_TWO_GUNS * fGunBaseDifficulty) - fGunBaseDifficulty;
|
||||
if (gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT ))
|
||||
{
|
||||
fTempPenalty = fTempPenalty * (100 - gSkillTraitValues.ubAMPenaltyDoubleReduction) / 100;
|
||||
}
|
||||
else if (!gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXT_OT ))
|
||||
{
|
||||
fTempPenalty = 0;
|
||||
}
|
||||
|
||||
fGunBaseDifficulty += fTempPenalty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Penalty for shooting a pistol with just one hand
|
||||
fGunBaseDifficulty *= gGameCTHConstants.BASE_ONE_HANDED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HEAVY WEAPON in OLD trait system
|
||||
if (Item[usInHand].rocketlauncher && !(gGameOptions.fNewTraitSystem) )
|
||||
{
|
||||
// Penalty for shooting heavy weapons (launchers). Heavy Weapons skill halves this, once per skill level.
|
||||
FLOAT fTempPenalty = (gGameCTHConstants.BASE_HEAVY_WEAPON * fGunBaseDifficulty) - fGunBaseDifficulty;
|
||||
|
||||
for (INT32 x=0; x < NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPS_OT ); x++)
|
||||
{
|
||||
fTempPenalty /= 2;
|
||||
}
|
||||
|
||||
fGunBaseDifficulty += fTempPenalty;
|
||||
}
|
||||
|
||||
// STANCE
|
||||
switch ( stance )
|
||||
{
|
||||
case ANIM_CROUCH:
|
||||
fGunBaseDifficulty *= gGameCTHConstants.BASE_CROUCHING_STANCE;
|
||||
break;
|
||||
|
||||
case ANIM_PRONE:
|
||||
fGunBaseDifficulty *= gGameCTHConstants.BASE_PRONE_STANCE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Percentage based handling modifier from the gun and its attachments
|
||||
FLOAT moda = (fGunBaseDifficulty * GetPercentHandlingModifier( pInHand, ANIM_PRONE )) / 100;
|
||||
FLOAT modb = (fGunBaseDifficulty * GetPercentHandlingModifier( pInHand, stance )) / 100;
|
||||
fGunBaseDifficulty += ((gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb)/100);
|
||||
|
||||
// Now apply Gun Difficulty to the Base Modifier.
|
||||
fBaseModifier -= fGunBaseDifficulty * gGameCTHConstants.BASE_DRAW_COST;
|
||||
|
||||
// Percentage based modifier from the weapon and its attachments
|
||||
moda = (fGunBaseDifficulty * GetPercentBaseModifier( pInHand, ANIM_PRONE )) / 100;
|
||||
modb = (fGunBaseDifficulty * GetPercentBaseModifier( pInHand, stance )) / 100;
|
||||
fBaseModifier += ((gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb)/100);
|
||||
|
||||
return fBaseModifier;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitBaseSpecialBonus(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
FLOAT fBaseModifier = 0;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// SANDRO - Bonus CtH for Militia
|
||||
if (pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA && gGameExternalOptions.sGreenMilitiaCtHBonusPercent != 0)
|
||||
fBaseModifier += gGameExternalOptions.sGreenMilitiaCtHBonusPercent;
|
||||
else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA && gGameExternalOptions.sRegularMilitiaCtHBonusPercent != 0)
|
||||
fBaseModifier += gGameExternalOptions.sRegularMilitiaCtHBonusPercent;
|
||||
else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaCtHBonusPercent != 0)
|
||||
fBaseModifier += gGameExternalOptions.sVeteranMilitiaCtHBonusPercent;
|
||||
// bonus for enemy
|
||||
else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0)
|
||||
fBaseModifier += gGameExternalOptions.sEnemyAdminCtHBonusPercent;
|
||||
else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY && gGameExternalOptions.sEnemyRegularCtHBonusPercent != 0)
|
||||
fBaseModifier += gGameExternalOptions.sEnemyRegularCtHBonusPercent;
|
||||
else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE && gGameExternalOptions.sEnemyEliteCtHBonusPercent != 0)
|
||||
fBaseModifier += gGameExternalOptions.sEnemyEliteCtHBonusPercent;
|
||||
|
||||
// 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:
|
||||
fBaseModifier += gGameExternalOptions.usSpecialNPCStronger;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// GAME DIFFICULTY
|
||||
if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_PC ) && (pSoldier->bSide != gbPlayerNum) )
|
||||
{
|
||||
fBaseModifier += gGameCTHConstants.BASE_DIFFICULTY[gGameOptions.ubDifficultyLevel];
|
||||
}
|
||||
|
||||
return fBaseModifier;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitBaseTargetBonus(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTarget, INT32 sGridNo, INT32 iRange, UINT8 ubAimPos, BOOLEAN fCantSeeTarget)
|
||||
{
|
||||
FLOAT fBaseModifier = 0;
|
||||
INT32 iHeightDifference = 0;
|
||||
|
||||
// SHOOTING AT SAME TARGET AGAIN
|
||||
if (sGridNo == pSoldier->sLastTarget )
|
||||
fBaseModifier += (FLOAT)gGameCTHConstants.BASE_SAME_TARGET;
|
||||
|
||||
// SHOOTING AT A TARGET AT DIFFERENT HEIGHT?
|
||||
if ( pTarget == NULL )
|
||||
{
|
||||
// Shooting to roof.
|
||||
if ( pSoldier->bTargetLevel > pSoldier->pathing.bLevel )
|
||||
{
|
||||
iHeightDifference = 3 * pSoldier->bTargetLevel;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// HEIGHT DIFFERENCE
|
||||
|
||||
UINT32 uiShooterHeight = 0;
|
||||
UINT32 uiTargetHeight = 0;
|
||||
|
||||
if ( pSoldier->pathing.bLevel > 0 )
|
||||
{
|
||||
uiShooterHeight += 3 * pSoldier->pathing.bLevel;
|
||||
}
|
||||
|
||||
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
||||
{
|
||||
case ANIM_STAND:
|
||||
uiShooterHeight += 2;
|
||||
break;
|
||||
case ANIM_CROUCH:
|
||||
uiShooterHeight += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pTarget->pathing.bLevel > 0)
|
||||
{
|
||||
uiTargetHeight += 3 * pTarget->pathing.bLevel;
|
||||
}
|
||||
|
||||
switch ( gAnimControl[ pTarget->usAnimState ].ubEndHeight )
|
||||
{
|
||||
case ANIM_STAND:
|
||||
switch (ubAimPos)
|
||||
{
|
||||
case AIM_SHOT_HEAD:
|
||||
uiTargetHeight += 2;
|
||||
break;
|
||||
case AIM_SHOT_TORSO:
|
||||
case AIM_SHOT_RANDOM:
|
||||
case AIM_SHOT_GLAND:
|
||||
uiTargetHeight += 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ANIM_CROUCH:
|
||||
switch (ubAimPos)
|
||||
{
|
||||
case AIM_SHOT_HEAD:
|
||||
uiTargetHeight += 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
iHeightDifference = uiShooterHeight - uiTargetHeight;
|
||||
if (iHeightDifference < 0)
|
||||
{
|
||||
iHeightDifference *= -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iHeightDifference = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Height difference is mitigated by range. A LONGER range reduces this penalty!
|
||||
if (iRange > 0 && iHeightDifference > 0)
|
||||
{
|
||||
FLOAT fTempPenalty = gGameCTHConstants.BASE_SHOOTING_UPWARDS * iHeightDifference;
|
||||
fTempPenalty /= iRange;
|
||||
|
||||
fBaseModifier += fTempPenalty;
|
||||
}
|
||||
|
||||
// Target has high Agility or Experience and is therefore harder to hit
|
||||
FLOAT fTempPenalty = (FLOAT)__max((pTarget->stats.bExpLevel*10), pTarget->stats.bAgility);
|
||||
fBaseModifier += (fTempPenalty * gGameCTHConstants.BASE_AGILE_TARGET) / 100;
|
||||
|
||||
// INVIS TARGET
|
||||
if (fCantSeeTarget)
|
||||
{
|
||||
fBaseModifier += gGameCTHConstants.BASE_TARGET_INVISIBLE;
|
||||
}
|
||||
|
||||
return fBaseModifier;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitAimAttributeBonus(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
INT8 bExperience = EffectiveExpLevel( pSoldier );
|
||||
INT8 bMarksmanship = EffectiveMarksmanship( pSoldier );
|
||||
INT16 bDexterity = EffectiveDexterity( pSoldier, FALSE );
|
||||
INT16 bWisdom = EffectiveWisdom( pSoldier );
|
||||
|
||||
// Start by calculating the CTH cap, the absolute maximum CTH a shooter can get.
|
||||
FLOAT fCombinedSkill = (FLOAT)( gGameCTHConstants.AIM_EXP * bExperience * 10 );
|
||||
fCombinedSkill += (FLOAT)( gGameCTHConstants.AIM_MARKS * bMarksmanship );
|
||||
fCombinedSkill += (FLOAT)( gGameCTHConstants.AIM_DEX * bDexterity );
|
||||
fCombinedSkill += (FLOAT)( gGameCTHConstants.AIM_WIS * bWisdom );
|
||||
|
||||
// Divide by the total coefficient value.
|
||||
fCombinedSkill /= (FLOAT)(gGameCTHConstants.AIM_EXP + gGameCTHConstants.AIM_MARKS + gGameCTHConstants.AIM_DEX + gGameCTHConstants.AIM_WIS);
|
||||
|
||||
return fCombinedSkill;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitAimEffectBonus(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
FLOAT fAimModifier = 0;
|
||||
|
||||
// MORALE
|
||||
INT32 iTempModifier = GetMoraleModifier( pSoldier );
|
||||
if ( iTempModifier > 0 )
|
||||
{
|
||||
fAimModifier += gGameCTHConstants.AIM_HIGH_MORALE * iTempModifier;
|
||||
}
|
||||
else if ( iTempModifier < 0 )
|
||||
{
|
||||
fAimModifier += gGameCTHConstants.AIM_LOW_MORALE * iTempModifier;
|
||||
}
|
||||
|
||||
// Flugente: drugs can temporarily cause a merc to go psycho
|
||||
if ( pSoldier->ubProfile != NO_PROFILE && (gMercProfiles[ pSoldier->ubProfile ].bDisability == PSYCHO || MercUnderTheInfluence(pSoldier, DRUG_TYPE_PSYCHO) ) )
|
||||
{
|
||||
fAimModifier += gGameCTHConstants.AIM_PSYCHO;
|
||||
}
|
||||
|
||||
// INJURY
|
||||
if (pSoldier->stats.bLife < pSoldier->stats.bLifeMax)
|
||||
{
|
||||
FLOAT fTempPenalty = gGameCTHConstants.AIM_INJURY;
|
||||
|
||||
// Bleeding damage is used as a percentage off the Max Life, giving a penalty of up to BASE_INJURY.
|
||||
fAimModifier += (pSoldier->bBleeding * fTempPenalty) / pSoldier->stats.bLifeMax;
|
||||
|
||||
// Bandage damage is used similarly, but is only 1/3 as bad.
|
||||
INT8 bBandaged = pSoldier->stats.bLifeMax - pSoldier->stats.bLife - pSoldier->bBleeding;
|
||||
fAimModifier += ((bBandaged * fTempPenalty) / pSoldier->stats.bLifeMax) / 3;
|
||||
}
|
||||
|
||||
// FATIGUE
|
||||
fAimModifier += (gGameCTHConstants.AIM_FATIGUE * (100 - pSoldier->bBreath)) / 100;
|
||||
|
||||
// DRUNKNESS
|
||||
INT8 iDrunkness = GetDrunkLevel(pSoldier);
|
||||
if (iDrunkness > 0)
|
||||
{
|
||||
iDrunkness -= 1;
|
||||
fAimModifier += gGameCTHConstants.AIM_DRUNK[iDrunkness];
|
||||
}
|
||||
|
||||
// GASSED
|
||||
if ( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED )
|
||||
{
|
||||
fAimModifier += gGameCTHConstants.AIM_GASSED;
|
||||
}
|
||||
|
||||
// BEING BANDAGED
|
||||
if (pSoldier->ubServiceCount > 0)
|
||||
{
|
||||
fAimModifier += gGameCTHConstants.AIM_BEING_BANDAGED;
|
||||
}
|
||||
|
||||
// SHOCK (SHOOTER)
|
||||
if (pSoldier->aiData.bShock)
|
||||
{
|
||||
FLOAT fTempPenalty = gGameCTHConstants.AIM_SHOCK;
|
||||
FLOAT fShockPercentage = (FLOAT)(pSoldier->aiData.bShock * 100) / gGameExternalOptions.ubMaxSuppressionShock;
|
||||
fAimModifier += (fTempPenalty * fShockPercentage) / 100;
|
||||
}
|
||||
|
||||
return fAimModifier;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitAimWeaponBonus(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, FLOAT fGunAimDifficulty, UINT8 stance)
|
||||
{
|
||||
FLOAT fAimModifier = 0;
|
||||
UINT16 usInHand = pSoldier->usAttackingWeapon;
|
||||
OBJECTTYPE * pInHand = &(pSoldier->inv[pSoldier->ubAttackingHand]);
|
||||
|
||||
// "AIMING" FROM ALTERNATIVE WEAPON HOLDING (faster shots, no scopes, less accuracy)
|
||||
if ( pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ))
|
||||
{
|
||||
//due to the way aiming levels are handled in NCTH, this penalty is increased here by 1/3 (it is harmonized by reduced aiming clicks)
|
||||
fAimModifier -= gGameExternalOptions.ubAltWeaponHoldingAimingPenaly * 4 / 3;
|
||||
}
|
||||
|
||||
// WEAPON CONDITION
|
||||
if ( (*pInHand)[0]->data.objectStatus < 50 )
|
||||
{
|
||||
fAimModifier += gGameCTHConstants.AIM_GUN_CONDITION * (50 - (*pInHand)[0]->data.objectStatus);
|
||||
}
|
||||
|
||||
// FIRING 1-HANDED WEAPONS
|
||||
if ( !(Item[ usInHand ].twohanded ) ) //JMich Todo: fix for UnderBarrel firing
|
||||
{
|
||||
if (pSoldier->inv[SECONDHANDPOS].exists() != false)
|
||||
{
|
||||
if ( pSoldier->IsValidSecondHandShot( ) )
|
||||
{
|
||||
// Penalty for shooting two pistols. Ambidextrous trait halves this.
|
||||
FLOAT fTempPenalty = (gGameCTHConstants.AIM_TWO_GUNS * fGunAimDifficulty) - fGunAimDifficulty;
|
||||
if (gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT ))
|
||||
{
|
||||
fTempPenalty = fTempPenalty * (100 - gSkillTraitValues.ubAMPenaltyDoubleReduction) / 100;
|
||||
}
|
||||
else if (!gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXT_OT ))
|
||||
{
|
||||
fTempPenalty = 0;
|
||||
}
|
||||
|
||||
fGunAimDifficulty += fTempPenalty;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Penalty for shooting a pistol with just one hand
|
||||
fGunAimDifficulty *= gGameCTHConstants.AIM_ONE_HANDED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HEAVY WEAPON in OLD trait system
|
||||
if (Item[usInHand].rocketlauncher && !(gGameOptions.fNewTraitSystem) )
|
||||
{
|
||||
// Penalty for shooting heavy weapons (launchers). Heavy Weapons skill halves this, once per skill level.
|
||||
FLOAT fTempPenalty = (gGameCTHConstants.AIM_HEAVY_WEAPON * fGunAimDifficulty) - fGunAimDifficulty;
|
||||
for (INT32 x=0; x < NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPS_OT ); x++)
|
||||
{
|
||||
fTempPenalty /= 2;
|
||||
}
|
||||
fGunAimDifficulty += fTempPenalty;
|
||||
}
|
||||
|
||||
// STANCE
|
||||
switch (stance)
|
||||
{
|
||||
case ANIM_STAND:
|
||||
fGunAimDifficulty *= gGameCTHConstants.AIM_STANDING_STANCE;
|
||||
break;
|
||||
|
||||
case ANIM_CROUCH:
|
||||
fGunAimDifficulty *= gGameCTHConstants.AIM_CROUCHING_STANCE;
|
||||
break;
|
||||
|
||||
case ANIM_PRONE:
|
||||
fGunAimDifficulty *= gGameCTHConstants.AIM_PRONE_STANCE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Percentage based handling modifier from the gun and its attachments
|
||||
FLOAT moda = (fGunAimDifficulty * GetPercentHandlingModifier( pInHand, ANIM_PRONE )) / 100;
|
||||
FLOAT modb = (fGunAimDifficulty * GetPercentHandlingModifier( pInHand, stance )) / 100;
|
||||
fGunAimDifficulty += (gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb)/100;
|
||||
|
||||
// Now apply Gun Difficulty to the Aim Modifier
|
||||
fAimModifier -= gGameCTHConstants.AIM_DRAW_COST * fGunAimDifficulty;
|
||||
|
||||
// Percent modifier from the weapon and its attachments
|
||||
INT32 imoda = GetPercentAimModifier( pInHand, ANIM_PRONE );
|
||||
INT32 imodb = GetPercentAimModifier( pInHand, stance );
|
||||
fAimModifier += (FLOAT)(gGameExternalOptions.ubProneModifierPercentage * imoda + (100 - gGameExternalOptions.ubProneModifierPercentage) * imodb)/100;
|
||||
|
||||
return fAimModifier;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitAimSpecialBonus(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
FLOAT fAimModifier = 0;
|
||||
|
||||
// GAME DIFFICULTY
|
||||
if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_PC ) && (pSoldier->bSide != gbPlayerNum) )
|
||||
{
|
||||
fAimModifier += gGameCTHConstants.AIM_DIFFICULTY[gGameOptions.ubDifficultyLevel];
|
||||
}
|
||||
|
||||
return fAimModifier;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitAimTargetBonus(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTarget, INT32 sGridNo, INT32 iRange, UINT8 ubAimPos, BOOLEAN fCantSeeTarget)
|
||||
{
|
||||
FLOAT fAimModifier = 0;
|
||||
INT32 iHeightDifference = 0;
|
||||
|
||||
// SHOOTING AT A TARGET AT DIFFERENT HEIGHT?
|
||||
if ( pTarget == NULL )
|
||||
{
|
||||
// Shooting to roof.
|
||||
if ( pSoldier->bTargetLevel > pSoldier->pathing.bLevel )
|
||||
{
|
||||
iHeightDifference = 3 * pSoldier->bTargetLevel;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// HEIGHT DIFFERENCE
|
||||
UINT32 uiShooterHeight = 0;
|
||||
UINT32 uiTargetHeight = 0;
|
||||
|
||||
if ( pSoldier->pathing.bLevel > 0 )
|
||||
{
|
||||
uiShooterHeight += 3 * pSoldier->pathing.bLevel;
|
||||
}
|
||||
|
||||
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
||||
{
|
||||
case ANIM_STAND:
|
||||
uiShooterHeight += 2;
|
||||
break;
|
||||
case ANIM_CROUCH:
|
||||
uiShooterHeight += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pTarget->pathing.bLevel > 0)
|
||||
{
|
||||
uiTargetHeight += 3 * pTarget->pathing.bLevel;
|
||||
}
|
||||
|
||||
switch ( gAnimControl[ pTarget->usAnimState ].ubEndHeight )
|
||||
{
|
||||
case ANIM_STAND:
|
||||
switch (ubAimPos)
|
||||
{
|
||||
case AIM_SHOT_HEAD:
|
||||
uiTargetHeight += 2;
|
||||
break;
|
||||
case AIM_SHOT_TORSO:
|
||||
case AIM_SHOT_RANDOM:
|
||||
case AIM_SHOT_GLAND:
|
||||
uiTargetHeight += 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ANIM_CROUCH:
|
||||
switch (ubAimPos)
|
||||
{
|
||||
case AIM_SHOT_HEAD:
|
||||
uiTargetHeight += 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
iHeightDifference = uiShooterHeight - uiTargetHeight;
|
||||
if (iHeightDifference < 0)
|
||||
{
|
||||
iHeightDifference *= -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iHeightDifference = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Height difference is mitigated by range. A LONGER range reduces this penalty!
|
||||
if (iRange > 0 && iHeightDifference > 0)
|
||||
{
|
||||
FLOAT fTempPenalty = gGameCTHConstants.AIM_SHOOTING_UPWARDS * iHeightDifference;
|
||||
fTempPenalty /= iRange;
|
||||
fAimModifier += fTempPenalty;
|
||||
}
|
||||
|
||||
// target is invisible to the shooter
|
||||
if(fCantSeeTarget)
|
||||
{
|
||||
fAimModifier += gGameCTHConstants.AIM_TARGET_INVISIBLE;
|
||||
}
|
||||
|
||||
return fAimModifier;
|
||||
}
|
||||
|
||||
FLOAT CalcNewChanceToHitAimTraitBonus(SOLDIERTYPE *pSoldier, FLOAT fDifference, INT32 sGridNo, INT16 ubAimTime, FLOAT fScopeMagFactor, UINT32 uiBestScopeRange)
|
||||
{
|
||||
FLOAT fAimChance = 0;
|
||||
FLOAT fSniperSkillBonus = 0;
|
||||
UINT16 usInHand = pSoldier->usAttackingWeapon;
|
||||
INT32 iRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo );
|
||||
|
||||
// SANDRO - added support to Throwing trait
|
||||
if ( fAimChance < gGameExternalOptions.ubMaximumCTH && Item[ usInHand ].usItemClass == IC_THROWING_KNIFE )
|
||||
{
|
||||
if( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, THROWING_NT ) )
|
||||
{
|
||||
fAimChance += (FLOAT)((fDifference * gSkillTraitValues.ubTHBladesCtHBonusPerClick*3) / gGameExternalOptions.ubMaximumCTH);
|
||||
}
|
||||
}
|
||||
// Add bonuses from Sniper Skill. Applies only when using a scope at or above its "best" range.
|
||||
else if ( !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) )
|
||||
{
|
||||
if (fAimChance < gGameExternalOptions.ubMaximumCTH && fScopeMagFactor > 1.0 && iRange >= (INT32)uiBestScopeRange )
|
||||
{
|
||||
INT8 loop;
|
||||
if(gGameOptions.fNewTraitSystem)
|
||||
{
|
||||
if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL || Weapon[usInHand].ubWeaponType == GUN_M_PISTOL )
|
||||
loop = NUM_SKILL_TRAITS( pSoldier, GUNSLINGER_NT );
|
||||
else
|
||||
loop = NUM_SKILL_TRAITS( pSoldier, SNIPER_NT );
|
||||
}
|
||||
else
|
||||
loop = NUM_SKILL_TRAITS( pSoldier, PROF_SNIPER_OT );
|
||||
|
||||
for (INT8 cnt = 0; cnt < loop; cnt++)
|
||||
{
|
||||
// For each level of the sniper skill, cover a defined distance between the current CTH Cap and 99 (the
|
||||
// maximum...
|
||||
// SANDRO - this results in giving us total bonus on fAimChance of lame 3-8%... compared to 10-20% bonus *PER AIM CLICK* in OCTH
|
||||
// ..as an artificial solution multiply the bonus by at least 2 to achieve some meaningful results
|
||||
if(gGameOptions.fNewTraitSystem)
|
||||
{
|
||||
if ( Weapon[usInHand].ubWeaponType == GUN_PISTOL || Weapon[usInHand].ubWeaponType == GUN_M_PISTOL )
|
||||
fSniperSkillBonus += (FLOAT)((fDifference * gSkillTraitValues.ubGSAimingBonusPerClick * 2) / gGameExternalOptions.ubMaximumCTH);
|
||||
else
|
||||
fSniperSkillBonus += (FLOAT)((fDifference * gSkillTraitValues.ubSNAimingBonusPerClick * 2) / gGameExternalOptions.ubMaximumCTH);
|
||||
}
|
||||
else
|
||||
fSniperSkillBonus += (FLOAT)((fDifference * gGameCTHConstants.AIM_SNIPER_SKILL * 2) / gGameExternalOptions.ubMaximumCTH);
|
||||
fDifference -= fSniperSkillBonus;
|
||||
}
|
||||
|
||||
fAimChance += fSniperSkillBonus;
|
||||
}
|
||||
}
|
||||
|
||||
return fAimChance;
|
||||
}
|
||||
Reference in New Issue
Block a user