diff --git a/Tactical/Morale.cpp b/Tactical/Morale.cpp index 9e6309fc..131f9b4d 100644 --- a/Tactical/Morale.cpp +++ b/Tactical/Morale.cpp @@ -170,7 +170,19 @@ INT8 GetMoraleModifier( SOLDIERTYPE * pSoldier ) { INT8 morale = 0; - if (pSoldier->flags.uiStatusFlags & SOLDIER_PC) + // sevenfm: use bMorale for both player and AI + if (pSoldier->aiData.bMorale > 50) + { + // give +1 at 55, +3 at 65, up to +5 at 95 and above + morale = (pSoldier->aiData.bMorale - 45) / 10; + } + else + { + // give penalties down to -20 at 0 (-2 at 45, -4 by 40...) + morale = (pSoldier->aiData.bMorale - 50) * 2 / 5; + } + + /*if (pSoldier->flags.uiStatusFlags & SOLDIER_PC) { if (pSoldier->aiData.bMorale > 50) { @@ -199,7 +211,7 @@ INT8 GetMoraleModifier( SOLDIERTYPE * pSoldier ) default: morale = 0; } - } + }*/ // Flugente: morale modifiers morale = max( morale, morale * pSoldier->GetMoraleModifier( ) ); diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index cbb7fc2f..9547f498 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -8755,13 +8755,8 @@ INT8 CalcSuppressionTolerance( SOLDIERTYPE * pSoldier ) INT8 bToleranceSkill; UINT8 ubMaxShock = gGameExternalOptions.ubMaxSuppressionShock; - if (pSoldier->flags.uiStatusFlags & SOLDIER_PC) - bToleranceMorale = pSoldier->aiData.bMorale; - else - bToleranceMorale = (20 + 80 * pSoldier->stats.bLife / max(OKLIFE, pSoldier->stats.bLifeMax) - 40 * min(ubMaxShock, pSoldier->aiData.bShock) / max(1, ubMaxShock)); - // limit base tolerance to 75% when having max morale and experience level - bToleranceSkill = (bToleranceMorale + 20 * pSoldier->stats.bExpLevel) / 4; + bToleranceSkill = (pSoldier->aiData.bMorale + 20 * pSoldier->stats.bExpLevel) / 4; // calculate tolerance as percent of max tolerance from INI bTolerance = gGameExternalOptions.ubSuppressionToleranceMax * bToleranceSkill / 100; } @@ -9020,27 +9015,6 @@ void HandleSuppressionFire( UINT8 ubTargetedMerc, UINT8 ubCausedAttacker ) else sFinalSuppressionEffectiveness = sFinalShooterDependentEffectiveness * gGameExternalOptions.usSuppressionEffectivenessAI / 100; - // INI-Controlled intensity. SuppressionEffectiveness acts as a percentage applied to the number of lost APs. - // To turn off the entire Suppression system, simply set the INI value to 0. (0% AP Loss) - // The default is obviously 100%. You can increase or decrease it, at will. - // PLEASE NOTE that AP loss governs ALL OTHER SUPPRESSION EFFECTS. - // sevenfm: commented out duplicated code: we don't want to apply ubFinalSuppressionEffectivness twice - // ubPointsLost = ( ubPointsLost * sFinalSuppressionEffectiveness ) / 100; - - // This is an upper cap for the number of APs we can lose per attack. - // sevenfm: commented out duplicated code: - /* - if (usLimitSuppressionAPsLostPerAttack > 0) - { - if (ubPointsLost > usLimitSuppressionAPsLostPerAttack) - { - // Flugente: eh.. wouldn't this _always_ be 255? I suspect this should be __min - //ubPointsLost = __max(255,(UINT8)usLimitSuppressionAPsLostPerAttack); - ubPointsLost = __min(255,(UINT8)usLimitSuppressionAPsLostPerAttack); - } - } - */ - // INI-Controlled intensity. SuppressionEffectiveness acts as a percentage applied to the number of lost APs. // To turn off the entire Suppression system, simply set the INI value to 0. (0% AP Loss) // The default is obviously 100%. You can increase or decrease it, at will. @@ -9134,7 +9108,18 @@ void HandleSuppressionFire( UINT8 ubTargetedMerc, UINT8 ubCausedAttacker ) { for ( ubLoop2 = 0; ubLoop2 < (sPointsLost / APBPConstants[AP_LOST_PER_MORALE_DROP]); ubLoop2++ ) { - HandleMoraleEvent( pSoldier, MORALE_SUPPRESSED, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); + // sevenfm: morale loss for AI soldiers + if (pSoldier->ubProfile == NO_PROFILE) + { + if (IS_MERC_BODY_TYPE(pSoldier)) + { + pSoldier->aiData.bMorale = max(20 + 2 * pSoldier->stats.bExpLevel, pSoldier->aiData.bMorale - 4); + } + } + else + { + HandleMoraleEvent(pSoldier, MORALE_SUPPRESSED, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ); + } // sevenfm: update ubLastMorale pSoldier->ubLastMorale++; } diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 9c8f1127..fbf04a3d 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -7967,6 +7967,18 @@ void SOLDIERTYPE::EVENT_BeginMercTurn( BOOLEAN fFromRealTime, INT32 iRealTimeCou // reduce the effects of any residual shock from past injuries by half this->aiData.bShock /= 2; + // sevenfm: increase morale for AI soldiers + if (this->ubProfile == NO_PROFILE && + !(this->flags.uiStatusFlags & SOLDIER_VEHICLE) && + !AM_A_ROBOT(this) && + !ARMED_VEHICLE(this) && + this->aiData.bShock == 0 && + !this->aiData.bUnderFire && + this->aiData.bMorale < 80 + 2 * this->stats.bExpLevel) + { + this->aiData.bMorale = __min(80 + 2 * this->stats.bExpLevel, this->aiData.bMorale + 2 + this->stats.bExpLevel / 5); + } + // if this person has heard a noise that hasn't been investigated if ( this->aiData.sNoiseGridno != NOWHERE ) { diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index e3ca328e..192b82a0 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -966,6 +966,12 @@ SOLDIERTYPE* TacticalCreateSoldier( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 * Soldier.uiAnimSubFlags |= SUB_ANIM_BIGGUYTHREATENSTANCE; } + // sevenfm: max morale for AI soldiers + if (Soldier.ubProfile == NO_PROFILE) + { + Soldier.aiData.bMorale = 60 + 2 * Soldier.stats.bExpLevel + Random(20); + } + //For inventory, look for any face class items that may be located in the big pockets and if found, move //that item to a face slot and clear the pocket! if( Soldier.bTeam != OUR_TEAM ) diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index f2abf0c6..27e2cdd9 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -2460,6 +2460,30 @@ BOOLEAN UseGunNCTH( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) OBJECTTYPE* pObjAttHand = pSoldier->GetUsedWeapon( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); UINT16 usUBItem = pSoldier->GetUsedWeaponNumber( &pSoldier->inv[ pSoldier->ubAttackingHand ] ); + // sevenfm: reduce shock when firing gun, for autofire shots - apply once + SOLDIERTYPE* pTarget = SimpleFindSoldier(sTargetGridNo, pSoldier->bTargetLevel); + if (gGameExternalOptions.fNewSuppressionCode && + Item[usUBItem].usItemClass == IC_GUN && + pTarget && + (pSoldier->bDoBurst == 1 || pSoldier->bDoBurst == 0)) + { + UINT16 noisefactor = GetPercentNoiseVolume(pObjHand); + UINT8 ubDamage = Weapon[usUBItem].ubImpact; + + INT8 bShotsToFire = 1; + if (pSoldier->bDoBurst) + { + bShotsToFire = pSoldier->bDoAutofire ? pSoldier->bDoAutofire : GetShotsPerBurst(pObjHand); + } + // calculate shock reduction value 0-50% + UINT8 ubShockReductPercent = (50 - 400 / (bShotsToFire + 8)) * noisefactor * Weapon[usUBItem].ubAttackVolume / (50 * 100); + if (pSoldier->IsValidSecondHandShot()) + ubShockReductPercent = ubShockReductPercent * 3 / 2; + ubShockReductPercent = __min(50, ubShockReductPercent); + + pSoldier->aiData.bShock -= pSoldier->aiData.bShock * ubShockReductPercent / 100; + } + // CALC MUZZLE SWAY if ( Item[ usUBItem ].usItemClass == IC_THROWING_KNIFE ) { @@ -3201,6 +3225,30 @@ BOOLEAN UseGun( SOLDIERTYPE *pSoldier , INT32 sTargetGridNo ) usItemNum = pSoldier->usAttackingWeapon; + // sevenfm: reduce shock when firing gun, for autofire shots - apply once + SOLDIERTYPE* pTarget = SimpleFindSoldier(sTargetGridNo, pSoldier->bTargetLevel); + if (gGameExternalOptions.fNewSuppressionCode && + Item[usUBItem].usItemClass == IC_GUN && + pTarget && + (pSoldier->bDoBurst == 1 || pSoldier->bDoBurst == 0)) + { + UINT16 noisefactor = GetPercentNoiseVolume(pObjUsed); + UINT8 ubDamage = Weapon[usUBItem].ubImpact; + + INT8 bShotsToFire = 1; + if (pSoldier->bDoBurst) + { + bShotsToFire = pSoldier->bDoAutofire ? pSoldier->bDoAutofire : GetShotsPerBurst(pObjUsed); + } + // calculate shock reduction value 0-50% + UINT8 ubShockReductPercent = (50 - 400 / (bShotsToFire + 8)) * noisefactor * Weapon[usUBItem].ubAttackVolume / (50 * 100); + if (pSoldier->IsValidSecondHandShot()) + ubShockReductPercent = ubShockReductPercent * 3 / 2; + ubShockReductPercent = __min(50, ubShockReductPercent); + + pSoldier->aiData.bShock -= pSoldier->aiData.bShock * ubShockReductPercent / 100; + } + // DEDUCT APs if (pSoldier->bDoBurst) { diff --git a/TacticalAI/AIUtils.cpp b/TacticalAI/AIUtils.cpp index 281aa5da..9d3ef320 100644 --- a/TacticalAI/AIUtils.cpp +++ b/TacticalAI/AIUtils.cpp @@ -3925,10 +3925,13 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier) } // limit AI morale when soldier is under heavy fire - if (pSoldier->ShockLevelPercent() > 75) + /*if (pSoldier->ShockLevelPercent() > 75) bMoraleCategory = min(bMoraleCategory, MORALE_NORMAL); else if (pSoldier->ShockLevelPercent() > 50) - bMoraleCategory = min(bMoraleCategory, MORALE_CONFIDENT); + bMoraleCategory = min(bMoraleCategory, MORALE_CONFIDENT);*/ + + // limit AI morale depending on morale and shock level + bMoraleCategory = min(bMoraleCategory, max(MORALE_WORRIED, ((pSoldier->aiData.bOrders == SEEKENEMY ? pSoldier->aiData.bMorale + 20 : pSoldier->aiData.bMorale) * 100 / (100 + pSoldier->ShockLevelPercent())) / 20)); // prevent hopeless morale when not under attack if (bMoraleCategory == MORALE_HOPELESS && !pSoldier->aiData.bUnderFire)