New option AI_TACTICAL_RETREAT (false by default) controls tactical enemy retreat from battle.

Moved options AI_YELLOW_FLANKING, AI_EXTRA_SUPPRESSION, AI_NEW_MORALE and AI_BETTER_COVER to [Tactical AI Settings]
AI_NEW_MORALE:
- zombies always have high morale
- always low morale when soldier is blinded
- neutrals always have low morale even if they have guns, so they don't run into the heat of battle
- prevent hopeless morale when not under attack
UpdateAniTiles: also check EXPLOSV_CREATUREGAS (from merc05's fixes)

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8721 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Sevenfm
2019-12-30 08:45:08 +00:00
parent d02a4ddd7d
commit 470bae3c20
6 changed files with 225 additions and 205 deletions
+8 -7
View File
@@ -1077,13 +1077,7 @@ void LoadGameExternalOptions()
gGameExternalOptions.iChanceSayAnnoyingPhrase = iniReader.ReadInteger("Tactical Interface Settings","CHANCE_SAY_ANNOYING_PHRASE",100);
// New enemy AI for night battles
gGameExternalOptions.bNewTacticalAIBehavior = iniReader.ReadBoolean("Tactical Interface Settings","NEW_AI_TACTICAL",FALSE);
// sevenfm: AI tweaks and improvements
gGameExternalOptions.fAIYellowFlanking = iniReader.ReadBoolean("Tactical Interface Settings","AI_YELLOW_FLANKING",FALSE);
gGameExternalOptions.fAIExtraSuppression = iniReader.ReadBoolean("Tactical Interface Settings","AI_EXTRA_SUPPRESSION",FALSE);
gGameExternalOptions.fAINewMorale = iniReader.ReadBoolean("Tactical Interface Settings","AI_NEW_MORALE",FALSE);
gGameExternalOptions.fAIBetterCover = iniReader.ReadBoolean("Tactical Interface Settings","AI_BETTER_COVER",FALSE);
gGameExternalOptions.bNewTacticalAIBehavior = iniReader.ReadBoolean("Tactical Interface Settings","NEW_AI_TACTICAL",FALSE);
// Heahshot penalty
gGameExternalOptions.uShotHeadPenalty = iniReader.ReadFloat("Tactical Interface Settings","SHOT_HEAD_PENALTY",3,0,100);
@@ -2274,6 +2268,13 @@ void LoadGameExternalOptions()
// sevenfm: improved auto fast forwarding
gGameExternalOptions.fImprovedAutoFastForward = iniReader.ReadBoolean("Clock Settings","IMPROVED_AUTO_FAST_FORWARD", TRUE);
// sevenfm: tactical AI
gGameExternalOptions.fAITacticalRetreat = iniReader.ReadBoolean("Tactical AI Settings", "AI_TACTICAL_RETREAT", FALSE);
gGameExternalOptions.fAIYellowFlanking = iniReader.ReadBoolean("Tactical AI Settings", "AI_YELLOW_FLANKING", FALSE);
gGameExternalOptions.fAIExtraSuppression = iniReader.ReadBoolean("Tactical AI Settings", "AI_EXTRA_SUPPRESSION", FALSE);
gGameExternalOptions.fAINewMorale = iniReader.ReadBoolean("Tactical AI Settings", "AI_NEW_MORALE", FALSE);
gGameExternalOptions.fAIBetterCover = iniReader.ReadBoolean("Tactical AI Settings", "AI_BETTER_COVER", FALSE);
}
+3
View File
@@ -1536,6 +1536,9 @@ typedef struct
UINT8 ubAPSharedAmongPassengersAndVehicleMode;
UINT8 ubAPSharedAmongPassengersAndVehicleScale;
// sevenfm: AI settings
BOOLEAN fAITacticalRetreat;
} GAME_EXTERNAL_OPTIONS;
typedef struct
+198 -185
View File
@@ -2135,47 +2135,47 @@ BOOLEAN InLightAtNight( INT32 sGridNo, INT8 bLevel )
INT8 CalcMorale(SOLDIERTYPE *pSoldier)
{
UINT32 uiLoop, uiLoop2;
INT32 iOurTotalThreat = 0, iTheirTotalThreat = 0;
INT16 sOppThreatValue, sFrndThreatValue, sMorale;
INT32 iPercent;
INT8 bMostRecentOpplistValue;
INT8 bMoraleCategory;
UINT8 *pSeenOpp; //,*friendOlPtr;
INT8 *pbPersOL, *pbPublOL;
SOLDIERTYPE *pOpponent,*pFriend;
UINT32 uiLoop, uiLoop2;
INT32 iOurTotalThreat = 0, iTheirTotalThreat = 0;
INT16 sOppThreatValue, sFrndThreatValue, sMorale;
INT32 iPercent;
INT8 bMostRecentOpplistValue;
INT8 bMoraleCategory;
UINT8 *pSeenOpp; //,*friendOlPtr;
INT8 *pbPersOL, *pbPublOL;
SOLDIERTYPE *pOpponent, *pFriend;
// sevenfm: use new calculation:
if( gGameExternalOptions.fAINewMorale )
if (gGameExternalOptions.fAINewMorale)
{
return CalcMoraleNew( pSoldier );
return CalcMoraleNew(pSoldier);
}
// if army guy has NO weapons left then panic!
if ( pSoldier->bTeam == ENEMY_TEAM )
{
if ( FindAIUsableObjClass( pSoldier, IC_WEAPON ) == NO_SLOT )
// if army guy has NO weapons left then panic!
if (pSoldier->bTeam == ENEMY_TEAM)
{
return( MORALE_HOPELESS );
if (FindAIUsableObjClass(pSoldier, IC_WEAPON) == NO_SLOT)
{
return(MORALE_HOPELESS);
}
}
}
// hang pointers to my personal opplist, my team's public opplist, and my
// list of previously seen opponents
pSeenOpp = (UINT8 *) &(gbSeenOpponents[pSoldier->ubID][0]);
// hang pointers to my personal opplist, my team's public opplist, and my
// list of previously seen opponents
pSeenOpp = (UINT8 *)&(gbSeenOpponents[pSoldier->ubID][0]);
// loop through every one of my possible opponents
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
pOpponent = MercSlots[ uiLoop ];
// loop through every one of my possible opponents
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
pOpponent = MercSlots[uiLoop];
// if this merc is inactive, at base, on assignment, dead, unconscious
if (!pOpponent || (pOpponent->stats.bLife < OKLIFE))
continue; // next merc
// if this merc is inactive, at base, on assignment, dead, unconscious
if (!pOpponent || (pOpponent->stats.bLife < OKLIFE))
continue; // next merc
// if this merc is neutral/on same side, he's not an opponent, skip him!
if ( CONSIDERED_NEUTRAL( pSoldier, pOpponent ) || (pSoldier->bSide == pOpponent->bSide))
continue; // next merc
// if this merc is neutral/on same side, he's not an opponent, skip him!
if (CONSIDERED_NEUTRAL(pSoldier, pOpponent) || (pSoldier->bSide == pOpponent->bSide))
continue; // next merc
// Special stuff for Carmen the bounty hunter
if (pSoldier->aiData.bAttitude == ATTACKSLAYONLY && pOpponent->ubProfile != SLAY)
@@ -2183,130 +2183,130 @@ INT8 CalcMorale(SOLDIERTYPE *pSoldier)
continue; // next opponent
}
pbPersOL = pSoldier->aiData.bOppList + pOpponent->ubID;
pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pOpponent->ubID;
pSeenOpp = (UINT8 *)gbSeenOpponents[pSoldier->ubID] + pOpponent->ubID;
pbPersOL = pSoldier->aiData.bOppList + pOpponent->ubID;
pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pOpponent->ubID;
pSeenOpp = (UINT8 *)gbSeenOpponents[pSoldier->ubID] + pOpponent->ubID;
// if this opponent is unknown to me personally AND unknown to my team, too
if ((*pbPersOL == NOT_HEARD_OR_SEEN) && (*pbPublOL == NOT_HEARD_OR_SEEN))
{
// if I have never seen him before anywhere in this sector, either
if (!(*pSeenOpp))
continue; // next merc
// have seen him in the past, so he remains something of a threat
bMostRecentOpplistValue = 0; // uses the free slot for 0 opplist
}
else // decide which opplist is more current
{
// if personal knowledge is more up to date or at least equal
if ((gubKnowledgeValue[*pbPublOL - OLDEST_HEARD_VALUE][*pbPersOL - OLDEST_HEARD_VALUE] > 0) || (*pbPersOL == *pbPublOL))
bMostRecentOpplistValue = *pbPersOL; // use personal
else
bMostRecentOpplistValue = *pbPublOL; // use public
}
iPercent = ThreatPercent[bMostRecentOpplistValue - OLDEST_HEARD_VALUE];
sOppThreatValue = (iPercent * CalcManThreatValue(pOpponent,pSoldier->sGridNo,FALSE,pSoldier)) / 100;
//sprintf(tempstr,"Known opponent %s, opplist status %d, percent %d, threat = %d",
// ExtMen[pOpponent->ubID].name,ubMostRecentOpplistValue,ubPercent,sOppThreatValue);
//PopMessage(tempstr);
// ADD this to their running total threatValue (decreases my MORALE)
iTheirTotalThreat += sOppThreatValue;
//NumMessage("Their TOTAL threat now = ",sTheirTotalThreat);
// NOW THE FUN PART: SINCE THIS OPPONENT IS KNOWN TO ME IN SOME WAY,
// ANY FRIENDS OF MINE THAT KNOW ABOUT HIM BOOST MY MORALE. SO, LET'S GO
// THROUGH THEIR PERSONAL OPPLISTS AND CHECK WHICH OF MY FRIENDS KNOW
// SOMETHING ABOUT HIM AND WHAT THEIR THREAT VALUE TO HIM IS.
for (uiLoop2 = 0; uiLoop2 < guiNumMercSlots; uiLoop2++)
{
pFriend = MercSlots[ uiLoop2 ];
// if this merc is inactive, at base, on assignment, dead, unconscious
if (!pFriend || (pFriend->stats.bLife < OKLIFE))
continue; // next merc
// if this merc is not on my side, then he's NOT one of my friends
// WE CAN'T AFFORD TO CONSIDER THE ENEMY OF MY ENEMY MY FRIEND, HERE!
// ONLY IF WE ARE ACTUALLY OFFICIALLY CO-OPERATING TOGETHER (SAME SIDE)
if ( pFriend->aiData.bNeutral && !( pSoldier->ubCivilianGroup != NON_CIV_GROUP && pSoldier->ubCivilianGroup == pFriend->ubCivilianGroup ) )
// if this opponent is unknown to me personally AND unknown to my team, too
if ((*pbPersOL == NOT_HEARD_OR_SEEN) && (*pbPublOL == NOT_HEARD_OR_SEEN))
{
continue; // next merc
// if I have never seen him before anywhere in this sector, either
if (!(*pSeenOpp))
continue; // next merc
// have seen him in the past, so he remains something of a threat
bMostRecentOpplistValue = 0; // uses the free slot for 0 opplist
}
else // decide which opplist is more current
{
// if personal knowledge is more up to date or at least equal
if ((gubKnowledgeValue[*pbPublOL - OLDEST_HEARD_VALUE][*pbPersOL - OLDEST_HEARD_VALUE] > 0) || (*pbPersOL == *pbPublOL))
bMostRecentOpplistValue = *pbPersOL; // use personal
else
bMostRecentOpplistValue = *pbPublOL; // use public
}
if ( pSoldier->bSide != pFriend->bSide )
continue; // next merc
iPercent = ThreatPercent[bMostRecentOpplistValue - OLDEST_HEARD_VALUE];
// THIS TEST IS INVALID IF A COMPUTER-TEAM IS PLAYING CO-OPERATIVELY
// WITH A NON-COMPUTER TEAM SINCE THE OPPLISTS INVOLVED ARE NOT
// UP-TO-DATE. THIS SITUATION IS CURRENTLY NOT POSSIBLE IN HTH/DG.
sOppThreatValue = (iPercent * CalcManThreatValue(pOpponent, pSoldier->sGridNo, FALSE, pSoldier)) / 100;
// ALSO NOTE THAT WE COUNT US AS OUR (BEST) FRIEND FOR THESE CALCULATIONS
//sprintf(tempstr,"Known opponent %s, opplist status %d, percent %d, threat = %d",
// ExtMen[pOpponent->ubID].name,ubMostRecentOpplistValue,ubPercent,sOppThreatValue);
//PopMessage(tempstr);
// ADD this to their running total threatValue (decreases my MORALE)
iTheirTotalThreat += sOppThreatValue;
//NumMessage("Their TOTAL threat now = ",sTheirTotalThreat);
// NOW THE FUN PART: SINCE THIS OPPONENT IS KNOWN TO ME IN SOME WAY,
// ANY FRIENDS OF MINE THAT KNOW ABOUT HIM BOOST MY MORALE. SO, LET'S GO
// THROUGH THEIR PERSONAL OPPLISTS AND CHECK WHICH OF MY FRIENDS KNOW
// SOMETHING ABOUT HIM AND WHAT THEIR THREAT VALUE TO HIM IS.
for (uiLoop2 = 0; uiLoop2 < guiNumMercSlots; uiLoop2++)
{
pFriend = MercSlots[uiLoop2];
// if this merc is inactive, at base, on assignment, dead, unconscious
if (!pFriend || (pFriend->stats.bLife < OKLIFE))
continue; // next merc
// if this merc is not on my side, then he's NOT one of my friends
// WE CAN'T AFFORD TO CONSIDER THE ENEMY OF MY ENEMY MY FRIEND, HERE!
// ONLY IF WE ARE ACTUALLY OFFICIALLY CO-OPERATING TOGETHER (SAME SIDE)
if (pFriend->aiData.bNeutral && !(pSoldier->ubCivilianGroup != NON_CIV_GROUP && pSoldier->ubCivilianGroup == pFriend->ubCivilianGroup))
{
continue; // next merc
}
if (pSoldier->bSide != pFriend->bSide)
continue; // next merc
// THIS TEST IS INVALID IF A COMPUTER-TEAM IS PLAYING CO-OPERATIVELY
// WITH A NON-COMPUTER TEAM SINCE THE OPPLISTS INVOLVED ARE NOT
// UP-TO-DATE. THIS SITUATION IS CURRENTLY NOT POSSIBLE IN HTH/DG.
// ALSO NOTE THAT WE COUNT US AS OUR (BEST) FRIEND FOR THESE CALCULATIONS
// subtract HEARD_2_TURNS_AGO (which is negative) to make values start at 0 and
// be positive otherwise
iPercent = ThreatPercent[pFriend->aiData.bOppList[pOpponent->ubID] - OLDEST_HEARD_VALUE];
iPercent = ThreatPercent[pFriend->aiData.bOppList[pOpponent->ubID] - OLDEST_HEARD_VALUE];
// reduce the percentage value based on how far away they are from the enemy, if they only hear him
if ( pFriend->aiData.bOppList[ pOpponent->ubID ] <= HEARD_LAST_TURN )
{
iPercent -= PythSpacesAway( pSoldier->sGridNo, pFriend->sGridNo ) * 2;
if ( iPercent <= 0 )
{
//ignore!
continue;
// reduce the percentage value based on how far away they are from the enemy, if they only hear him
if (pFriend->aiData.bOppList[pOpponent->ubID] <= HEARD_LAST_TURN)
{
iPercent -= PythSpacesAway(pSoldier->sGridNo, pFriend->sGridNo) * 2;
if (iPercent <= 0)
{
//ignore!
continue;
}
}
sFrndThreatValue = (iPercent * CalcManThreatValue(pFriend, pOpponent->sGridNo, FALSE, pSoldier)) / 100;
//sprintf(tempstr,"Known by friend %s, opplist status %d, percent %d, threat = %d",
// ExtMen[pFriend->ubID].name,pFriend->aiData.bOppList[pOpponent->ubID],ubPercent,sFrndThreatValue);
//PopMessage(tempstr);
// ADD this to our running total threatValue (increases my MORALE)
// We multiply by sOppThreatValue to PRO-RATE this based on opponent's
// threat value to ME personally. Divide later by sum of them all.
iOurTotalThreat += sOppThreatValue * sFrndThreatValue;
}
}
sFrndThreatValue = (iPercent * CalcManThreatValue(pFriend,pOpponent->sGridNo,FALSE,pSoldier)) / 100;
//sprintf(tempstr,"Known by friend %s, opplist status %d, percent %d, threat = %d",
// ExtMen[pFriend->ubID].name,pFriend->aiData.bOppList[pOpponent->ubID],ubPercent,sFrndThreatValue);
//PopMessage(tempstr);
// ADD this to our running total threatValue (increases my MORALE)
// We multiply by sOppThreatValue to PRO-RATE this based on opponent's
// threat value to ME personally. Divide later by sum of them all.
iOurTotalThreat += sOppThreatValue * sFrndThreatValue;
}
// this could get slow if I have a lot of friends...
//KeepInterfaceGoing();
// this could get slow if I have a lot of friends...
//KeepInterfaceGoing();
}
// if they are no threat whatsoever
if (!iTheirTotalThreat)
sMorale = 500; // our morale is just incredible
else
// if they are no threat whatsoever
if (!iTheirTotalThreat)
sMorale = 500; // our morale is just incredible
else
{
// now divide sOutTotalThreat by sTheirTotalThreat to get the REAL value
iOurTotalThreat /= iTheirTotalThreat;
// now divide sOutTotalThreat by sTheirTotalThreat to get the REAL value
iOurTotalThreat /= iTheirTotalThreat;
// calculate the morale (100 is even, < 100 is us losing, > 100 is good)
sMorale = (INT16) ((100 * iOurTotalThreat) / iTheirTotalThreat);
// calculate the morale (100 is even, < 100 is us losing, > 100 is good)
sMorale = (INT16)((100 * iOurTotalThreat) / iTheirTotalThreat);
}
if (sMorale <= 25) // odds 1:4 or worse
bMoraleCategory = MORALE_HOPELESS;
else if (sMorale <= 50) // odds between 1:4 and 1:2
bMoraleCategory = MORALE_WORRIED;
else if (sMorale <= 150) // odds between 1:2 and 3:2
bMoraleCategory = MORALE_NORMAL;
else if (sMorale <= 300) // odds between 3:2 and 3:1
bMoraleCategory = MORALE_CONFIDENT;
else // odds better than 3:1
bMoraleCategory = MORALE_FEARLESS;
if (sMorale <= 25) // odds 1:4 or worse
bMoraleCategory = MORALE_HOPELESS;
else if (sMorale <= 50) // odds between 1:4 and 1:2
bMoraleCategory = MORALE_WORRIED;
else if (sMorale <= 150) // odds between 1:2 and 3:2
bMoraleCategory = MORALE_NORMAL;
else if (sMorale <= 300) // odds between 3:2 and 3:1
bMoraleCategory = MORALE_CONFIDENT;
else // odds better than 3:1
bMoraleCategory = MORALE_FEARLESS;
switch (pSoldier->aiData.bAttitude)
switch (pSoldier->aiData.bAttitude)
{
case DEFENSIVE: bMoraleCategory--; break;
case BRAVESOLO: bMoraleCategory += 2; break;
@@ -2317,44 +2317,44 @@ INT8 CalcMorale(SOLDIERTYPE *pSoldier)
}
// make idiot administrators much more aggressive
if ( pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT )
if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT)
{
bMoraleCategory += 2;
}
// if still full of energy
if (pSoldier->bBreath > 75)
bMoraleCategory++;
else
// if still full of energy
if (pSoldier->bBreath > 75)
bMoraleCategory++;
else
{
// if getting a bit low on breath
if (pSoldier->bBreath < 40)
bMoraleCategory--;
// if getting a bit low on breath
if (pSoldier->bBreath < 40)
bMoraleCategory--;
// if getting REALLY low on breath
if (pSoldier->bBreath < 10)
bMoraleCategory--;
// if getting REALLY low on breath
if (pSoldier->bBreath < 10)
bMoraleCategory--;
}
// if still very healthy
if (pSoldier->stats.bLife > 75)
bMoraleCategory++;
else
// if still very healthy
if (pSoldier->stats.bLife > 75)
bMoraleCategory++;
else
{
// if getting a bit low on life
if (pSoldier->stats.bLife < 40)
bMoraleCategory--;
// if getting a bit low on life
if (pSoldier->stats.bLife < 40)
bMoraleCategory--;
// if getting REALLY low on life
if (pSoldier->stats.bLife < 20)
bMoraleCategory--;
// if getting REALLY low on life
if (pSoldier->stats.bLife < 20)
bMoraleCategory--;
}
// if soldier is currently not under fire
if (!pSoldier->aiData.bUnderFire)
bMoraleCategory++;
// if soldier is currently not under fire
if (!pSoldier->aiData.bUnderFire)
bMoraleCategory++;
// if adjustments made it outside the allowed limits
@@ -2366,29 +2366,18 @@ INT8 CalcMorale(SOLDIERTYPE *pSoldier)
bMoraleCategory = MORALE_FEARLESS;
}
// if only 1/4 of side left, reduce morale
// and do this after we've capped all those other silly values
/*
if ( pSoldier->bTeam == ENEMY_TEAM && gTacticalStatus.Team[ ENEMY_TEAM ].bMenInSector <= gTacticalStatus.bOriginalSizeOfEnemyForce / 4 )
{
bMoraleCategory -= 2;
if (bMoraleCategory < MORALE_HOPELESS)
bMoraleCategory = MORALE_HOPELESS;
}
*/
// brave guys never get hopeless, at worst they get worried
// SANDRO - on Insane difficulty enemy morale cannot go below worried
if ( bMoraleCategory == MORALE_HOPELESS )
{
if ( pSoldier->aiData.bAttitude == BRAVESOLO || pSoldier->aiData.bAttitude == BRAVEAID || zDiffSetting[gGameOptions.ubDifficultyLevel].bEnemyMoraleWorried )
bMoraleCategory = MORALE_WORRIED;
}
if (bMoraleCategory == MORALE_HOPELESS)
{
if (pSoldier->aiData.bAttitude == BRAVESOLO || pSoldier->aiData.bAttitude == BRAVEAID || zDiffSetting[gGameOptions.ubDifficultyLevel].bEnemyMoraleWorried)
bMoraleCategory = MORALE_WORRIED;
}
#ifdef DEBUGDECISIONS
STR tempstr;
sprintf( tempstr, "Morale = %d (category %d)\n", pSoldier->aiData.bMorale,bMoraleCategory);
DebugAI (tempstr);
sprintf(tempstr, "Morale = %d (category %d)\n", pSoldier->aiData.bMorale, bMoraleCategory);
DebugAI(tempstr);
#endif
return(bMoraleCategory);
@@ -3390,15 +3379,33 @@ UINT8 CountNearbyFriends( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubDistance
// sevenfm: new AI morale calculation
INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
{
UINT32 uiLoop, uiLoop2;
INT32 iOurTotalThreat = 0, iTheirTotalThreat = 0;
INT16 sOppThreatValue, sFrndThreatValue, sMorale;
INT32 iPercent;
UINT32 uiLoop, uiLoop2;
INT32 iOurTotalThreat = 0, iTheirTotalThreat = 0;
INT16 sOppThreatValue, sFrndThreatValue, sMorale;
INT32 iPercent;
INT8 bMostRecentOpplistValue;
INT8 bMoraleCategory;
UINT8 *pSeenOpp; //,*friendOlPtr;
INT8 bMoraleCategory;
UINT8 *pSeenOpp; //,*friendOlPtr;
INT8 *pbPersOL, *pbPublOL;
SOLDIERTYPE *pOpponent,*pFriend;
SOLDIERTYPE *pOpponent, *pFriend;
// zombies always have high morale
if (pSoldier->IsZombie())
{
return MORALE_FEARLESS;
}
// sevenfm: retreat when blinded
if (pSoldier->bBlindedCounter > 0)
{
return MORALE_WORRIED;
}
// sevenfm: neutrals always have low AI morale even if they have weapons (so they run from enemy)
if (pSoldier->aiData.bNeutral)
{
return(MORALE_WORRIED);
}
// if army guy has NO weapons left then panic!
if ( pSoldier->bTeam == ENEMY_TEAM || !pSoldier->aiData.bNeutral )
@@ -3572,12 +3579,12 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
}
// if have good health
if( pSoldier->stats.bLife > pSoldier->stats.bLifeMax/2 )
if( pSoldier->stats.bLife > pSoldier->stats.bLifeMax / 2 )
{
bMoraleCategory++;
}
// bad health
if( pSoldier->stats.bLife < pSoldier->stats.bLifeMax/4 )
if( pSoldier->stats.bLife < pSoldier->stats.bLifeMax / 4 )
{
bMoraleCategory--;
}
@@ -3612,7 +3619,7 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
}
// if some friend hit enemy - increase morale
if( CountNearbyFriendsLastAttackHit(pSoldier, pSoldier->sGridNo, DAY_VISION_RANGE/4 ) > 0 )
if (CountNearbyFriendsLastAttackHit(pSoldier, pSoldier->sGridNo, TACTICAL_RANGE / 4) > 0)
{
bMoraleCategory++;
}
@@ -3620,7 +3627,13 @@ INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier)
// limit AI morale depending on morale and suppression shock
if( pSoldier->aiData.bShock )
{
bMoraleCategory = __min(bMoraleCategory, (20 + pSoldier->aiData.bMorale - 20*__min(3, pSoldier->aiData.bShock/5)) / 20);
bMoraleCategory = min(bMoraleCategory, (20 + pSoldier->aiData.bMorale - 20 * min(3, pSoldier->aiData.bShock / 5)) / 20);
}
// prevent hopeless morale when not under attack
if (bMoraleCategory == MORALE_HOPELESS && !pSoldier->aiData.bUnderFire)
{
bMoraleCategory = MORALE_WORRIED;
}
// if adjustments made it outside the allowed limits
+12 -10
View File
@@ -1245,15 +1245,15 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
INT32 FindSpotMaxDistFromOpponents(SOLDIERTYPE *pSoldier)
{
INT32 sGridNo;
INT32 sBestSpot = NOWHERE;
UINT32 uiLoop;
INT32 iThreatRange,iClosestThreatRange = 1500, iSpotClosestThreatRange;
INT32 sThreatLoc, sThreatGridNo[MAXMERCS];
UINT32 uiThreatCnt = 0;
INT32 iSearchRange;
INT32 sGridNo;
INT32 sBestSpot = NOWHERE;
UINT32 uiLoop;
INT32 iThreatRange, iClosestThreatRange = 1500, iSpotClosestThreatRange;
INT32 sThreatLoc, sThreatGridNo[MAXMERCS];
UINT32 uiThreatCnt = 0;
INT32 iSearchRange;
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
INT8 * pbPersOL,*pbPublOL, bEscapeDirection, bBestEscapeDirection = -1;
INT8 *pbPersOL, *pbPublOL, bEscapeDirection, bBestEscapeDirection = -1;
SOLDIERTYPE *pOpponent;
INT32 sOrigin;
INT32 iRoamRange;
@@ -1480,7 +1480,10 @@ INT32 FindSpotMaxDistFromOpponents(SOLDIERTYPE *pSoldier)
iSpotClosestThreatRange = 1500;
if ( pSoldier->bTeam == ENEMY_TEAM && GridNoOnEdgeOfMap( sGridNo, &bEscapeDirection ) && EscapeDirectionIsValid( &bEscapeDirection ) )
if (gGameExternalOptions.fAITacticalRetreat &&
pSoldier->bTeam == ENEMY_TEAM &&
GridNoOnEdgeOfMap(sGridNo, &bEscapeDirection) &&
EscapeDirectionIsValid(&bEscapeDirection))
{
// We can escape! This is better than anything else except a closer spot which we can
// cross over from.
@@ -1521,7 +1524,6 @@ INT32 FindSpotMaxDistFromOpponents(SOLDIERTYPE *pSoldier)
//NumMessage("New best grid = ",bestSpot);
}
}
}
gubNPCAPBudget = 0;
+3
View File
@@ -314,4 +314,7 @@ UINT8 CountSeenEnemiesLastTurn( SOLDIERTYPE *pSoldier );
#define MIN_FLANK_DIST_RED (DAY_VISION_RANGE/2)
#define MAX_FLANK_DIST_RED (VISION_RANGE + 20)
// sevenfm: distance for tactical AI checks, roughly equal to normal day vision range
#define TACTICAL_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO * 2)
#endif
+1 -3
View File
@@ -571,7 +571,7 @@ void UpdateAniTiles( )
ubExpType = Explosive[ Item[ (UINT16)pNode->uiUserData ].ubClassIndex ].ubType;
if ( ubExpType == EXPLOSV_TEARGAS || ubExpType == EXPLOSV_MUSTGAS ||
ubExpType == EXPLOSV_SMOKE || ubExpType == EXPLOSV_BURNABLEGAS || ubExpType == EXPLOSV_SIGNAL_SMOKE || ubExpType == EXPLOSV_SMOKE_DEBRIS )
ubExpType == EXPLOSV_SMOKE || ubExpType == EXPLOSV_BURNABLEGAS || ubExpType == EXPLOSV_SIGNAL_SMOKE || ubExpType == EXPLOSV_SMOKE_DEBRIS || ubExpType == EXPLOSV_CREATUREGAS)
{
// Do sound....
// PlayJA2Sample( AIR_ESCAPING_1, RATE_11025, SoundVolume( HIGHVOLUME, pNode->sGridNo ), 1, SoundDir( pNode->sGridNo ) );
@@ -584,9 +584,7 @@ void UpdateAniTiles( )
// Forfait any other animations this frame....
return;
}
}
}
else
{