mirror of
https://github.com/1dot13/source.git
synced 2026-07-29 13:52:17 +02:00
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:
+8
-7
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1536,6 +1536,9 @@ typedef struct
|
||||
UINT8 ubAPSharedAmongPassengersAndVehicleMode;
|
||||
UINT8 ubAPSharedAmongPassengersAndVehicleScale;
|
||||
|
||||
// sevenfm: AI settings
|
||||
BOOLEAN fAITacticalRetreat;
|
||||
|
||||
} GAME_EXTERNAL_OPTIONS;
|
||||
|
||||
typedef struct
|
||||
|
||||
+198
-185
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user