diff --git a/GameSettings.cpp b/GameSettings.cpp index c4d61c35..adf13438 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -716,10 +716,6 @@ 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); - // Flugente: this change allows to target head/torso/legs of prone targets. This option will be removed once accepted gGameExternalOptions.fAllowTargetHeadAndLegIfProne = iniReader.ReadBoolean( "System Limit Settings", "ALLOW_TARGET_HEADANDLEG_IFPRONE", TRUE ); diff --git a/GameSettings.h b/GameSettings.h index bf878ecc..591b28ad 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -575,10 +575,6 @@ 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; - // Flugente: this change allows to target head/torso/legs of prone targets. This option will be removed once accepted BOOLEAN fAllowTargetHeadAndLegIfProne; diff --git a/GameVersion.cpp b/GameVersion.cpp index debed40b..593de0b4 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -15,9 +15,9 @@ #ifdef JA2EDITOR #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8278 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8281 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8278 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8281 (Development Build)" }; #endif // ------------------------------ @@ -27,11 +27,11 @@ //DEBUG BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8278 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8281 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8278 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8281 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8278 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8281 (Development Build)" }; #endif #elif defined CRIPPLED_VERSION @@ -46,16 +46,16 @@ //RELEASE BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8278 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8281 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8278 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8281 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Release v1.13.8278 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.8281 (Development Build)" }; #endif #endif -CHAR8 czVersionNumber[16] = { "Build 16.07.31" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 16.08.06" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/Tactical/Interface Enhanced.cpp b/Tactical/Interface Enhanced.cpp index 9d3c1e2e..14920fbe 100644 --- a/Tactical/Interface Enhanced.cpp +++ b/Tactical/Interface Enhanced.cpp @@ -1474,7 +1474,7 @@ void InternalInitEDBTooltipRegion( OBJECTTYPE * gpItemDescObject, UINT32 guiCurr if(UsingNewCTHSystem() == true) { // with the new Laser Performance Bonus we need to display a different text for laser on a weapons general tab - if ( gGameExternalOptions.fUseNewCTHCalculation && cnt == 6 + if ( cnt == 6 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) swprintf( pStr, L"%s%s", szUDBAdvStatsTooltipText[64], szUDBGenWeaponsStatsExplanationsTooltipText[ cnt ]); else if ( cnt == 10 && gGameExternalOptions.fAdvRepairSystem && !gSkillTraitValues.fTETraitsCanRestoreItemThreshold ) @@ -1628,7 +1628,7 @@ void InternalInitEDBTooltipRegion( OBJECTTYPE * gpItemDescObject, UINT32 guiCurr // with the reworked NCTH code and the laser performance factor we will display BestLaserRange instead of ProjectionFactor but we still need the mouse region if (UsingNewCTHSystem() == true && ( (Item[gpItemDescObject->usItem].projectionfactor > 1.0 || GetProjectionFactor( gpItemDescObject ) > 1.0) || - ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpItemDescObject ) > 0 + ( GetBestLaserRange( gpItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) ) ) { ubRegionOffset = 6; @@ -3158,7 +3158,7 @@ void InternalInitEDBTooltipRegion( OBJECTTYPE * gpItemDescObject, UINT32 guiCurr ///////////////////// PROJECTION FACTOR // with the reworked NCTH code and the laser performance factor we will display BestLaserRange instead of ProjectionFactor - if ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpItemDescObject ) > 0 + if ( GetBestLaserRange( gpItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) { if( UsingNewCTHSystem() == true ) @@ -4307,11 +4307,11 @@ void DrawWeaponStats( OBJECTTYPE * gpItemDescObject ) // with the reworked NCTH code and the laser performance factor we will display BestLaserRange instead of ProjectionFactor but we use the same icon if ( (UsingNewCTHSystem() == true && ( (Item[gpItemDescObject->usItem].projectionfactor > 1.0 || GetProjectionFactor( gpItemDescObject ) > 1.0) || - ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpItemDescObject ) > 0 + ( GetBestLaserRange( gpItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) ) ) || ( fComparisonMode && UsingNewCTHSystem() == true && ( (Item[gpComparedItemDescObject->usItem].projectionfactor > 1.0 || GetProjectionFactor( gpComparedItemDescObject ) > 1.0) || - ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpComparedItemDescObject ) > 0 + ( GetBestLaserRange( gpComparedItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) ) ) ) { ubNumLine = 6; @@ -5227,11 +5227,11 @@ void DrawAdvancedStats( OBJECTTYPE * gpItemDescObject ) // with the reworked NCTH code and the laser performance factor we will display BestLaserRange instead of ProjectionFactor but we use the same icon if ( (cnt-sFirstLine < sLastLine && ( GetProjectionFactor( gpItemDescObject ) > 1.0 || - ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpItemDescObject ) > 0 + ( GetBestLaserRange( gpItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) ) ) || ( fComparisonMode && (cnt-sFirstLine < sLastLine && ( GetProjectionFactor( gpComparedItemDescObject ) > 1.0 || - ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpComparedItemDescObject ) > 0 + ( GetBestLaserRange( gpComparedItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) ) ) ) ) { if( UsingNewCTHSystem() == true ) @@ -7191,7 +7191,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) // with the reworked NCTH code and the laser performance factor we will display BestLaserRange instead of ProjectionFactor if (UsingNewCTHSystem() == true && ( (Item[gpItemDescObject->usItem].projectionfactor > 1.0 || GetProjectionFactor( gpItemDescObject ) > 1.0) || - ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpItemDescObject ) > 0 + ( GetBestLaserRange( gpItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) ) ) { // Set line to draw into @@ -7202,7 +7202,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) FLOAT iFinalProjectionValue = 0; BOOLEAN bNewCode = FALSE; - if ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpItemDescObject ) > 0 + if ( GetBestLaserRange( gpItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) { // Get base laser range @@ -7289,7 +7289,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) } else if( fComparisonMode && UsingNewCTHSystem() == true && ( (Item[gpComparedItemDescObject->usItem].projectionfactor > 1.0 || GetProjectionFactor( gpComparedItemDescObject ) > 1.0) || - ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpItemDescObject ) > 0 + ( GetBestLaserRange( gpItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) ) ) { ubNumLine = 6; @@ -7299,7 +7299,7 @@ void DrawWeaponValues( OBJECTTYPE * gpItemDescObject ) FLOAT iFinalProjectionValue = 0; BOOLEAN bNewCode = FALSE; - if ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpComparedItemDescObject ) > 0 + if ( GetBestLaserRange( gpComparedItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) { // Get base laser range @@ -11034,7 +11034,7 @@ void DrawAdvancedValues( OBJECTTYPE *gpItemDescObject ) BOOLEAN bNewCode = FALSE; ///////////////////// PROJECTION FACTOR // with the reworked NCTH code and the laser performance factor we will display BestLaserRange instead of ProjectionFactor - if ( gGameExternalOptions.fUseNewCTHCalculation && GetBestLaserRange( gpItemDescObject ) > 0 + if ( GetBestLaserRange( gpItemDescObject ) > 0 && (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) { iFloatModifier[0] = ((FLOAT)GetBestLaserRange( gpItemDescObject ) / CELL_X_SIZE); diff --git a/Tactical/Interface.cpp b/Tactical/Interface.cpp index b5eea65a..e9aab9be 100644 --- a/Tactical/Interface.cpp +++ b/Tactical/Interface.cpp @@ -2509,58 +2509,54 @@ BOOLEAN DrawCTHIndicator() FLOAT fBrightnessModifier = 1; FLOAT fEffectiveLaserRatio = 1; - // when using the reworked NCTH code we do additional calculations for iron sights and lasers - if (gGameExternalOptions.fUseNewCTHCalculation) + // silversurfer: New functionality for iron sights - There have been many complaints that iron sights lose their usefulness + // very fast the farther the target is away. Setting IRON_SIGHT_PERFORMANCE_BONUS too high makes them overly powerful at + // close range. This experimental formula implements a curve that lowers iBasicAperture the farther the target is away. + // At 1 tile distance iBasicAperture will be the same as before. That's the common start. + if ( gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_USE_GRADIENT && gCTHDisplay.ScopeMagFactor <= 1.0 && !pSoldier->IsValidAlternativeFireMode( pSoldier->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) + + iBasicAperture = iBasicAperture * ( 1 / sqrt( d2DDistance / FLOAT(CELL_X_SIZE) ) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER + + (gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - 1) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER ); + + // iron sights can get a percentage bonus to make them overall better but only when not shooting from hip + if ( gCTHDisplay.ScopeMagFactor <= 1.0 && !pSoldier->IsValidAlternativeFireMode( pSoldier->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) + + iBasicAperture = iBasicAperture * (FLOAT)( (100 - gGameCTHConstants.IRON_SIGHT_PERFORMANCE_BONUS) / 100); + + // laser pointers can provide a percentage bonus to base aperture + if ( gCTHDisplay.iBestLaserRange > 0 + && ( gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) { - // silversurfer: New functionality for iron sights - There have been many complaints that iron sights lose their usefulness - // very fast the farther the target is away. Setting IRON_SIGHT_PERFORMANCE_BONUS too high makes them overly powerful at - // close range. This experimental formula implements a curve that lowers iBasicAperture the farther the target is away. - // At 1 tile distance iBasicAperture will be the same as before. That's the common start. - if ( gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_USE_GRADIENT && gCTHDisplay.ScopeMagFactor <= 1.0 && !pSoldier->IsValidAlternativeFireMode( pSoldier->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) + INT8 bLightLevel = LightTrueLevel(gCTHDisplay.iTargetGridNo, gsInterfaceLevel ); + INT32 iMaxLaserRange = ( gCTHDisplay.iBestLaserRange*( 2*bLightLevel + 3*NORMAL_LIGHTLEVEL_NIGHT - 5*NORMAL_LIGHTLEVEL_DAY ) ) / ( 2 * ( NORMAL_LIGHTLEVEL_NIGHT - NORMAL_LIGHTLEVEL_DAY ) ); - iBasicAperture = iBasicAperture * ( 1 / sqrt( d2DDistance / FLOAT(CELL_X_SIZE) ) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - + (gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - 1) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER ); - - // iron sights can get a percentage bonus to make them overall better but only when not shooting from hip - if ( gCTHDisplay.ScopeMagFactor <= 1.0 && !pSoldier->IsValidAlternativeFireMode( pSoldier->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) - - iBasicAperture = iBasicAperture * (FLOAT)( (100 - gGameCTHConstants.IRON_SIGHT_PERFORMANCE_BONUS) / 100); - - // laser pointers can provide a percentage bonus to base aperture - if ( gCTHDisplay.iBestLaserRange > 0 - && ( gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) + // laser only has effect when in range + if ( iMaxLaserRange > d2DDistance ) { - INT8 bLightLevel = LightTrueLevel(gCTHDisplay.iTargetGridNo, gsInterfaceLevel ); - INT32 iMaxLaserRange = ( gCTHDisplay.iBestLaserRange*( 2*bLightLevel + 3*NORMAL_LIGHTLEVEL_NIGHT - 5*NORMAL_LIGHTLEVEL_DAY ) ) / ( 2 * ( NORMAL_LIGHTLEVEL_NIGHT - NORMAL_LIGHTLEVEL_DAY ) ); + // which bonus do we want to apply? + if ( pSoldier->IsValidAlternativeFireMode( pSoldier->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) + // shooting from hip + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP; + else if ( gCTHDisplay.ScopeMagFactor <= 1.0 ) + // using iron sights or other 1x sights + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON; + else + // must be using a scope + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE; - // laser only has effect when in range - if ( iMaxLaserRange > d2DDistance ) + // light level influences how easy it is to spot the laser dot on the target + fBrightnessModifier = (FLOAT)(bLightLevel) / (FLOAT)(NORMAL_LIGHTLEVEL_NIGHT); + + // laser fully efficient + if ( gCTHDisplay.iBestLaserRange > d2DDistance ) + // apply full bonus + iBasicAperture = iBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier)) / 100); + else { - // which bonus do we want to apply? - if ( pSoldier->IsValidAlternativeFireMode( pSoldier->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) - // shooting from hip - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP; - else if ( gCTHDisplay.ScopeMagFactor <= 1.0 ) - // using iron sights or other 1x sights - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON; - else - // must be using a scope - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE; - - // light level influences how easy it is to spot the laser dot on the target - fBrightnessModifier = (FLOAT)(bLightLevel) / (FLOAT)(NORMAL_LIGHTLEVEL_NIGHT); - - // laser fully efficient - if ( gCTHDisplay.iBestLaserRange > d2DDistance ) - // apply full bonus - iBasicAperture = iBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier)) / 100); - else - { - // beyond BestLaserRange laser bonus drops linearly to 0 - fEffectiveLaserRatio = (FLOAT)(iMaxLaserRange - d2DDistance) / (FLOAT)(iMaxLaserRange - gCTHDisplay.iBestLaserRange); - // apply partial bonus - iBasicAperture = iBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier * fEffectiveLaserRatio)) / 100); - } + // beyond BestLaserRange laser bonus drops linearly to 0 + fEffectiveLaserRatio = (FLOAT)(iMaxLaserRange - d2DDistance) / (FLOAT)(iMaxLaserRange - gCTHDisplay.iBestLaserRange); + // apply partial bonus + iBasicAperture = iBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier * fEffectiveLaserRatio)) / 100); } } } diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index bcaa633c..b1e1e76f 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -7342,7 +7342,7 @@ BOOLEAN AutoPlaceObject( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj, BOOLEAN fNew break; } - if (PlaceInAnySlot(pSoldier, pObj, (fNewItem == TRUE), bExcludeSlot, fStackOrSingleSlot) == true) + if ( PlaceInAnySlot(pSoldier, pObj, (fNewItem == TRUE), bExcludeSlot, fStackOrSingleSlot) ) return TRUE; return( FALSE ); @@ -12845,7 +12845,14 @@ FLOAT GetBestScopeMagnificationFactor( SOLDIERTYPE *pSoldier, OBJECTTYPE * pObj, INT32 iCurrentTotalPenalty = 0; INT32 iBestTotalPenalty = 0; FLOAT rangeModifier = GetScopeRangeMultiplier(pSoldier, pObjUsed, uiRange); - FLOAT iProjectionFactor = CalcProjectionFactor(pSoldier, pObjUsed, uiRange, 1); + FLOAT iProjectionFactor = 0; + + // With the reworked NCTH code we don't want to use iProjectionFactor anymore. + // Instead we use the performance bonus if at least one bonus is != 0. Otherwise -> continue using Projection Factor. + if ( (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE) != 0 ) + iProjectionFactor = 1.0; + else + iProjectionFactor = CalcProjectionFactor(pSoldier, pObjUsed, uiRange, 1); // Flugente: if scope modes are allowed, use them if ( gGameExternalOptions.fScopeModes && pSoldier && pObjUsed->exists() == true && Item[pObjUsed->usItem].usItemClass == IC_GUN ) diff --git a/Tactical/LOS.cpp b/Tactical/LOS.cpp index 2969847e..c0180c26 100644 --- a/Tactical/LOS.cpp +++ b/Tactical/LOS.cpp @@ -2505,7 +2505,7 @@ INT32 LocationToLocationLineOfSightTest( INT32 sStartGridNo, INT8 bStartLevel, I { FLOAT dStartZPos, dEndZPos; INT16 sStartXPos, sStartYPos, sEndXPos, sEndYPos; - UINT8 ubStartID; +// UINT8 ubStartID; // sevenfm: always use standing heights /*ubStartID = WhoIsThere2( sStartGridNo, bStartLevel ); @@ -7901,59 +7901,55 @@ void AdjustTargetCenterPoint( SOLDIERTYPE *pShooter, INT32 iTargetGridNo, FLOAT // First, let's calculate the basic Aperture. This is the size of an unmodified aperture at 1x Normal Distance. iBasicAperture = CalcBasicAperture( ); - // when using the reworked NCTH code we do additional calculations for iron sights and lasers - if (gGameExternalOptions.fUseNewCTHCalculation) + // silversurfer: New functionality for iron sights - There have been many complaints that iron sights lose their usefulness + // very fast the farther the target is away. Setting IRON_SIGHT_PERFORMANCE_BONUS too high makes them overly powerful at + // close range. This experimental formula implements a curve that lowers iBasicAperture the farther the target is away. + // At 1 tile distance iBasicAperture will be the same as before. That's the common start. + if ( gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_USE_GRADIENT && gCTHDisplay.ScopeMagFactor <= 1.0 && !pShooter->IsValidAlternativeFireMode( pShooter->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) + + iBasicAperture = iBasicAperture * ( 1 / sqrt( d2DDistance / FLOAT(CELL_X_SIZE) ) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER + + (gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - 1) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER ); + + // iron sights can get a percentage bonus to make them overall better but only when not shooting from hip + if ( gCTHDisplay.ScopeMagFactor <= 1.0 && !pShooter->IsValidAlternativeFireMode( pShooter->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) + + iBasicAperture = iBasicAperture * (FLOAT)( (100 - gGameCTHConstants.IRON_SIGHT_PERFORMANCE_BONUS) / 100); + + // laser pointers can provide a percentage bonus to base aperture + if ( gCTHDisplay.iBestLaserRange > 0 + && ( gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) { - // silversurfer: New functionality for iron sights - There have been many complaints that iron sights lose their usefulness - // very fast the farther the target is away. Setting IRON_SIGHT_PERFORMANCE_BONUS too high makes them overly powerful at - // close range. This experimental formula implements a curve that lowers iBasicAperture the farther the target is away. - // At 1 tile distance iBasicAperture will be the same as before. That's the common start. - if ( gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_USE_GRADIENT && gCTHDisplay.ScopeMagFactor <= 1.0 && !pShooter->IsValidAlternativeFireMode( pShooter->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) + INT8 bLightLevel = LightTrueLevel(gCTHDisplay.iTargetGridNo, gsInterfaceLevel ); + INT32 iMaxLaserRange = ( gCTHDisplay.iBestLaserRange*( 2*bLightLevel + 3*NORMAL_LIGHTLEVEL_NIGHT - 5*NORMAL_LIGHTLEVEL_DAY ) ) / ( 2 * ( NORMAL_LIGHTLEVEL_NIGHT - NORMAL_LIGHTLEVEL_DAY ) ); - iBasicAperture = iBasicAperture * ( 1 / sqrt( d2DDistance / FLOAT(CELL_X_SIZE) ) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - + (gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - 1) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER ); - - // iron sights can get a percentage bonus to make them overall better but only when not shooting from hip - if ( gCTHDisplay.ScopeMagFactor <= 1.0 && !pShooter->IsValidAlternativeFireMode( pShooter->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) - - iBasicAperture = iBasicAperture * (FLOAT)( (100 - gGameCTHConstants.IRON_SIGHT_PERFORMANCE_BONUS) / 100); - - // laser pointers can provide a percentage bonus to base aperture - if ( gCTHDisplay.iBestLaserRange > 0 - && ( gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) + // laser only has effect when in range + if ( iMaxLaserRange > d2DDistance ) { - INT8 bLightLevel = LightTrueLevel(gCTHDisplay.iTargetGridNo, gsInterfaceLevel ); - INT32 iMaxLaserRange = ( gCTHDisplay.iBestLaserRange*( 2*bLightLevel + 3*NORMAL_LIGHTLEVEL_NIGHT - 5*NORMAL_LIGHTLEVEL_DAY ) ) / ( 2 * ( NORMAL_LIGHTLEVEL_NIGHT - NORMAL_LIGHTLEVEL_DAY ) ); + FLOAT fLaserBonus = 0; + // which bonus do we want to apply? + if ( pShooter->IsValidAlternativeFireMode( pShooter->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) + // shooting from hip + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP; + else if ( gCTHDisplay.ScopeMagFactor <= 1.0 ) + // using iron sights or other 1x sights + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON; + else + // must be using a scope + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE; - // laser only has effect when in range - if ( iMaxLaserRange > d2DDistance ) + // light level influences how easy it is to spot the laser dot on the target + FLOAT fBrightnessModifier = (FLOAT)(bLightLevel) / (FLOAT)(NORMAL_LIGHTLEVEL_NIGHT); + + // laser fully efficient + if ( gCTHDisplay.iBestLaserRange > d2DDistance ) + // apply full bonus + iBasicAperture = iBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier)) / 100); + else { - FLOAT fLaserBonus = 0; - // which bonus do we want to apply? - if ( pShooter->IsValidAlternativeFireMode( pShooter->aiData.bAimTime, gCTHDisplay.iTargetGridNo ) ) - // shooting from hip - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP; - else if ( gCTHDisplay.ScopeMagFactor <= 1.0 ) - // using iron sights or other 1x sights - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON; - else - // must be using a scope - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE; - - // light level influences how easy it is to spot the laser dot on the target - FLOAT fBrightnessModifier = (FLOAT)(bLightLevel) / (FLOAT)(NORMAL_LIGHTLEVEL_NIGHT); - - // laser fully efficient - if ( gCTHDisplay.iBestLaserRange > d2DDistance ) - // apply full bonus - iBasicAperture = iBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier)) / 100); - else - { - // beyond BestLaserRange laser bonus drops linearly to 0 - FLOAT fEffectiveLaserRatio = (FLOAT)(iMaxLaserRange - d2DDistance) / (FLOAT)(iMaxLaserRange - gCTHDisplay.iBestLaserRange); - // apply partial bonus - iBasicAperture = iBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier * fEffectiveLaserRatio)) / 100); - } + // beyond BestLaserRange laser bonus drops linearly to 0 + FLOAT fEffectiveLaserRatio = (FLOAT)(iMaxLaserRange - d2DDistance) / (FLOAT)(iMaxLaserRange - gCTHDisplay.iBestLaserRange); + // apply partial bonus + iBasicAperture = iBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier * fEffectiveLaserRatio)) / 100); } } } @@ -8305,7 +8301,14 @@ FLOAT CalcMagFactor( SOLDIERTYPE *pShooter, OBJECTTYPE *pWeapon, FLOAT d2DDistan iScopeFactor = GetBestScopeMagnificationFactor( pShooter, pWeapon, d2DDistance ); iScopeFactor = __min(iScopeFactor, __max(1.0f, iTargetMagFactor/rangeModifier)); } - iProjectionFactor = CalcProjectionFactor(pShooter, pWeapon, d2DDistance, ubAimTime); + + // With the reworked NCTH code we don't want to use iProjectionFactor anymore. + // Instead we use the performance bonus if at least one bonus is != 0. Otherwise -> continue using Projection Factor. + if ( (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE) != 0 ) + iProjectionFactor = 1.0; + else + iProjectionFactor = CalcProjectionFactor(pShooter, pWeapon, d2DDistance, ubAimTime); + iFinalMagFactor = __max(iScopeFactor, iProjectionFactor); } diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 6c12fb59..dc9e2998 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -5250,12 +5250,6 @@ 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: @@ -5269,15 +5263,12 @@ UINT32 CalcNewChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTi // Basic defines SOLDIERTYPE * pTarget; - INT32 iChance, iRange, iSightRange; //, minRange; + INT32 iRange, iSightRange; //, minRange; UINT16 usInHand; OBJECTTYPE * pInHand; - INT8 bBandaged, iTraitModifier; INT16 sDistVis, sDistVisNoScope; UINT8 ubTargetID; -// bool highPowerScope = false; -// INT16 sTotalAutofirePenalty = 0; bool fCantSeeTarget = false; FLOAT scopeRangeMod = 0.0f; @@ -5369,18 +5360,7 @@ 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, fFinalChance = 0; ////////////////////////////////////////////////////////////////////////////////// @@ -5539,17 +5519,6 @@ if (gGameExternalOptions.fUseNewCTHCalculation) // 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 ) ) { @@ -5613,1216 +5582,8 @@ if (gGameExternalOptions.fUseNewCTHCalculation) return ((INT32)fFinalChance); } -else -{ -////////////////////////////////////////////////////////////////////////////////// -// END CLEANED UP VERSION OF NCTH CALCULATION -////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////// -// START OLD VERSION OF NCTH CALCULATION -////////////////////////////////////////////////////////////////////////////////// - // Handling value modified by ini - UINT8 ubModifiedHandling = Weapon[ usInHand ].ubHandling; - if ( Item[ usInHand ].usItemClass == IC_GUN ) - ubModifiedHandling *= gItemSettings.fHandlingModifierGun[ Weapon[ usInHand ].ubWeaponType ]; - else if ( Item[ usInHand ].usItemClass == IC_LAUNCHER ) - ubModifiedHandling *= gItemSettings.fHandlingModifierLauncher; - - // Calculate how easy it is to handle this gun. - FLOAT iGunDifficulty = (FLOAT)(( ubModifiedHandling * (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 - // - // 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. - // - // The most important skill here is EXPERIENCE. - - FLOAT iBaseModifier = 0; - - INT8 bExperience = EffectiveExpLevel( pSoldier ); - INT8 bMarksmanship = EffectiveMarksmanship( pSoldier ); - INT16 bDexterity = EffectiveDexterity( pSoldier, FALSE ); - INT16 bWisdom = EffectiveWisdom( pSoldier ); - - if ( bMarksmanship == 0 || bDexterity == 0 ) - { - return( gGameExternalOptions.ubMinimumCTH ); - } - - // Calculate the basic value for BASE CTH by factoring in all the character's skills. - FLOAT iCombinedSkill = (FLOAT)(gGameCTHConstants.BASE_EXP*(bExperience*10)); - iCombinedSkill += (FLOAT)(gGameCTHConstants.BASE_MARKS * bMarksmanship); - iCombinedSkill += (FLOAT)(gGameCTHConstants.BASE_WIS * bWisdom); - iCombinedSkill += (FLOAT)(gGameCTHConstants.BASE_DEX * bDexterity); - - // Divide by the total coefficient value. - iCombinedSkill = (FLOAT)(iCombinedSkill / - (gGameCTHConstants.BASE_EXP+ - gGameCTHConstants.BASE_MARKS+ - gGameCTHConstants.BASE_WIS+ - gGameCTHConstants.BASE_DEX)); - - // At this point we have a value of 1 to 100. Divide by 3. - iCombinedSkill /= 3; - - // iCombinedSkill is now our basic CTH. Its range is 0 to 33. - iChance = (INT32)iCombinedSkill; - - // Add a flat Base bonus from the item and its attachments. - INT32 moda = GetObjectModifier( pSoldier, pInHand, stance, ITEMMODIFIER_FLATBASE ); - INT32 modb = GetObjectModifier( pSoldier, pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight, ITEMMODIFIER_FLATBASE ); - iChance += (INT32)((gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb)/100); - - // We now begin adding up factors that may increase or decrease Base CTH. They are pooled together to form a percentage - // value. - - // FIRING FROM ALTERNATIVE WEAPON HOLDING (faster shot, less accuracy) - if ( pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) - { - iBaseModifier -= gGameExternalOptions.ubAltWeaponHoldingCtHPenaly; - } - - // MORALE - INT8 iCurrentMorale = GetMoraleModifier(pSoldier); - if (iCurrentMorale > 0) - { - iBaseModifier += (FLOAT)(iCurrentMorale * gGameCTHConstants.BASE_HIGH_MORALE); - } - else if (iCurrentMorale < 0) - { - iBaseModifier += (FLOAT)(iCurrentMorale * gGameCTHConstants.BASE_LOW_MORALE); - } - - // Flugente: moved trait modifiers into a member function - UINT8 targetprofile = NO_PROFILE; - if ( pTarget && pTarget->ubProfile != NO_PROFILE ) - targetprofile = pTarget->ubProfile; - - iTraitModifier = pSoldier->GetTraitCTHModifier( usInHand, ubAimTime, targetprofile ); - - // Flugente: backgrounds - if ( pTarget && pTarget->bTeam == CREATURE_TEAM ) - iTraitModifier += pSoldier->GetBackgroundValue(BG_PERC_CTH_CREATURE); - - // Flugente: if we are a sniper and a spotter from our team spots the targetted location, we receive a powerful cth bonus - if ( gGameOptions.fNewTraitSystem && Weapon[usInHand].ubWeaponType == GUN_SN_RIFLE || Weapon[usInHand].ubWeaponType == GUN_RIFLE ) - { - iTraitModifier += (INT8)(GridNoSpotterCTHBonus( pSoldier, sGridNo, pSoldier->bTeam)); - } - - // SHOOTING AT SAME TARGET AGAIN - if (sGridNo == pSoldier->sLastTarget ) - iBaseModifier += (FLOAT)gGameCTHConstants.BASE_SAME_TARGET; - - ///////////////////////////////////////////////////////////////////////////////////// - // SANDRO - Bonus CtH for Militia - if (pSoldier->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA && gGameExternalOptions.sGreenMilitiaCtHBonusPercent != 0) - iBaseModifier += gGameExternalOptions.sGreenMilitiaCtHBonusPercent; - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA && gGameExternalOptions.sRegularMilitiaCtHBonusPercent != 0) - iBaseModifier += gGameExternalOptions.sRegularMilitiaCtHBonusPercent; - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA && gGameExternalOptions.sVeteranMilitiaCtHBonusPercent != 0) - iBaseModifier += gGameExternalOptions.sVeteranMilitiaCtHBonusPercent; - // bonus for enemy - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR && gGameExternalOptions.sEnemyAdminCtHBonusPercent != 0) - iBaseModifier += gGameExternalOptions.sEnemyAdminCtHBonusPercent; - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY && gGameExternalOptions.sEnemyRegularCtHBonusPercent != 0) - iBaseModifier += gGameExternalOptions.sEnemyRegularCtHBonusPercent; - else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE && gGameExternalOptions.sEnemyEliteCtHBonusPercent != 0) - iBaseModifier += 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: - iBaseModifier += (iBaseModifier * gGameExternalOptions.usSpecialNPCStronger / 100); - break; - } - } - ///////////////////////////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////// - // Gun Difficulty Modifiers - - // 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, once per skill level. - // SANDRO - Ambidextrous trait has only one level - FLOAT iTempPenalty = (gGameCTHConstants.BASE_TWO_GUNS * iGunBaseDifficulty) - iGunBaseDifficulty; - //INT8 traitLoop = (gGameOptions.fNewTraitSystem?NUM_SKILL_TRAITS( pSoldier, AMBIDEXTROUS_NT ):NUM_SKILL_TRAITS( pSoldier, AMBIDEXT_OT )); - if (gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT )) - { - iTempPenalty = iTempPenalty * (100 - gSkillTraitValues.ubAMPenaltyDoubleReduction) / 100; - } - else if (!gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT )) - { - iTempPenalty = 0; - } - //for (INT32 x = 0; x < traitLoop; x++) - //{ - // iTempPenalty /= 2; - //} - - iGunBaseDifficulty += iTempPenalty; - } - else - { - // Penalty for shooting a pistol with just one hand - iGunBaseDifficulty *= gGameCTHConstants.BASE_ONE_HANDED; - } - } - } - - // HEAVY WEAPON - if (Item[usInHand].rocketlauncher && !(gGameOptions.fNewTraitSystem) ) - { - // Penalty for shooting heavy weapons (launchers). Heavy Weapons skill halves this, once per skill level. - FLOAT iTempPenalty = (gGameCTHConstants.BASE_HEAVY_WEAPON * iGunBaseDifficulty) - iGunBaseDifficulty; - - for (INT32 x=0; x < NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPS_OT ); x++) - { - iTempPenalty /= 2; - } - - iGunBaseDifficulty += iTempPenalty; - } - - // STANCE - switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight ) - { - case ANIM_CROUCH: - iGunBaseDifficulty *= gGameCTHConstants.BASE_CROUCHING_STANCE; - break; - case ANIM_PRONE: - iGunBaseDifficulty *= gGameCTHConstants.BASE_PRONE_STANCE; - break; - } - - // Percent based modifier from the gun and its attachments - FLOAT umoda = (iGunBaseDifficulty * GetObjectModifier( pSoldier, pInHand, stance, ITEMMODIFIER_PERCENTHANDLING )) / 100; - FLOAT umodb = (iGunBaseDifficulty * GetObjectModifier( pSoldier, pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight, ITEMMODIFIER_PERCENTHANDLING )) / 100; - iGunBaseDifficulty += ((gGameExternalOptions.ubProneModifierPercentage * umoda + (100 - gGameExternalOptions.ubProneModifierPercentage) * umodb)/100); - - // End gun difficulty modifiers - ///////////////////////////////////////////// - - // Now apply Gun Difficulty to the Base Modifier. - iBaseModifier -= iGunBaseDifficulty * gGameCTHConstants.BASE_DRAW_COST; - - // INJURY - if (pSoldier->stats.bLife < pSoldier->stats.bLifeMax) - { - FLOAT iTempPenalty = gGameCTHConstants.BASE_INJURY; - - // Bleeding damage is used as a percentage off the Max Life, giving a penalty of up to BASE_INJURY. - iBaseModifier += (pSoldier->bBleeding * iTempPenalty) / pSoldier->stats.bLifeMax; - - // Bandage damage is used similarly, but is only 1/3 as bad. - bBandaged = pSoldier->stats.bLifeMax - pSoldier->stats.bLife - pSoldier->bBleeding; - iBaseModifier += ((bBandaged * iTempPenalty) / pSoldier->stats.bLifeMax) / 3; - } - - // FATIGUE - iBaseModifier += (gGameCTHConstants.BASE_FATIGUE * (100 - pSoldier->bBreath)) / 100; - - // DRUNKNESS - INT8 iDrunkness = GetDrunkLevel(pSoldier); - if (iDrunkness > 0) - { - iDrunkness -= 1; - iBaseModifier += gGameCTHConstants.BASE_DRUNK[iDrunkness]; - } - - // GASSED - if ( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED ) - { - iBaseModifier += gGameCTHConstants.BASE_GASSED; - } - - // BEING BANDAGED - if (pSoldier->ubServiceCount > 0) - { - iBaseModifier += gGameCTHConstants.BASE_BEING_BANDAGED; - } - - // SHOCK (SHOOTER) - if (pSoldier->aiData.bShock) - { - FLOAT iTempPenalty = gGameCTHConstants.BASE_SHOCK; - INT32 iShockPercentage = (pSoldier->aiData.bShock * 100) / gGameExternalOptions.ubMaxSuppressionShock; - - iBaseModifier += (iTempPenalty * iShockPercentage) / 100; - } - - ///////////// - // Target-specific tests - - INT32 iHeightDifference = 0; - 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; - } - - // AGILITY OR EXPERIENCE - FLOAT iTempPenalty = (FLOAT)__max((pTarget->stats.bExpLevel*10), pTarget->stats.bAgility); - - iBaseModifier += (iTempPenalty * gGameCTHConstants.BASE_AGILE_TARGET) / 100; - } - - // Height difference is mitigated by range. A LONGER range reduces this penalty! - if (iRange > 0 && iHeightDifference > 0) - { - FLOAT iTempPenalty = gGameCTHConstants.BASE_SHOOTING_UPWARDS * iHeightDifference; - iTempPenalty /= iRange; - - iBaseModifier += iTempPenalty; - } - - // INVIS TARGET - if (fCantSeeTarget) - { - iBaseModifier += gGameCTHConstants.BASE_TARGET_INVISIBLE; - } - //CHRISL: Applying the Gear AimBonus (penalty) here, and directly to iBaseModifier 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). - iBaseModifier += GetGearAimBonus ( pSoldier, iSightRange, ubAimTime ); - - // GAME DIFFICULTY - if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_PC ) && (pSoldier->bSide != gbPlayerNum) ) - { - iBaseModifier += zDiffSetting[gGameOptions.ubDifficultyLevel].NewDifficultySettingsBASE_DIFFICULTY; - } - - // Percentage based-modifier from the weapon and its attachments - umoda = (iGunBaseDifficulty * GetObjectModifier( pSoldier, pInHand, stance, ITEMMODIFIER_PERCENTBASE )) / 100; - umodb = (iGunBaseDifficulty * GetObjectModifier( pSoldier, pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight, ITEMMODIFIER_PERCENTBASE )) / 100; - iBaseModifier += ((gGameExternalOptions.ubProneModifierPercentage * umoda + (100 - gGameExternalOptions.ubProneModifierPercentage) * umodb)/100); - - - //////////////////////////////////// - // Finish BASE CTH calculation: - - - - iChance = (INT32)(iChance * (100+iBaseModifier)) / 100; - iChance = __max( iChance, 0 ); - iChance = __min( iChance, 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 iScopeMagFactor, rangeModifier; - //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 - { - iScopeMagFactor = GetBestScopeMagnificationFactor( pSoldier, &(pSoldier->inv[pSoldier->ubAttackingHand]), d2DDistance ); - rangeModifier = GetScopeRangeMultiplier(pSoldier, &(pSoldier->inv[pSoldier->ubAttackingHand]), (FLOAT)iRange); - } - else - { - iScopeMagFactor = 1.0f; - rangeModifier = gGameCTHConstants.SCOPE_RANGE_MULTIPLIER; - } - - UINT32 uiBestScopeRange = (UINT32)(iScopeMagFactor * gGameCTHConstants.NORMAL_SHOOTING_DISTANCE * rangeModifier); - - FLOAT iAimModifier = 0; - - // "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) - iAimModifier -= gGameExternalOptions.ubAltWeaponHoldingAimingPenaly * 4 / 3; - } - - // WEAPON CONDITION - if ( (*pInHand)[0]->data.objectStatus < 50 ) - { - iAimModifier += gGameCTHConstants.AIM_GUN_CONDITION * (50 - (*pInHand)[0]->data.objectStatus); - } - - // MORALE - INT32 iTempModifier = GetMoraleModifier( pSoldier ); - if ( iTempModifier > 0 ) - { - iAimModifier += gGameCTHConstants.AIM_HIGH_MORALE * iTempModifier; - } - else if ( iTempModifier < 0 ) - { - iAimModifier += gGameCTHConstants.AIM_LOW_MORALE * iTempModifier; - } - - if(gGameOptions.fNewTraitSystem) - { - //Give 60% the normal stomp modifier as the aim mod - iAimModifier += (FLOAT)iTraitModifier * 0.6f; - } - - // Flugente: drugs can temporarily cause a merc to go psycho - if ( DoesMercHaveDisability( pSoldier, PSYCHO ) ) - { - iAimModifier += gGameCTHConstants.AIM_PSYCHO; - } - - ////////////////////////////////////////// - // Gun Handling modifiers - - switch (gAnimControl[ pSoldier->usAnimState ].ubEndHeight) - { - case ANIM_STAND: - iGunAimDifficulty *= gGameCTHConstants.AIM_STANDING_STANCE; - break; - - case ANIM_CROUCH: - iGunAimDifficulty *= gGameCTHConstants.AIM_CROUCHING_STANCE; - break; - - case ANIM_PRONE: - iGunAimDifficulty *= gGameCTHConstants.AIM_PRONE_STANCE; - break; - } - - // 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, once per skill level. - // SANDRO - Ambidextrous trait has only one level - FLOAT iTempPenalty = (gGameCTHConstants.AIM_TWO_GUNS * iGunAimDifficulty) - iGunAimDifficulty; - //INT8 traitLoop = (gGameOptions.fNewTraitSystem?NUM_SKILL_TRAITS( pSoldier, AMBIDEXTROUS_NT ):NUM_SKILL_TRAITS( pSoldier, AMBIDEXT_OT )); - if (gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT )) - { - iTempPenalty = iTempPenalty * (100 - gSkillTraitValues.ubAMPenaltyDoubleReduction) / 100; - } - else if (!gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, AMBIDEXTROUS_NT )) - { - iTempPenalty = 0; - } - //for (INT32 x = 0; x < traitLoop; x++) - //{ - // iTempPenalty /= 2; - //} - - iGunAimDifficulty += iTempPenalty; - } - else - { - // Penalty for shooting a pistol with just one hand - iGunAimDifficulty *= gGameCTHConstants.AIM_ONE_HANDED; - } - } - } - - // HEAVY WEAPON - if (Item[usInHand].rocketlauncher && !(gGameOptions.fNewTraitSystem) ) - { - // Penalty for shooting heavy weapons (launchers). Heavy Weapons skill halves this, once per skill level. - FLOAT iTempPenalty = (gGameCTHConstants.AIM_HEAVY_WEAPON * iGunAimDifficulty) - iGunAimDifficulty; - - for (INT32 x=0; x < NUM_SKILL_TRAITS( pSoldier, HEAVY_WEAPS_OT ); x++) - { - iTempPenalty /= 2; - } - - iGunAimDifficulty += iTempPenalty; - } - - FLOAT umoda = (iGunAimDifficulty * GetObjectModifier( pSoldier, pInHand, stance, ITEMMODIFIER_PERCENTHANDLING )) / 100; - FLOAT umodb = (iGunAimDifficulty * GetObjectModifier( pSoldier, pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight, ITEMMODIFIER_PERCENTHANDLING )) / 100; - iGunAimDifficulty += (gGameExternalOptions.ubProneModifierPercentage * umoda + (100 - gGameExternalOptions.ubProneModifierPercentage) * umodb)/100; - - // End Gun Handling modifiers - ////////////////////////////////////////// - - iAimModifier -= gGameCTHConstants.AIM_DRAW_COST * iGunAimDifficulty; - - // VISIBILITY - if (iRange > 0 && iSightRange > iRange) - { - FLOAT iTempPenalty = (FLOAT)((FLOAT)iSightRange / (FLOAT)iRange); - iTempPenalty = (FLOAT)(100 / iTempPenalty); - iAimModifier += ((100-iTempPenalty) * gGameCTHConstants.AIM_VISIBILITY)/100; - iAimModifier = __max( gGameCTHConstants.AIM_TARGET_INVISIBLE, iAimModifier ); - } - //if (iSightRange == -1) - if(fCantSeeTarget) - { - iAimModifier += gGameCTHConstants.AIM_TARGET_INVISIBLE; - } - - // INJURY - if (pSoldier->stats.bLife < pSoldier->stats.bLifeMax) - { - FLOAT iTempPenalty = gGameCTHConstants.AIM_INJURY; - - // Bleeding damage is used as a percentage off the Max Life, giving a penalty of up to BASE_INJURY. - iAimModifier += (pSoldier->bBleeding * iTempPenalty) / pSoldier->stats.bLifeMax; - - // Bandage damage is used similarly, but is only 1/3 as bad. - bBandaged = pSoldier->stats.bLifeMax - pSoldier->stats.bLife - pSoldier->bBleeding; - iAimModifier += ((bBandaged * iTempPenalty) / pSoldier->stats.bLifeMax) / 3; - } - - // FATIGUE - iAimModifier += (gGameCTHConstants.AIM_FATIGUE * (100 - pSoldier->bBreath)) / 100; - - // DRUNKNESS - INT8 iDrunkness = GetDrunkLevel(pSoldier); - if (iDrunkness > 0) - { - iDrunkness -= 1; - iAimModifier += gGameCTHConstants.AIM_DRUNK[iDrunkness]; - } - - // GASSED - if ( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED ) - { - iAimModifier += gGameCTHConstants.AIM_GASSED; - } - - // BEING BANDAGED - if (pSoldier->ubServiceCount > 0) - { - iAimModifier += gGameCTHConstants.AIM_BEING_BANDAGED; - } - - // SHOCK (SHOOTER) - if (pSoldier->aiData.bShock) - { - FLOAT iTempPenalty = gGameCTHConstants.AIM_SHOCK; - INT32 iShockPercentage = (pSoldier->aiData.bShock * 100) / gGameExternalOptions.ubMaxSuppressionShock; - - iAimModifier += (iTempPenalty * iShockPercentage) / 100; - } - - ///////////// - // Target-specific tests - - INT32 iHeightDifference = 0; - 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 iTempPenalty = gGameCTHConstants.AIM_SHOOTING_UPWARDS * iHeightDifference; - iTempPenalty /= iRange; - - iAimModifier += iTempPenalty; - } - - // GAME DIFFICULTY - if ( !(pSoldier->flags.uiStatusFlags & SOLDIER_PC ) && (pSoldier->bSide != gbPlayerNum) ) - { - iAimModifier += zDiffSetting[gGameOptions.ubDifficultyLevel].NewDifficultySettingsAIM_DIFFICULTY; - } - - // Percent modifier from the weapon and its attachments - INT32 moda = GetObjectModifier( pSoldier, pInHand, stance, ITEMMODIFIER_PERCENTAIM ); - INT32 modb = GetObjectModifier( pSoldier, pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight, ITEMMODIFIER_PERCENTAIM ); - iAimModifier += (gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb)/100; - - // Calculate final max aiming - - - ////////////////////////////////////////// - // Start by calculating the CTH cap, the absolute maximum CTH a shooter can get. - iCombinedSkill = ( gGameCTHConstants.AIM_EXP * bExperience * 10 ); - iCombinedSkill += ( gGameCTHConstants.AIM_MARKS * bMarksmanship ); - iCombinedSkill += ( gGameCTHConstants.AIM_WIS * bWisdom ); - iCombinedSkill += ( gGameCTHConstants.AIM_DEX * bDexterity ); - - iCombinedSkill /= (gGameCTHConstants.AIM_EXP + gGameCTHConstants.AIM_MARKS + gGameCTHConstants.AIM_WIS + gGameCTHConstants.AIM_DEX); - - INT32 uiCap = (INT32)iCombinedSkill; - // Add percent-based modifier from the gun and its attachments - INT32 uimoda = (INT32)(uiCap * GetObjectModifier( pSoldier, pInHand, stance, ITEMMODIFIER_PERCENTCAP )) / 100; - INT32 uimodb = (INT32)(uiCap * GetObjectModifier( pSoldier, pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight, ITEMMODIFIER_PERCENTCAP )) / 100; - uiCap += (INT32)((gGameExternalOptions.ubProneModifierPercentage * uimoda + (100 - gGameExternalOptions.ubProneModifierPercentage) * uimodb)/100); - - // Add bonuses from Sniper Skill. Applies only when using a scope at or above its "best" range. - INT16 sDifference = 99 - uiCap; - INT16 sSniperSkillBonus = 0; - // SANDRO - added support to Throwing trait - if ( uiCap < gGameExternalOptions.ubMaximumCTH && Item[ usInHand ].usItemClass == IC_THROWING_KNIFE ) - { - if( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( pSoldier, THROWING_NT ) ) - { - uiCap += (INT16)((sDifference * gSkillTraitValues.ubTHBladesCtHBonusPerClick*3) / gGameExternalOptions.ubMaximumCTH); - //uiCap += gSkillTraitValues.ubTHBladesCtHBonusPerClick*2; - } - } - else if ( !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) - { - if (uiCap < gGameExternalOptions.ubMaximumCTH && iScopeMagFactor > 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 uiCap 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 ) - sSniperSkillBonus += (INT16)((sDifference * gSkillTraitValues.ubGSAimingBonusPerClick * 2) / gGameExternalOptions.ubMaximumCTH); - else - sSniperSkillBonus += (INT16)((sDifference * gSkillTraitValues.ubSNAimingBonusPerClick * 2) / gGameExternalOptions.ubMaximumCTH); - } - else - sSniperSkillBonus += (INT16)((sDifference * gGameCTHConstants.AIM_SNIPER_SKILL * 2) / gGameExternalOptions.ubMaximumCTH); - sDifference -= sSniperSkillBonus; - } - - uiCap += sSniperSkillBonus; - } - } - // Make sure cap is within limits - uiCap = __max(uiCap, __max(0, iChance)); - uiCap = __min(uiCap, 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 = uiCap - iChance; - - iMaxAimBonus = (INT32)((iMaxAimBonus * (100+iAimModifier)) / 100); - iMaxAimBonus = __max(0, iMaxAimBonus); // can't get less than 0 points for aiming... - - // factor in scopes under their range - if ( !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) - { - if (iScopeMagFactor > 1.0 && iRange < (INT32)(uiBestScopeRange * gGameCTHConstants.AIM_TOO_CLOSE_THRESHOLD)) - { - // Calculate how much penalty this scope should give at this range - FLOAT dScopePenaltyRatio = ((FLOAT)uiBestScopeRange * gGameCTHConstants.AIM_TOO_CLOSE_THRESHOLD / (FLOAT)iRange); - INT32 iScopePenalty = (INT32)(dScopePenaltyRatio * gGameCTHConstants.AIM_TOO_CLOSE_SCOPE * (iScopeMagFactor /2)); - iMaxAimBonus += iScopePenalty; - } - else if (iScopeMagFactor == 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); - iMaxAimBonus += iScopePenalty; - } - - 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 = GetObjectModifier( pSoldier, pInHand, stance, ITEMMODIFIER_FLATAIM ); - INT32 modb = GetObjectModifier( pSoldier, pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight, ITEMMODIFIER_FLATAIM ); - 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. - iChance = __max(iChance + (INT32)iAimPoints, iChance); - iChance = __min(iChance, (INT32)uiCap); - } - - // Impose global limits. - // Flugente: backgrounds - iChance = min(iChance, min(100, gGameExternalOptions.ubMaximumCTH + (UINT8)(pSoldier->GetBackgroundValue(BG_PERC_CTH_MAX))) ); - iChance = __max(iChance, gGameExternalOptions.ubMinimumCTH); - - return (iChance); -} -////////////////////////////////////////////////////////////////////////////////// -// END OLD VERSION OF NCTH CALCULATION -////////////////////////////////////////////////////////////////////////////////// -} - -/* - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun")); - - - if ( AM_A_ROBOT( pSoldier ) ) - { - SOLDIERTYPE * pSoldier2; - pSoldier2 = pSoldier->GetRobotController( ); - if ( pSoldier2 ) - { - uiExperience = EffectiveExpLevel( pSoldier2 ); - uiMarksmanship = EffectiveMarksmanship( pSoldier2 ); - uiDexterity = EffectiveDexterity( pSoldier2 );; - uiWisdom = EffectiveWisdom( pSoldier2 ); - } - } - - // calculate actual range (in units, 10 units = 1 tile) - iRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, sGridNo ); - - // If in burst mode, deduct points for change to hit for each shot after the first - if ( pSoldier->bDoBurst && pSoldier->bDoAutofire == 0 ) - { - // Snap: bipod may reduce burst penalty - iPenalty = GetBurstPenalty(pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE) - * (pSoldier->bDoBurst - 1); - - // halve the penalty for people with the autofire trait - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPS ) ) - { - iPenalty /= 2 * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPS ); - } - iChance -= iPenalty; - } - else if ( pSoldier->bDoAutofire > 0 ) - { - // Snap: bipod may reduce auto penalty - iPenalty = GetAutoPenalty(pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE) - * (pSoldier->bDoBurst - 1); - - // halve the penalty for people with the autofire trait - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPS ) ) - { - iPenalty /= 2 * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPS ); - } - iChance -= iPenalty; - // HEADROCK HAM 3.5: Store the penalty - sTotalAutofirePenalty = iPenalty; - } - - - // 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 ); - } - else - { - iMaxRange = CELL_X_SIZE; // one tile - } - - - // 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; - } - } - } - } - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun: after scope bonus - ubAimTime = %d, iSightRange = %d, iChance = %d ", ubAimTime, iSightRange, iChance)); - - UINT8 bLightLevel = LightTrueLevel(sGridNo, pSoldier->bTargetLevel); - // 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, iSightRange, 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)); - } - - if ( TANK( 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) ); - } - - if (iSightRange == 0) - { - // firing blind! - iChance -= AIM_PENALTY_BLIND; - } - - // 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; - UINT8 cnt; - //UINT16 iAutoPenaltySinceLastTracer; - iTracersFired = 0; - iBulletsPerTracer = gGameExternalOptions.ubNumBulletsPerTracer; - - // Calculate number of bullets left right before firing this bullet - if (fCalculateCTHDuringGunfire) - { - iBulletsLeft = (*pInHand)[0]->data.gun.ubGunShotsLeft + (pSoldier->bDoBurst - 1); - } - else - { - iBulletsLeft = (*pInHand)[0]->data.gun.ubGunShotsLeft; - } - - 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?! - - // 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 - UINT8 ubAutoPenaltySinceLastTracer = GetAutoPenalty(pInHand, gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE) * iBulletsSinceLastTracer; - if ( HAS_SKILL_TRAIT( pSoldier, AUTO_WEAPS ) ) - { - ubAutoPenaltySinceLastTracer /= 2 * NUM_SKILL_TRAITS( pSoldier, AUTO_WEAPS ); - } - // Add penalty to bonus. - //iBonus -= iAutoPenaltySinceLastTracer; - - /////////////////////////////////////////////////// - // HEADROCK HAM 3.5: Limit maximum bonus by range. - - 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; - } - } - - - pTarget = SimpleFindSoldier( sGridNo, pSoldier->bTargetLevel ); - if (pTarget != NULL) - { - // 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 ); - iChance -= iPenalty; - - // 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); - } - } - } - - - - // 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; - } - - DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcChanceToHitGun: ichance = %d",iChance)); - return (iChance); -}*/ - UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, UINT8 ubAimPos ) { if(UsingNewCTHSystem() == true) @@ -9173,59 +7934,56 @@ UINT32 AICalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTim // basic aperture that is equal for everyone FLOAT dBasicAperture = CalcBasicAperture( ); - if (gGameExternalOptions.fUseNewCTHCalculation) + // silversurfer: New functionality for iron sights - There have been many complaints that iron sights lose their usefulness + // very fast the farther the target is away. Setting IRON_SIGHT_PERFORMANCE_BONUS too high makes them overly powerful at + // close range. This experimental formula implements a curve that lowers dBasicAperture the farther the target is away. + // At 1 tile distance iBasicAperture will be the same as before. That's the common start. + if ( gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_USE_GRADIENT && dMagFactor <= 1.0 && !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) + + dBasicAperture = dBasicAperture * ( 1 / sqrt( d2DDistance / FLOAT(CELL_X_SIZE) ) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER + + (gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - 1) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER ); + + // iron sights can get a percentage bonus to make them overall better but only when not shooting from hip + if ( dMagFactor <= 1.0 && !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) + + dBasicAperture = dBasicAperture * (FLOAT)( (100 - gGameCTHConstants.IRON_SIGHT_PERFORMANCE_BONUS) / 100); + + // laser pointers can provide a percentage bonus to base aperture + INT32 iLaserRange = GetBestLaserRange( &(pSoldier->inv[pSoldier->ubAttackingHand]) ); + if ( iLaserRange > 0 + && ( gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) { - // silversurfer: New functionality for iron sights - There have been many complaints that iron sights lose their usefulness - // very fast the farther the target is away. Setting IRON_SIGHT_PERFORMANCE_BONUS too high makes them overly powerful at - // close range. This experimental formula implements a curve that lowers dBasicAperture the farther the target is away. - // At 1 tile distance iBasicAperture will be the same as before. That's the common start. - if ( gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_USE_GRADIENT && dMagFactor <= 1.0 && !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) + INT8 bLightLevel = LightTrueLevel(sGridNo, gsInterfaceLevel ); + INT32 iMaxLaserRange = ( iLaserRange*( 2*bLightLevel + 3*NORMAL_LIGHTLEVEL_NIGHT - 5*NORMAL_LIGHTLEVEL_DAY ) ) / ( 2 * ( NORMAL_LIGHTLEVEL_NIGHT - NORMAL_LIGHTLEVEL_DAY ) ); - dBasicAperture = dBasicAperture * ( 1 / sqrt( d2DDistance / FLOAT(CELL_X_SIZE) ) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - + (gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER - 1) / gGameCTHConstants.IRON_SIGHTS_MAX_APERTURE_MODIFIER ); - - // iron sights can get a percentage bonus to make them overall better but only when not shooting from hip - if ( dMagFactor <= 1.0 && !pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) - - dBasicAperture = dBasicAperture * (FLOAT)( (100 - gGameCTHConstants.IRON_SIGHT_PERFORMANCE_BONUS) / 100); - - // laser pointers can provide a percentage bonus to base aperture - INT32 iLaserRange = GetBestLaserRange( &(pSoldier->inv[pSoldier->ubAttackingHand]) ); - if ( iLaserRange > 0 - && ( gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0) ) + // laser only has effect when in range + if ( iMaxLaserRange > d2DDistance ) { - INT8 bLightLevel = LightTrueLevel(sGridNo, gsInterfaceLevel ); - INT32 iMaxLaserRange = ( iLaserRange*( 2*bLightLevel + 3*NORMAL_LIGHTLEVEL_NIGHT - 5*NORMAL_LIGHTLEVEL_DAY ) ) / ( 2 * ( NORMAL_LIGHTLEVEL_NIGHT - NORMAL_LIGHTLEVEL_DAY ) ); + FLOAT fLaserBonus = 0; + // which bonus do we want to apply? + if ( pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) + // shooting from hip + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP; + else if ( dMagFactor <= 1.0 ) + // using iron sights or other 1x sights + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON; + else + // must be using a scope + fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE; - // laser only has effect when in range - if ( iMaxLaserRange > d2DDistance ) + // light level influences how easy it is to spot the laser dot on the target + FLOAT fBrightnessModifier = (FLOAT)(bLightLevel) / (FLOAT)(NORMAL_LIGHTLEVEL_NIGHT); + + // laser fully efficient + if ( iLaserRange > d2DDistance ) + // apply full bonus + dBasicAperture = dBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier)) / 100); + else { - FLOAT fLaserBonus = 0; - // which bonus do we want to apply? - if ( pSoldier->IsValidAlternativeFireMode( ubAimTime, sGridNo ) ) - // shooting from hip - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP; - else if ( dMagFactor <= 1.0 ) - // using iron sights or other 1x sights - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON; - else - // must be using a scope - fLaserBonus = gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE; - - // light level influences how easy it is to spot the laser dot on the target - FLOAT fBrightnessModifier = (FLOAT)(bLightLevel) / (FLOAT)(NORMAL_LIGHTLEVEL_NIGHT); - - // laser fully efficient - if ( iLaserRange > d2DDistance ) - // apply full bonus - dBasicAperture = dBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier)) / 100); - else - { - // beyond BestLaserRange laser bonus drops linearly to 0 - FLOAT fEffectiveLaserRatio = (FLOAT)(iMaxLaserRange - d2DDistance) / (FLOAT)(iMaxLaserRange - iLaserRange); - // apply partial bonus - dBasicAperture = dBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier * fEffectiveLaserRatio)) / 100); - } + // beyond BestLaserRange laser bonus drops linearly to 0 + FLOAT fEffectiveLaserRatio = (FLOAT)(iMaxLaserRange - d2DDistance) / (FLOAT)(iMaxLaserRange - iLaserRange); + // apply partial bonus + dBasicAperture = dBasicAperture * (FLOAT)( (100 - (fLaserBonus * fBrightnessModifier * fEffectiveLaserRatio)) / 100); } } } @@ -12544,8 +11302,7 @@ void CalcMagFactorSimple( SOLDIERTYPE *pSoldier, FLOAT d2DDistance, INT16 bAimTi // With the reworked NCTH code we don't want to use iProjectionFactor anymore. // Instead we use the performance bonus if at least one bonus is != 0. Otherwise -> continue using Projection Factor. - if (gGameExternalOptions.fUseNewCTHCalculation - && ( gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE != 0 )) + if ( (gGameCTHConstants.LASER_PERFORMANCE_BONUS_HIP + gGameCTHConstants.LASER_PERFORMANCE_BONUS_IRON + gGameCTHConstants.LASER_PERFORMANCE_BONUS_SCOPE) != 0 ) iProjectionFactor = 1.0; else iProjectionFactor = CalcProjectionFactor(pSoldier, pWeapon, d2DDistance, (UINT8)bAimTime); @@ -12916,6 +11673,10 @@ FLOAT CalcNewChanceToHitBaseWeaponBonus(SOLDIERTYPE *pSoldier, INT32 sGridNo, IN // STANCE switch ( stance ) { + case ANIM_STAND: + fGunBaseDifficulty *= gGameCTHConstants.BASE_STANDING_STANCE; + break; + case ANIM_CROUCH: fGunBaseDifficulty *= gGameCTHConstants.BASE_CROUCHING_STANCE; break;