New option AI_DECISION_INFO enables extended logging for AI decisions.

Moved zombie AI into separate file ZombieDecideAction.cpp
New zombie AI:
- simplified code
- advancing using cover
- if zombie cannot reach any opponent, he tries to hide
- if zombie cannot reach opponent and cannot hide and he is under fire, he will retreat, seeking cover away from known opponents
- if zombie doesn't know enemy location, he will try to run to closest friend who knows enemy location
- new zombie AI has extended AI logging
More water splash sounds (Data\Sounds\Misc\Splash1.ogg .. SplashN.ogg).
CalcCoverValue: 
- allow moving into shallow water if it provides better position
- special calculations for zombies: zombie is very dangerous at close range
- if soldier has no gun, use different calculation based on distance instead of CTGT
FindBestNearbyCover: zombies only choose spots that provide sight cover.
Added functions for extended AI log.
ClosestReachableDisturbance:
- if soldier is zombie and he cannot climb, skip location
- zombies do not attack vehicles
- improved check to avoid choosing dying opponents
EstimatePathCostToLocation: return 0 if checking for zombie which cannot climb and path needs climbing.
RoamingRange: always max roaming range for zombies.
SightCoverAtSpot, ProneSightCoverAtSpot:
- improved max sight range calculation
- added option for unlimited sight range check
New AI functions:
- AIDirection
- AICheckIsSniper
- AICheckIsMarksman
- AICheckIsMedic
- AICheckIsMortarOperator
- AICheckIsGLOperator
- AICheckIsOfficer
- AICheckIsCommander
- AICheckIsMachinegunner
- AIGunInHandScoped
- AIGunScoped
- AIGunRange
- AIGunClass
- AIGunType
- AIGunDeadliness
- AIGunAmmo
- AIGunAutofireCapable
- FindObstacleNearSpot
- InSmoke
- CorpseWarning
- CorpseEnemyTeam
- CorpseMilitiaTeam
- AICheckUnderground
- AnyCoverAtSpot
- AICheckHasWeaponOfType
- AICheckHasGun
- AICheckShortWeaponRange
GetCorpseAtGridNo: additional check for valid gridno.
More zombie raise sounds (Data\Sounds\Misc\ZombieRaise1.ogg .. ZombieRaiseN.ogg).
GetCorpseRotFactor: improved code to return FLOAT value between 0 and 1.
New debug topic id TOPIC_DECISIONS for logging AI decisions.

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8760 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Sevenfm
2020-02-21 23:33:25 +00:00
parent 73cbc27ea4
commit 32482f20f6
24 changed files with 3296 additions and 310 deletions
+3
View File
@@ -988,6 +988,9 @@ void DeductPoints( SOLDIERTYPE *pSoldier, INT16 sAPCost, INT32 iBPCost, UINT8 ub
if ( pSoldier->usSoldierFlagMask & SOLDIER_COVERT_TEMPORARY_OVERT && pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_COVERTOPS_TEMPORARYOVERT_APS] > 0 )
pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_COVERTOPS_TEMPORARYOVERT_APS] = max( 0, (INT16)(pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_COVERTOPS_TEMPORARYOVERT_APS] - sAPCost) );
// sevenfm: indicate in realtime that soldier spent some action point this turn
pSoldier->usSoldierFlagMask2 |= SOLDIER_SPENT_AP;
pSoldier->StopChatting();
}
+45 -23
View File
@@ -60,10 +60,6 @@
class OBJECTTYPE;
class SOLDIERTYPE;
#define CORPSE_WARNING_MAX 5
#define CORPSE_WARNING_DIST 5
#define CORPSE_INDEX_OFFSET 10000
// Flugente: these are ini values now
@@ -501,7 +497,7 @@ INT32 AddRottingCorpse( ROTTING_CORPSE_DEFINITION *pCorpseDef )
uiDirectionUseFlag = ANITILE_USE_DIRECTION_FOR_START_FRAME;
// If we are a soecial type...
// If we are a special type...
switch( pCorpseDef->ubType )
{
case SMERC_FALL:
@@ -527,7 +523,7 @@ INT32 AddRottingCorpse( ROTTING_CORPSE_DEFINITION *pCorpseDef )
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
{
// OK, AS WE ADD, CHECK FOR TOD AND DECAY APPROPRIATELY
if ( ((GetWorldTotalMin( ) - pCorpse->def.uiTimeOfDeath) > gGameExternalOptions.usCorpseDelayUntilRotting) && (pCorpse->def.ubType < ROTTING_STAGE2) )
if ( ((GetWorldTotalMin( ) - pCorpse->def.uiTimeOfDeath) >= gGameExternalOptions.usCorpseDelayUntilRotting) && (pCorpse->def.ubType < ROTTING_STAGE2) )
{
if ( pCorpse->def.ubType <= FMERC_FALLF )
{
@@ -1693,17 +1689,17 @@ INT32 FindNearestAvailableGridNoForCorpse( ROTTING_CORPSE_DEFINITION *pDef, INT8
INT16 sTop, sBottom;
INT16 sLeft, sRight;
INT16 cnt1, cnt2, cnt3;
INT32 sGridNo;
INT32 uiRange, uiLowestRange = 999999;
INT32 sLowestGridNo=0;
INT32 leftmost;
INT32 sGridNo;
INT32 uiRange, uiLowestRange = 999999;
INT32 sLowestGridNo=0;
INT32 leftmost;
BOOLEAN fFound = FALSE;
SOLDIERTYPE soldier;
INT16 ubSaveNPCAPBudget;
UINT8 ubSaveNPCDistLimit;
INT16 ubSaveNPCAPBudget;
UINT8 ubSaveNPCDistLimit;
STRUCTURE_FILE_REF * pStructureFileRef = NULL;
CHAR8 zFilename[150];
UINT8 ubBestDirection=0;
CHAR8 zFilename[150];
UINT8 ubBestDirection=0;
BOOLEAN fSetDirection = FALSE;
cnt3 = 0;
@@ -1752,8 +1748,6 @@ INT32 FindNearestAvailableGridNoForCorpse( ROTTING_CORPSE_DEFINITION *pDef, INT8
//(use the fake soldier and the pathing settings)
FindBestPath( &soldier, GRIDSIZE, 0, WALKING, COPYREACHABLE, 0 );//dnl ch50 071009
uiLowestRange = 999999;
for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
{
leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
@@ -1847,12 +1841,15 @@ ROTTING_CORPSE *GetCorpseAtGridNo( INT32 sGridNo, INT8 bLevel )
// Get base....
pBaseStructure = FindBaseStructure( pStructure );
// Find base gridno...
sBaseGridNo = pBaseStructure->sGridNo;
if ( pBaseStructure != NULL )
{
return(FindCorpseBasedOnStructure( sBaseGridNo, bLevel, pBaseStructure ));
// Find base gridno...
sBaseGridNo = pBaseStructure->sGridNo;
if (!TileIsOutOfBounds(sBaseGridNo))
{
return(FindCorpseBasedOnStructure(sBaseGridNo, bLevel, pBaseStructure));
}
}
}
@@ -2692,6 +2689,7 @@ std::vector<INT16> GetCorpseIDsNearGridNo( INT32 sGridNo, INT8 bLevel, INT8 sRad
}
extern UNDERGROUND_SECTORINFO* FindUnderGroundSector( INT16 sMapX, INT16 sMapY, UINT8 bMapZ );
INT16 gsZombieRaiseSoundNum = -1;
// Flugente Zombies: resurrect zombies
void RaiseZombies( void )
@@ -2801,8 +2799,32 @@ void RaiseZombies( void )
#ifdef JA2TESTVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"A wave of zombies is created");
#endif
// Play sound
PlayJA2SampleFromFile( "Sounds\\zombie1.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
CHAR8 zFilename[512];
// prepare zombie raise sound
if (gsZombieRaiseSoundNum < 0)
{
gsZombieRaiseSoundNum = 0;
do
{
gsZombieRaiseSoundNum++;
sprintf(zFilename, "Sounds\\Misc\\ZombieRaise%d.ogg", gsZombieRaiseSoundNum);
} while (FileExists(zFilename));
gsZombieRaiseSoundNum--;
}
if (gsZombieRaiseSoundNum > 0)
{
sprintf(zFilename, "Sounds\\Misc\\ZombieRaise%d.ogg", Random(gsZombieRaiseSoundNum) + 1);
if (FileExists(zFilename))
{
//PlayJA2SampleFromFile(zFilename, RATE_11025, SoundVolume(MIDVOLUME, this->sGridNo), 1, SoundDir(this->sGridNo));
PlayJA2SampleFromFile(zFilename, RATE_11025, MIDVOLUME, 1, MIDDLEPAN);
}
}
else
{
// Play default sound
PlayJA2SampleFromFile("Sounds\\zombie1.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN);
}
UseCreatureMusic(TRUE); // Madd: music when zombies rise
#ifdef NEWMUSIC
@@ -3251,5 +3273,5 @@ FLOAT GetCorpseRotFactor( ROTTING_CORPSE* pCorpse )
if ( pCorpse->def.ubType == ROTTING_STAGE2 )
return 1.0f;
return (GetWorldTotalMin( ) - pCorpse->def.uiTimeOfDeath) / gGameExternalOptions.usCorpseDelayUntilRotting;
return (FLOAT)(min(gGameExternalOptions.usCorpseDelayUntilRotting, GetWorldTotalMin() - pCorpse->def.uiTimeOfDeath)) / gGameExternalOptions.usCorpseDelayUntilRotting;
}
+2
View File
@@ -4,6 +4,8 @@
#include "tile animation.h"
#define NUM_CORPSE_SHADES 17
#define CORPSE_WARNING_MAX 5
#define CORPSE_WARNING_DIST 5
enum RottingCorpseDefines
{
+11 -1
View File
@@ -16522,6 +16522,16 @@ BOOLEAN SOLDIERTYPE::IsAssassin( )
return FALSE;
}
BOOLEAN SOLDIERTYPE::CheckInitialAP(void)
{
if (this->bActionPoints < this->bInitialActionPoints || this->usSoldierFlagMask2 & SOLDIER_SPENT_AP)
{
return FALSE;
}
return TRUE;
}
UINT8 SOLDIERTYPE::GetMultiTurnAction( )
{
return usMultiTurnAction;
@@ -17679,7 +17689,7 @@ void SOLDIERTYPE::SoldierPropertyUpkeep( )
{
// these effects last only one turn
this->usSoldierFlagMask &= ~(SOLDIER_AIRDROP_TURN | SOLDIER_ASSAULT_BONUS | SOLDIER_RAISED_REDALERT);
this->usSoldierFlagMask2 &= ~SOLDIER_CONCEALINSERTION;
this->usSoldierFlagMask2 &= ~(SOLDIER_CONCEALINSERTION | SOLDIER_SPENT_AP);
// this looks bizarre, but is required
if ( this->usSoldierFlagMask2 & SOLDIER_CONCEALINSERTION_DISCOVERED )
+4
View File
@@ -425,6 +425,7 @@ enum
#define SOLDIER_SURGERY_BOOSTED 0x00040000 // we are a boosted performing surgery (e.g. by using up a blood bag)
#define SOLDIER_DRAG_SOUND 0x00080000 // played sound when started dragging
#define SOLDIER_SPENT_AP 0x00100000 // soldier has spent some AP this turn (including realtime)
#define SOLDIER_INTERROGATE_ALL 0x000001F8 // all interrogation flags
// ----------------------------------------------------------------
@@ -1854,6 +1855,9 @@ public:
// Flugente: are we an assassin?
BOOLEAN IsAssassin();
// sevenfm: service functions
BOOLEAN CheckInitialAP(void);
// Flugente: multi-turn actions
UINT8 GetMultiTurnAction();
void StartMultiTurnAction( UINT8 usActionType, INT32 asGridNo );