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
+1
View File
@@ -2295,6 +2295,7 @@ void LoadGameExternalOptions()
gGameExternalOptions.fAITacticalRetreat = iniReader.ReadBoolean("Tactical AI Settings", "AI_TACTICAL_RETREAT", FALSE);
gGameExternalOptions.fAIMovementMode = iniReader.ReadBoolean("Tactical AI Settings", "AI_MOVEMENT_MODE", TRUE);
gGameExternalOptions.fAIPathTweaks = iniReader.ReadBoolean("Tactical AI Settings", "AI_PATH_TWEAKS", TRUE);
gGameExternalOptions.fAIDecisionInfo = iniReader.ReadBoolean("Tactical AI Settings", "AI_DECISION_INFO", false, false);
}
+1
View File
@@ -1555,6 +1555,7 @@ typedef struct
BOOLEAN fAITacticalRetreat;
BOOLEAN fAIMovementMode;
BOOLEAN fAIPathTweaks;
BOOLEAN fAIDecisionInfo;
} GAME_EXTERNAL_OPTIONS;
+1
View File
@@ -3,6 +3,7 @@
const unsigned INVALID_TOPIC = 0xffff;
const unsigned TOPIC_DECISIONS = 25;
const unsigned TOPIC_MEMORY_MANAGER = 24;
const unsigned TOPIC_FILE_MANAGER = 23;
const unsigned TOPIC_DATABASE_MANAGER = 22;
+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 );
+1
View File
@@ -232,6 +232,7 @@ INT32 GoAsFarAsPossibleTowards(SOLDIERTYPE *pSoldier, INT32 sDesGrid, INT8 bActi
INT8 HeadForTheStairCase( SOLDIERTYPE * pSoldier );
BOOLEAN InSmoke(INT32 sGridNo, INT8 bLevel);
BOOLEAN InGas( SOLDIERTYPE *pSoldier, INT32 sGridNo );
BOOLEAN InGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo );
BOOLEAN InWaterGasOrSmoke( SOLDIERTYPE *pSoldier, INT32 sGridNo );
+196
View File
@@ -134,6 +134,191 @@ void DebugAI( STR szOutput )
#endif
}
STR szAction[] = {
"AI_ACTION_NONE",
"AI_ACTION_RANDOM_PATROL",
"AI_ACTION_SEEK_FRIEND",
"AI_ACTION_SEEK_OPPONENT",
"AI_ACTION_TAKE_COVER",
"AI_ACTION_GET_CLOSER",
"AI_ACTION_POINT_PATROL",
"AI_ACTION_LEAVE_WATER_GAS",
"AI_ACTION_SEEK_NOISE",
"AI_ACTION_ESCORTED_MOVE",
"AI_ACTION_RUN_AWAY",
"AI_ACTION_KNIFE_MOVE",
"AI_ACTION_APPROACH_MERC",
"AI_ACTION_TRACK",
"AI_ACTION_EAT",
"AI_ACTION_PICKUP_ITEM",
"AI_ACTION_SCHEDULE_MOVE",
"AI_ACTION_WALK",
"AI_ACTION_RUN",
"AI_ACTION_WITHDRAW",
"AI_ACTION_FLANK_LEFT",
"AI_ACTION_FLANK_RIGHT",
"AI_ACTION_MOVE_TO_CLIMB",
"AI_ACTION_CHANGE_FACING",
"AI_ACTION_CHANGE_STANCE",
"AI_ACTION_YELLOW_ALERT",
"AI_ACTION_RED_ALERT",
"AI_ACTION_CREATURE_CALL",
"AI_ACTION_PULL_TRIGGER",
"AI_ACTION_USE_DETONATOR",
"AI_ACTION_FIRE_GUN",
"AI_ACTION_TOSS_PROJECTILE",
"AI_ACTION_KNIFE_STAB",
"AI_ACTION_THROW_KNIFE",
"AI_ACTION_GIVE_AID",
"AI_ACTION_WAIT",
"AI_ACTION_PENDING_ACTION",
"AI_ACTION_DROP_ITEM",
"AI_ACTION_COWER",
"AI_ACTION_STOP_COWERING",
"AI_ACTION_OPEN_OR_CLOSE_DOOR",
"AI_ACTION_UNLOCK_DOOR",
"AI_ACTION_LOCK_DOOR",
"AI_ACTION_LOWER_GUN",
"AI_ACTION_ABSOLUTELY_NONE",
"AI_ACTION_CLIMB_ROOF",
"AI_ACTION_END_TURN",
"AI_ACTION_END_COWER_AND_MOVE",
"AI_ACTION_TRAVERSE_DOWN",
"AI_ACTION_OFFER_SURRENDER",
"AI_ACTION_RAISE_GUN",
"AI_ACTION_STEAL_MOVE",
"AI_ACTION_RELOAD_GUN",
"AI_ACTION_JUMP_WINDOW",
"AI_ACTION_FREE_PRISONER",
"AI_ACTION_USE_SKILL",
"AI_ACTION_DOCTOR",
"AI_ACTION_DOCTOR_SELF",
"AI_ACTION_SELFDETONATE",
"AI_ACTION_STOP_MEDIC"
};
// sevenfm
UINT32 guiAIStartCounter = 0, guiAILastCounter = 0;
//UINT8 gubAISelectedSoldier = NOBODY;
void DebugAI( INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction )
{
FILE* DebugFile;
CHAR8 msg[1024];
CHAR8 buf[1024];
if (!gGameExternalOptions.fAIDecisionInfo)
{
return;
}
memset(buf, 0, 1024 * sizeof(char));
/*if (bMsgType == AI_MSG_TOPIC && !gGameExternalOptions.fDecisionTopicInfo)
{
return;
}*/
if (bMsgType == AI_MSG_START)
{
guiAIStartCounter = GetJA2Clock();
guiAILastCounter = GetJA2Clock();
}
sprintf(msg, "");
if (pSoldier)
{
sprintf(buf, "[%d] (%d)", pSoldier->ubID, pSoldier->sGridNo);
strcat(msg, buf);
if (pSoldier->ubProfile != NO_PROFILE)
{
wcstombs(buf, pSoldier->GetName(), 1024 - 1);
strcat(msg, " ");
strcat(msg, buf);
}
}
strcat(msg, " ");
if (bAction >= 0)
{
sprintf(buf, " (total %d ms)", GetJA2Clock() - guiAIStartCounter);
strcat(msg, buf);
}
else
{
sprintf(buf, " (%d ms)", GetJA2Clock() - guiAILastCounter);
strcat(msg, buf);
}
if (bAction >= AI_ACTION_NONE && bAction <= AI_ACTION_LAST)
{
strcat(msg, " ");
strcat(msg, szAction[bAction]);
if (pSoldier)
{
sprintf(buf, " %d", pSoldier->aiData.usActionData);
strcat(msg, buf);
}
if (pSoldier && pSoldier->aiData.bNextAction != AI_ACTION_NONE)
{
strcat(msg, " ");
strcat(msg, szAction[pSoldier->aiData.bNextAction]);
sprintf(buf, " %d", pSoldier->aiData.usNextActionData);
strcat(msg, buf);
}
}
strcat(msg, " ");
strcat(msg, szOutput);
guiAILastCounter = GetJA2Clock();
DebugMsg(TOPIC_DECISIONS, DBG_LEVEL_3, szOutput);
if ((DebugFile = fopen("Logs\\AI_Decisions.txt", "a+t")) != NULL)
{
if (bMsgType == AI_MSG_START)
{
fputs("\n", DebugFile);
}
fputs(msg, DebugFile);
fputs("\n", DebugFile);
fclose(DebugFile);
}
// also log to individual file for selected soldier
sprintf(buf, "Logs\\AI_Decisions [%d].txt", pSoldier->ubID);
if ((DebugFile = fopen(buf, "a+t")) != NULL)
{
if (bMsgType == AI_MSG_START)
{
fputs("\n", DebugFile);
}
fputs(msg, DebugFile);
fputs("\n", DebugFile);
fclose(DebugFile);
}
}
BOOLEAN InitAI( void )
{
@@ -164,6 +349,17 @@ BOOLEAN InitAI( void )
}
#endif
// sevenfm: Clear the AI debug txt file to prevent it from getting huge
remove("Logs\\AI_Decisions.txt");
// remove all individual files
CHAR8 buf[1024];
for (UINT8 cnt = 0; cnt < TOTAL_SOLDIERS; cnt++)
{
sprintf(buf, "Logs\\AI_Decisions [%d].txt", cnt);
remove(buf);
}
return( TRUE );
}
+813 -127
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -15,6 +15,11 @@
class OBJECTTYPE;
class SOLDIERTYPE;
extern STR8 gStr8AlertStatus[];
extern STR8 gStr8Attitude[];
extern STR8 gStr8Orders[];
extern STR8 gStr8Team[];
extern STR8 gStr8Class[];
extern INT8 STRAIGHT; //lal
+97 -87
View File
@@ -46,6 +46,8 @@
extern BOOLEAN gfHiddenInterrupt;
extern BOOLEAN gfUseAlternateQueenPosition;
extern UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady, BOOLEAN fHipStance );
void LogDecideInfo(SOLDIERTYPE *pSoldier);
void LogKnowledgeInfo(SOLDIERTYPE *pSoldier);
// global status time counters to determine what takes the most time
@@ -68,6 +70,13 @@ INT8 ArmedVehicleDecideActionYellow(SOLDIERTYPE *pSoldier);
INT8 ArmedVehicleDecideActionRed(SOLDIERTYPE *pSoldier);
INT8 ArmedVehicleDecideActionBlack(SOLDIERTYPE *pSoldier);
STR8 gStr8AlertStatus[] = { "Green", "Yellow", "Red", "Black" };
STR8 gStr8Attitude[] = { "DEFENSIVE", "BRAVESOLO", "BRAVEAID", "CUNNINGSOLO", "CUNNINGAID", "AGGRESSIVE", "MAXATTITUDES", "ATTACKSLAYONLY" };
STR8 gStr8Orders[] = { "STATIONARY", "ONGUARD", "CLOSEPATROL", "FARPATROL", "POINTPATROL", "ONCALL", "SEEKENEMY", "RNDPTPATROL", "SNIPER" };
STR8 gStr8Team[] = { "OUR_TEAM", "ENEMY_TEAM", "CREATURE_TEAM", "MILITIA_TEAM", "CIV_TEAM", "LAST_TEAM", "PLAYER_PLAN", "LAN_TEAM_ONE", "LAN_TEAM_TWO", "LAN_TEAM_THREE", "LAN_TEAM_FOUR" };
STR8 gStr8Class[] = { "SOLDIER_CLASS_NONE", "SOLDIER_CLASS_ADMINISTRATOR", "SOLDIER_CLASS_ELITE", "SOLDIER_CLASS_ARMY", "SOLDIER_CLASS_GREEN_MILITIA", "SOLDIER_CLASS_REG_MILITIA", "SOLDIER_CLASS_ELITE_MILITIA", "SOLDIER_CLASS_CREATURE", "SOLDIER_CLASS_MINER", "SOLDIER_CLASS_ZOMBIE", "SOLDIER_CLASS_TANK", "SOLDIER_CLASS_JEEP", "SOLDIER_CLASS_BANDIT" };
STR8 gStr8Knowledge[] = { "HEARD_3_TURNS_AGO", "HEARD_2_TURNS_AGO", "HEARD_LAST_TURN", "HEARD_THIS_TURN", "NOT_HEARD_OR_SEEN", "SEEN_CURRENTLY", "SEEN_THIS_TURN", "SEEN_LAST_TURN", "SEEN_2_TURNS_AGO", "SEEN_3_TURNS_AGO" };
void DoneScheduleAction( SOLDIERTYPE * pSoldier )
{
pSoldier->aiData.fAIFlags &= (~AI_CHECK_SCHEDULE);
@@ -695,10 +704,6 @@ INT8 DecideActionGreen(SOLDIERTYPE *pSoldier)
STR16 tempstr;
#endif
// Flugente: to prevent an accidental call
if ( pSoldier->IsZombie() )
return( ZombieDecideActionGreen(pSoldier) );
if ( ARMED_VEHICLE( pSoldier ) )
return ArmedVehicleDecideActionGreen( pSoldier );
@@ -1547,10 +1552,6 @@ INT8 DecideActionYellow(SOLDIERTYPE *pSoldier)
STR16 tempstr;
#endif
// Flugente: to prevent an accidental call
if ( pSoldier->IsZombie() )
return( ZombieDecideActionYellow(pSoldier) );
if (fCivilian || (gGameExternalOptions.fAllNamedNpcsDecideAction && pSoldier->ubProfile != NO_PROFILE))
{
if (pSoldier->flags.uiStatusFlags & SOLDIER_COWERING)
@@ -2467,10 +2468,6 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
(pSoldier->aiData.bNeutral && gTacticalStatus.fCivGroupHostile[pSoldier->ubCivilianGroup] == CIV_GROUP_NEUTRAL) ||
(pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= CRIPPLECIV) ) );
// Flugente: to prevent an accidental call
if ( pSoldier->IsZombie() )
return ZombieDecideActionRed(pSoldier);
// WANNE: Headrock informed me that I should remove that because it needs a lot of CPU!
// HEADROCK HAM B2.7: Calculate the overall tactical situation
//INT16 ubOverallTacticalSituation = AssessTacticalSituation(pSoldier->bSide);
@@ -2484,7 +2481,7 @@ INT8 DecideActionRed(SOLDIERTYPE *pSoldier)
return(AI_ACTION_NONE);
}
fProneSightCover = ProneSightCoverAtSpot(pSoldier, pSoldier->sGridNo);
fProneSightCover = ProneSightCoverAtSpot(pSoldier, pSoldier->sGridNo, FALSE);
if( !fProneSightCover || pSoldier->aiData.bUnderFire )
{
fDangerousSpot = TRUE;
@@ -4534,10 +4531,6 @@ INT16 ubMinAPCost;
#endif
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DecideActionBlack: soldier = %d, orders = %d, attitude = %d",pSoldier->ubID,pSoldier->aiData.bOrders,pSoldier->aiData.bAttitude));
// Flugente: to prevent an accidental call
if ( pSoldier->IsZombie() )
return( ZombieDecideActionBlack(pSoldier) );
ATTACKTYPE BestShot, BestThrow, BestStab ,BestAttack;//dnl ch69 150913
BOOLEAN fCivilian = (PTR_CIVILIAN && (pSoldier->ubCivilianGroup == NON_CIV_GROUP || pSoldier->aiData.bNeutral || (pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= CRIPPLECIV) ) );
UINT8 ubBestStance = 1;
@@ -6697,51 +6690,6 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier )
}
}
#if 0
else
{
if (pSoldier->bOppCnt > 0)
{
pSoldier->aiData.bAlertStatus = STATUS_BLACK; // opponent(s) in sight
}
else
{
pSoldier->aiData.bAlertStatus = STATUS_RED; // enemy sector
/*
// wow, JA1 stuff...
// good guys all have a built-in, magic, "enemy detecting radar"...
if (Status.enemies)
pSoldier->aiData.bAlertStatus = STATUS_RED; // enemy sector
else
{
pSoldier->aiData.bAlertStatus = STATUS_GREEN; // secured sector
// if he just dropped back from alert status, and it's a GUARD
if ((oldStatus >= STATUS_RED) && (pSoldier->manCategory == MAN_GUARD))
{
if (TileIsOutOfBounds(pSoldier->whereIWas)) // not assigned to any trees
// FUTURE ENHANCEMENT: Look for unguarded trees with tappers
pSoldier->orders = ONCALL;
else // assigned to trees
// FUTURE ENHANCEMENT: If his tree is now tapperless, go ONCALL
pSoldier->orders = CLOSEPATROL; // go back to his tree area
// turn off any existing bypass to Green and its "hyper-activity"
pSoldier->bypassToGreen = FALSE;
// turn off the "inTheWay" flag, may have been set during TurnBased
pSoldier->inTheWay = FALSE;
// make the guard put his gun away if he has it drawn
HandleNoMoreTarget(pSoldier);
}
}
*/
}
}
#endif
if ( gTacticalStatus.bBoxingState == NOT_BOXING )
{
@@ -6806,7 +6754,7 @@ void DecideAlertStatus( SOLDIERTYPE *pSoldier )
// ------------------------------ ZOMBIE AI --------------------------
INT8 ZombieDecideActionGreen(SOLDIERTYPE *pSoldier)
/*INT8 ZombieDecideActionGreen(SOLDIERTYPE *pSoldier)
{
DOUBLE iChance, iSneaky = 10;
INT8 bInWater;
@@ -7206,9 +7154,9 @@ INT8 ZombieDecideActionGreen(SOLDIERTYPE *pSoldier)
// for realtime, regular AI guys will use a standard wait set outside of here
pSoldier->aiData.usActionData = NOWHERE;
return(AI_ACTION_NONE);
}
}*/
INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier)
/*INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier)
{
UINT8 ubNoiseDir;
INT32 sNoiseGridNo;
@@ -7440,10 +7388,10 @@ INT8 ZombieDecideActionYellow(SOLDIERTYPE *pSoldier)
// by default, if everything else fails, just stands in place without turning
pSoldier->aiData.usActionData = NOWHERE;
return(AI_ACTION_NONE);
}
}*/
INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier)
/*INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier)
{
INT32 iDummy;
INT32 iChance,sClosestOpponent,sClosestFriend;
@@ -8072,14 +8020,6 @@ INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier)
// if not in combat or under fire, and we COULD have moved, just chose not to
if ( (pSoldier->aiData.bAlertStatus != STATUS_BLACK) && !pSoldier->aiData.bUnderFire && ubCanMove && (!gfTurnBasedAI || pSoldier->bActionPoints >= pSoldier->bInitialActionPoints) && ( TileIsOutOfBounds(ClosestReachableDisturbance(pSoldier, &fClimb))) )
{
// addition: if soldier is bleeding then reduce bleeding and do nothing
/*if ( pSoldier->bBleeding > MIN_BLEEDING_THRESHOLD )
{
// no! zombies don't bandage themselves!
// reduce bleeding by 1 point per AP (in RT, APs will get recalculated so it's okay)
// pSoldier->bBleeding = __max( 0, pSoldier->bBleeding - (pSoldier->bActionPoints/2) );
return( AI_ACTION_NONE ); // will end-turn/wait depending on whether we're in TB or realtime
}*/
#ifdef RECORDNET
fprintf(NetDebugFile,"\tZombieDecideActionRed: guynum %d switching to GREEN AI...\n",pSoldier->ubID);
#endif
@@ -8126,9 +8066,9 @@ INT8 ZombieDecideActionRed(SOLDIERTYPE *pSoldier)
pSoldier->aiData.usActionData = NOWHERE;
return(AI_ACTION_NONE);
}
}*/
INT8 ZombieDecideActionBlack(SOLDIERTYPE *pSoldier)
/*INT8 ZombieDecideActionBlack(SOLDIERTYPE *pSoldier)
{
INT32 iCoverPercentBetter = 0;
INT32 iOffense, iDefense, iChance;
@@ -8685,12 +8625,12 @@ INT8 ZombieDecideActionBlack(SOLDIERTYPE *pSoldier)
}
// get the location of the closest reachable opponent
/* Flugente 22.02.2012 - A few clarifications: I changed ClosestSeenOpponent so that for zombies, this function also returns an opponent if he is on the
* roof of a building, we are not, but our GridNo belongs to that same building.
* If that is the case, it is clear that we have to get on that roof. However, we cannot do that in BlackState. If, by pure chance, we can still see our
* enemy, we cannot climb (there is no climbing option in BlackState sofar).
* So, I changed the code so that now we will climb the roof.
*/
// Flugente 22.02.2012 - A few clarifications: I changed ClosestSeenOpponent so that for zombies, this function also returns an opponent if he is on the
// roof of a building, we are not, but our GridNo belongs to that same building.
// If that is the case, it is clear that we have to get on that roof. However, we cannot do that in BlackState. If, by pure chance, we can still see our
// enemy, we cannot climb (there is no climbing option in BlackState sofar).
// So, I changed the code so that now we will climb the roof.
INT32 targetGridNo = -1;
INT8 targetbLevel = 0;
sClosestOpponent = ClosestSeenOpponentWithRoof(pSoldier, &targetGridNo, &targetbLevel);
@@ -8838,9 +8778,9 @@ INT8 ZombieDecideActionBlack(SOLDIERTYPE *pSoldier)
pSoldier->aiData.usActionData = NOWHERE;
return(AI_ACTION_NONE);
}
}*/
INT8 ZombieDecideAction( SOLDIERTYPE *pSoldier )
/*INT8 ZombieDecideAction( SOLDIERTYPE *pSoldier )
{
INT8 bAction = AI_ACTION_NONE;
@@ -8882,7 +8822,7 @@ INT8 ZombieDecideAction( SOLDIERTYPE *pSoldier )
#endif
return(bAction);
}
}*/
INT8 ArmedVehicleDecideAction( SOLDIERTYPE *pSoldier )
@@ -9845,7 +9785,7 @@ INT8 ArmedVehicleDecideActionRed( SOLDIERTYPE *pSoldier)
return(AI_ACTION_NONE);
}
fProneSightCover = ProneSightCoverAtSpot( pSoldier, pSoldier->sGridNo );
fProneSightCover = ProneSightCoverAtSpot(pSoldier, pSoldier->sGridNo, FALSE);
if ( !fProneSightCover || pSoldier->aiData.bUnderFire )
{
fDangerousSpot = TRUE;
@@ -11901,3 +11841,73 @@ INT8 ArmedVehicleDecideActionBlack( SOLDIERTYPE *pSoldier )
pSoldier->aiData.usActionData = NOWHERE;
return(AI_ACTION_NONE);
}
extern UINT32 guiTurnCnt;
extern UINT32 guiReinforceTurn;
extern UINT32 guiArrived;
void LogDecideInfo(SOLDIERTYPE *pSoldier)
{
DebugAI(AI_MSG_INFO, pSoldier, String("Turn num %d aware %d", guiTurnCnt, gTacticalStatus.Team[pSoldier->bTeam].bAwareOfOpposition));
DebugAI(AI_MSG_INFO, pSoldier, String("current team %d interrupt occurred %d", gTacticalStatus.ubCurrentTeam, gTacticalStatus.fInterruptOccurred));
DebugAI(AI_MSG_INFO, pSoldier, String("AP=%d/%d %s %s %s %s %s", pSoldier->bActionPoints, pSoldier->bInitialActionPoints, gStr8AlertStatus[pSoldier->aiData.bAlertStatus], gStr8Orders[pSoldier->aiData.bOrders], gStr8Attitude[pSoldier->aiData.bAttitude], gStr8Team[pSoldier->bTeam], gStr8Class[pSoldier->ubSoldierClass]));
DebugAI(AI_MSG_INFO, pSoldier, String("Health %d/%d Breath %d/%d Shock %d Tolerance %d AI Morale %d Morale %d", pSoldier->stats.bLife, pSoldier->stats.bLifeMax, pSoldier->bBreath, pSoldier->bBreathMax, pSoldier->aiData.bShock, CalcSuppressionTolerance(pSoldier), pSoldier->aiData.bAIMorale, pSoldier->aiData.bMorale));
DebugAI(AI_MSG_INFO, pSoldier, String("Spot %d level %d opponents %d", pSoldier->sGridNo, pSoldier->pathing.bLevel, pSoldier->aiData.bOppCnt));
DebugAI(AI_MSG_INFO, pSoldier, String("ubServiceCount %d ubServicePartner %d fDoingSurgery %d", pSoldier->ubServiceCount, pSoldier->ubServicePartner, pSoldier->fDoingSurgery));
if (pSoldier->usAnimState == COWERING || pSoldier->usAnimState == COWERING_PRONE)
{
DebugAI(AI_MSG_INFO, pSoldier, String("Cowering"));
}
if (pSoldier->usAnimState == GIVING_AID || pSoldier->usAnimState == GIVING_AID_PRN)
{
DebugAI(AI_MSG_INFO, pSoldier, String("Giving aid"));
}
//CHAR8 str8[1024];
// show watched locations
INT8 bLoop;
for (bLoop = 0; bLoop < NUM_WATCHED_LOCS; bLoop++)
{
if (!TileIsOutOfBounds(gsWatchedLoc[pSoldier->ubID][bLoop]))
{
DebugAI(AI_MSG_INFO, pSoldier, String("Watched location %d level %d points %d", gsWatchedLoc[pSoldier->ubID][bLoop], gbWatchedLocLevel[pSoldier->ubID][bLoop], gubWatchedLocPoints[pSoldier->ubID][bLoop]));
}
}
LogKnowledgeInfo(pSoldier);
DebugAI(AI_MSG_INFO, pSoldier, String("What I know %d", WhatIKnowThatPublicDont(pSoldier, FALSE)));
DebugAI(AI_MSG_INFO, pSoldier, String("Has Gun %d, Short range weapon %d, Gun Range %d, Gun Ammo %d, Gun Scoped %d ", AICheckHasGun(pSoldier), AICheckShortWeaponRange(pSoldier), AIGunRange(pSoldier), AIGunAmmo(pSoldier), AIGunScoped(pSoldier)));
}
void LogKnowledgeInfo(SOLDIERTYPE *pSoldier)
{
//CHAR8 str8[1024];
//memset(str8, 0, 1024 * sizeof(char));
// show public opponents
for (UINT16 oppID = 0; oppID < MAX_NUM_SOLDIERS; oppID++)
{
if (gbPublicOpplist[pSoldier->bTeam][oppID] != NOT_HEARD_OR_SEEN &&
!MercPtrs[oppID]->aiData.bNeutral)
{
//wcstombs(str8, MercPtrs[oppID]->GetName(), wcslen(MercPtrs[oppID]->GetName())+1);
//wcstombs(str8, MercPtrs[oppID]->GetName(), 1024 - 1);
DebugAI(AI_MSG_INFO, pSoldier, String("public opponent [%d] knowledge %s gridno %d level %d", oppID, gStr8Knowledge[gbPublicOpplist[pSoldier->bTeam][oppID] - OLDEST_HEARD_VALUE], gsPublicLastKnownOppLoc[pSoldier->bTeam][oppID], gbPublicLastKnownOppLevel[pSoldier->bTeam][oppID]));
//swprintf( pStrInfo, L"%s[%d] %s %s\n", pStrInfo, oppID, MercPtrs[oppID]->GetName(), SeenStr(gbPublicOpplist[pSoldier->bTeam][oppID]) );
}
}
// show personal opponents
for (UINT16 oppID = 0; oppID < MAX_NUM_SOLDIERS; oppID++)
{
if (pSoldier->aiData.bOppList[oppID] != NOT_HEARD_OR_SEEN &&
!MercPtrs[oppID]->aiData.bNeutral)
{
//wcstombs(str8, MercPtrs[oppID]->GetName(), wcslen(MercPtrs[oppID]->GetName())+1);
//wcstombs(str8, MercPtrs[oppID]->GetName(), 1024 - 1);
DebugAI(AI_MSG_INFO, pSoldier, String("personal opponent [%d] knowledge %s gridno %d level %d", oppID, gStr8Knowledge[pSoldier->aiData.bOppList[oppID] - OLDEST_HEARD_VALUE], gsLastKnownOppLoc[pSoldier->ubID][oppID], gbLastKnownOppLevel[pSoldier->ubID][oppID]));
//swprintf( pStrInfo, L"%s[%d] %s %s\n", pStrInfo, oppID, MercPtrs[oppID]->GetName(), SeenStr(pSoldier->aiData.bOppList[oppID]) );
}
}
}
+582 -65
View File
@@ -290,14 +290,13 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
INT16 sTempX, sTempY;
FLOAT dMyX, dMyY, dHisX, dHisY;
INT8 bHisBestCTGT, bHisActualCTGT, bHisCTGT, bMyCTGT;
INT32 iRangeChange, iRangeFactor, iRangeFactorMultiplier;
INT32 iRangeChange, iRangeFactor, iRCD;
SOLDIERTYPE *pHim;
// sevenfm
INT8 bHisLevel;
INT8 bMyLevel;
INT32 sDist;
UINT8 ubCoverReduction;
dMyX = dMyY = dHisX = dHisY = -1.0;
@@ -338,7 +337,8 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
}
if (InWaterOrGas(pHim,sHisGridNo))
if (DeepWater(sHisGridNo, bHisLevel) || InGas(pHim, sHisGridNo))
//if (InWaterOrGas(pHim,sHisGridNo))
{
bHisActualCTGT = 0;
}
@@ -378,7 +378,8 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
// if my intended gridno is in water or gas, I can't attack at all from there
// here, for smoke, consider bad
if (InWaterGasOrSmoke(pMe,sMyGridNo))
if (DeepWater(sMyGridNo, bMyLevel) || InGas(pMe, sMyGridNo))
//if (InWaterGasOrSmoke(pMe,sMyGridNo))
{
bMyCTGT = 0;
}
@@ -392,8 +393,6 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
pHim->dXPos = (FLOAT) sTempX;
pHim->dYPos = (FLOAT) sTempY;
}
// bMyCTGT = ChanceToGetThrough(pMe,sHisGridNo,FAKE,ACTUAL,TESTWALLS,9999,M9PISTOL,NOT_FOR_LOS); // assume a gunshot
// bMyCTGT = SoldierToLocationChanceToGetThrough( pMe, sHisGridNo, pMe->bTargetLevel, pMe->bTargetCubeLevel );
// let's not assume anything about the stance the enemy might take, so take an average
// value... no cover give a higher value than partial cover
@@ -423,6 +422,39 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
pHim->dYPos = dHisY; // and the 'y'
}
sDist = PythSpacesAway(sMyGridNo, sHisGridNo);
if (gGameExternalOptions.fAIBetterCover)
{
// sevenfm: special calculations for zombies: zombie is very dangerous at close range
if (pHim->IsZombie() || !AICheckHasGun(pHim))
{
if (sDist < (INT32)(DAY_VISION_RANGE / 2))
{
// 100 - 25
bHisCTGT = 100 - 150 * sDist / DAY_VISION_RANGE;
}
else
{
// 25 - 0
bHisCTGT = 100 * DAY_VISION_RANGE * DAY_VISION_RANGE / (16 * sDist * sDist);
}
}
// sevenfm: if soldier has no gun, use different calculation
if (pMe->IsZombie() || !AICheckHasGun(pMe))
{
if (sDist < (INT32)(DAY_VISION_RANGE / 2))
{
bMyCTGT = 100 - 150 * sDist / DAY_VISION_RANGE;
}
else
{
bMyCTGT = 100 * DAY_VISION_RANGE * DAY_VISION_RANGE / (16 * sDist * sDist);
}
}
}
// these value should be < 1 million each
iHisPosValue = bHisCTGT * Threat[uiThreatIndex].iValue * Threat[uiThreatIndex].iAPs;
iMyPosValue = bMyCTGT * iMyThreat * iMyAPsLeft;
@@ -431,18 +463,20 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
// max 25% at DAY_VISION_RANGE/2, 0 at zero range
if( gGameExternalOptions.fAIBetterCover )
{
if( AnyCoverFromSpot(sMyGridNo, bMyLevel, sHisGridNo, bHisLevel) )
UINT8 ubCoverBonus = 25;
if (sDist < (INT32)(DAY_VISION_RANGE / 2))
{
ubCoverReduction = 25;
sDist = PythSpacesAway(sMyGridNo, sHisGridNo);
if (sDist < (INT32)(DAY_VISION_RANGE / 2))
{
ubCoverReduction = ubCoverReduction * 2 * sDist / DAY_VISION_RANGE;
}
iHisPosValue -= iHisPosValue * ubCoverReduction / 100;
iMyPosValue += iMyPosValue * ubCoverReduction / 100;
ubCoverBonus = ubCoverBonus * 2 * sDist / DAY_VISION_RANGE;
}
if (!pHim->IsZombie() && AICheckHasGun(pHim) && AnyCoverFromSpot(sMyGridNo, bMyLevel, sHisGridNo, bHisLevel))
{
iHisPosValue -= iHisPosValue * ubCoverBonus / 100;
iMyPosValue += iMyPosValue * ubCoverBonus / 100;
}
if (!pMe->IsZombie() && AICheckHasGun(pMe) && AnyCoverFromSpot(sHisGridNo, bHisLevel, sMyGridNo, bMyLevel))
{
iHisPosValue += iHisPosValue * ubCoverBonus / 100;
iMyPosValue -= iMyPosValue * ubCoverBonus / 100;
}
}
@@ -459,66 +493,62 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
}
// if my positional value is worth something at all here
if (iMyPosValue > 0)
// if my positional value is worth something at all here
if (iMyPosValue > 0)
{
// if I CAN'T crouch when I get there, that makes it significantly less
// appealing a spot (how much depends on range), so that's a penalty to me
if (iMyAPsLeft < GetAPsCrouch(pMe,TRUE))
// subtract another 1 % penalty for NOT being able to crouch per tile
// the farther away we are, the bigger a difference crouching will make!
iMyPosValue -= ((iMyPosValue * (AIM_PENALTY_TARGET_CROUCHED + (iRange / CELL_X_SIZE))) / 100);
// if I CAN'T crouch when I get there, that makes it significantly less
// appealing a spot (how much depends on range), so that's a penalty to me
if (iMyAPsLeft < GetAPsCrouch(pMe, TRUE))
// subtract another 1 % penalty for NOT being able to crouch per tile
// the farther away we are, the bigger a difference crouching will make!
iMyPosValue -= ((iMyPosValue * (AIM_PENALTY_TARGET_CROUCHED + (iRange / CELL_X_SIZE))) / 100);
}
// high morale prefers decreasing the range (positive factor), while very
// low morale (HOPELESS) prefers increasing it
// if (bHisCTGT < 100 || (morale - 1 < 0))
iRCD = RangeChangeDesire(pMe);
if (iRCD)
{
iRangeChange = Threat[uiThreatIndex].iOrigRange - iRange;
iRangeFactorMultiplier = RangeChangeDesire( pMe );
if (iRangeFactorMultiplier)
if (iRangeChange)
{
iRangeChange = Threat[uiThreatIndex].iOrigRange - iRange;
//iRangeFactor = (iRangeChange * (morale - 1)) / 4;
iRangeFactor = (iRangeChange * iRCD) / 2;
if (iRangeChange)
// sevenfm: reduce range bonus depending on cover
if (gGameExternalOptions.fAIBetterCover)
{
//iRangeFactor = (iRangeChange * (morale - 1)) / 4;
iRangeFactor = (iRangeChange * iRangeFactorMultiplier) / 2;
// sevenfm: reduce range bonus depending on cover
if( gGameExternalOptions.fAIBetterCover )
if (!AnyCoverFromSpot(sMyGridNo, bMyLevel, sHisGridNo, bHisLevel) &&
CountSeenEnemiesLastTurn(pMe) > CountNearbyFriends(pMe, sMyGridNo, DAY_VISION_RANGE / 2))
{
if( !AnyCoverFromSpot(sMyGridNo, bMyLevel, sHisGridNo, bHisLevel) &&
CountSeenEnemiesLastTurn(pMe) > CountNearbyFriends(pMe, sMyGridNo, DAY_VISION_RANGE / 2) )
{
iRangeFactor = iRangeFactor * (100 - bHisCTGT * __min(Threat[uiThreatIndex].iAPs, APBPConstants[AP_MAXIMUM]) / APBPConstants[AP_MAXIMUM]) / 100;
}
}
#ifdef DEBUGCOVER
DebugAI( String( "CalcCoverValue: iRangeChange %d, iRangeFactor %d\n", iRangeChange, iRangeFactor ) );
#endif
// aggression booster for stupider enemies
iMyPosValue += 100 * iRangeFactor * ( 5 - SoldierDifficultyLevel( pMe ) ) / 5 ;
// if factor is positive increase positional value, else decrease it
// change both values, since one or the other could be 0
if (iRangeFactor > 0)
{
iMyPosValue = (iMyPosValue * (100 + iRangeFactor)) / 100;
iHisPosValue = (100 * iHisPosValue) / (100 + iRangeFactor);
}
else if (iRangeFactor < 0)
{
iMyPosValue = (100 * iMyPosValue) / (100 - iRangeFactor);
iHisPosValue = (iHisPosValue * (100 - iRangeFactor)) / 100;
iRangeFactor = iRangeFactor * (100 - bHisCTGT * __min(Threat[uiThreatIndex].iAPs, APBPConstants[AP_MAXIMUM]) / APBPConstants[AP_MAXIMUM]) / 100;
}
}
#ifdef DEBUGCOVER
DebugAI( String( "CalcCoverValue: iRangeChange %d, iRangeFactor %d\n", iRangeChange, iRangeFactor ) );
#endif
// aggression booster for stupider enemies
iMyPosValue += 100 * iRangeFactor * (5 - SoldierDifficultyLevel(pMe)) / 5;
// if factor is positive increase positional value, else decrease it
// change both values, since one or the other could be 0
if (iRangeFactor > 0)
{
iMyPosValue = (iMyPosValue * (100 + iRangeFactor)) / 100;
iHisPosValue = (100 * iHisPosValue) / (100 + iRangeFactor);
}
else if (iRangeFactor < 0)
{
iMyPosValue = (100 * iMyPosValue) / (100 - iRangeFactor);
iHisPosValue = (iHisPosValue * (100 - iRangeFactor)) / 100;
}
}
}
@@ -527,7 +557,7 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
iReductionFactor = ((MAX_THREAT_RANGE - iRange) * Threat[uiThreatIndex].iCertainty) /
MAX_THREAT_RANGE;
// divide by a 100 to make the numbers more managable and avoid 32-bit limit
// divide by a 100 to make the numbers more manageable and avoid 32-bit limit
iThisScale = max( iMyPosValue, iHisPosValue) / 100;
iThisScale = (iThisScale * iReductionFactor) / 100;
*iTotalScale += iThisScale;
@@ -535,7 +565,7 @@ INT32 CalcCoverValue(SOLDIERTYPE *pMe, INT32 sMyGridNo, INT32 iMyThreat, INT32 i
// POSITIVE COVER VALUE INDICATES THE COVER BENEFITS ME, NEGATIVE RESULT
// MEANS IT BENEFITS THE OTHER GUY.
// divide by a 100 to make the numbers more managable and avoid 32-bit limit
// divide by a 100 to make the numbers more manageable and avoid 32-bit limit
iCoverValue = (iMyPosValue - iHisPosValue) / 100;
iCoverValue = (iCoverValue * iReductionFactor) / 100;
@@ -604,7 +634,7 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
UINT8 ubBackgroundLightLevel;
UINT8 ubBackgroundLightPercent = 0;
UINT8 ubLightPercentDifference;
BOOLEAN fNight;
BOOLEAN fNight;
// sevenfm
UINT8 ubNearbyFriends;
@@ -1057,6 +1087,13 @@ INT32 FindBestNearbyCover(SOLDIERTYPE *pSoldier, INT32 morale, INT32 *piPercentB
continue;
}
// zombies only want to hide
if (pSoldier->IsZombie() && !SightCoverAtSpot(pSoldier, sGridNo, TRUE))
{
//DebugCover(pSoldier, String("zombie: no sight cover at new spot, skip"));
continue;
}
iPathCost = gubAIPathCosts[AI_PATHCOST_RADIUS + sXOffset][AI_PATHCOST_RADIUS + sYOffset];
/*
// water is OK, if the only good hiding place requires us to get wet, OK
@@ -3145,3 +3182,483 @@ INT32 FindNearestPassableSpot( INT32 sGridNo, UINT8 usSearchRadius )
return( sNearestSpot );
}
INT32 FindAdvanceSpot(SOLDIERTYPE *pSoldier, INT32 sTargetSpot, INT8 bAction, UINT8 ubType, BOOLEAN fUnlimited)
{
INT32 sGridNo, sRealGridNo;
INT32 sBestSpot = NOWHERE;
INT32 iSearchRange = min(AI_PATHCOST_RADIUS, MAX_TILES_MOVE_TURN);
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
INT32 iPathCost, iBestPathCost = 0;
INT16 usMovementMode;
INT32 iRoamRange, iDistFromOrigin, sOrigin;
//BOOLEAN fClimbingNecessary;
//INT32 sClimbGridNo;
BOOLEAN fHasMortar;
if (!pSoldier)
{
return NOWHERE;
}
// check target location
if (!NewOKDestination(pSoldier, sTargetSpot, FALSE, pSoldier->pathing.bLevel))
{
//ScreenMsg(FONT_ORANGE, MSG_INTERFACE, L"bad destination %d", sTargetSpot);
return NOWHERE;
}
fHasMortar = AICheckIsMortarOperator(pSoldier);
usMovementMode = DetermineMovementMode(pSoldier, bAction);
UINT8 ubReserveAP = 0;
if (ubType != ADVANCE_SPOT_SIGHT_COVER)
{
if (usMovementMode == RUNNING || usMovementMode == WALKING)
{
ubReserveAP = APBPConstants[AP_CHANGE_FACING] + GetAPsCrouch(pSoldier, TRUE) + GetAPsProne(pSoldier, TRUE);
}
else
{
ubReserveAP = APBPConstants[AP_LOOK_CROUCHED] + GetAPsProne(pSoldier, TRUE);
}
}
if (pSoldier->bActionPoints <= ubReserveAP)
{
return NOWHERE;
}
// check that location is reachable
iBestPathCost = EstimatePlotPath(pSoldier, sTargetSpot, FALSE, FALSE, FALSE, usMovementMode, pSoldier->bStealthMode, FALSE, 0);
if (iBestPathCost == 0)
{
return NOWHERE;
}
iRoamRange = RoamingRange(pSoldier, &sOrigin);
// set AP limit
gubNPCAPBudget = pSoldier->bActionPoints - ubReserveAP;
// set the distance limit of the square region
gubNPCDistLimit = (UINT8)iSearchRange;
// determine maximum horizontal limits
sMaxLeft = min(iSearchRange, (pSoldier->sGridNo % MAXCOL));
//NumMessage("sMaxLeft = ",sMaxLeft);
sMaxRight = min(iSearchRange, MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
//NumMessage("sMaxRight = ",sMaxRight);
// determine maximum vertical limits
sMaxUp = min(iSearchRange, (pSoldier->sGridNo / MAXROW));
//NumMessage("sMaxUp = ",sMaxUp);
sMaxDown = min(iSearchRange, MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
//NumMessage("sMaxDown = ",sMaxDown);
// Call FindBestPath to set flags in all locations that we can
// walk into within range. We have to set some things up first...
// reset the "reachable" flags in the region we're looking at
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
{
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
{
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
if (!(sGridNo >= 0 && sGridNo < WORLD_MAX))
{
continue;
}
gpWorldLevelData[sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
}
}
FindBestPath(pSoldier, GRIDSIZE, pSoldier->pathing.bLevel, usMovementMode, COPYREACHABLE, 0);
// Turn off the "reachable" flag for his current location
// so we don't consider it
gpWorldLevelData[pSoldier->sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
{
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
{
// calculate the next potential gridno
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
if (!(sGridNo >= 0 && sGridNo < WORLD_MAX))
{
continue;
}
if (!(gpWorldLevelData[sGridNo].uiFlags & MAPELEMENT_REACHABLE))
{
continue;
}
if (sGridNo == pSoldier->pathing.sBlackList)
{
continue;
}
// check roaming range
if (iRoamRange < MAX_ROAMING_RANGE && !TileIsOutOfBounds(sOrigin))
{
iDistFromOrigin = SpacesAway(sOrigin, sGridNo);
if (iDistFromOrigin > iRoamRange)
{
continue;
}
}
// check if we will be closer to target spot
if (PythSpacesAway(sGridNo, sTargetSpot) > PythSpacesAway(pSoldier->sGridNo, sTargetSpot))
{
continue;
}
// exclude locations with tear/mustard gas (at this point, smoke is cool!)
if (InGas(pSoldier, sGridNo))
{
continue;
}
// skip lighted spots
if (InLightAtNight(sGridNo, pSoldier->pathing.bLevel) &&
!InLightAtNight(pSoldier->sGridNo, pSoldier->pathing.bLevel))
continue;
// skip deep water
if (DeepWater(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// skip water
if (Water(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// skip locations too close to target spot
if (PythSpacesAway(sGridNo, sTargetSpot) < (INT16)(DAY_VISION_RANGE / 4))
{
continue;
}
// avoid smoke
if (InSmoke(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// skip if this spot has no cover nearby
if ((ubType == ADVANCE_SPOT_ANY_COVER || fUnlimited) &&
!FindObstacleNearSpot(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// avoid locations near fresh corpses
if (!pSoldier->IsZombie() && GetNearestRottingCorpseAIWarning(sGridNo) > 0)
//if (CorpseWarning(pSoldier, sGridNo, pSoldier->pathing.bLevel, TRUE))
{
continue;
}
// avoid overcrowding
if (!pSoldier->IsZombie() && NumberOfTeamMatesAdjacent(pSoldier, sGridNo) > 1)
{
continue;
}
if (!CheckNPCDestination(pSoldier, sGridNo))
{
continue;
}
if (!LegalNPCDestination(pSoldier, sGridNo, IGNORE_PATH, NOWATER, 0))
{
continue; // skip on to the next potential grid
}
// sevenfm: avoid rooms for mortar operators
if (!AICheckUnderground() &&
pSoldier->pathing.bLevel == 0 &&
fHasMortar &&
InARoom(sGridNo, NULL))
{
continue;
}
if (ubType == ADVANCE_SPOT_SIGHT_COVER)
{
// skip locations with no sight cover
if (!SightCoverAtSpot(pSoldier, sGridNo, fUnlimited))
{
continue;
}
}
else if (ubType == ADVANCE_SPOT_PRONE_COVER)
{
// check prone sight cover at spot
if (!ProneSightCoverAtSpot(pSoldier, sGridNo, fUnlimited))
{
continue;
}
// check that we'll have enough APs to go prone at target spot
/*iPathCost = EstimatePlotPath( pSoldier, sGridNo, FALSE, FALSE, FALSE, usMovementMode, pSoldier->bStealthMode, FALSE, 0);
if( pSoldier->bActionPoints - iPathCost < GetAPsCrouch(pSoldier, TRUE) + GetAPsProne(pSoldier, TRUE) + APBPConstants[AP_CHANGE_FACING] )
{
continue;
}*/
}
else if (ubType == ADVANCE_SPOT_ANY_COVER)
{
// check any cover at spot
if (!AnyCoverAtSpot(pSoldier, sGridNo))
{
continue;
}
// check that we'll have enough APs to go prone at target spot
/*iPathCost = EstimatePlotPath( pSoldier, sGridNo, FALSE, FALSE, FALSE, usMovementMode, pSoldier->bStealthMode, FALSE, 0);
if( pSoldier->bActionPoints - iPathCost < GetAPsCrouch(pSoldier, TRUE) + GetAPsProne(pSoldier, TRUE) + APBPConstants[AP_CHANGE_FACING] )
{
continue;
}*/
}
sRealGridNo = pSoldier->sGridNo;
pSoldier->sGridNo = sGridNo;
iPathCost = EstimatePlotPath(pSoldier, sTargetSpot, FALSE, FALSE, FALSE, usMovementMode, pSoldier->bStealthMode, FALSE, 0);
//iPathCost = PlotPath(pSoldier, sTargetSpot, FALSE, FALSE, FALSE, usMovementMode, pSoldier->bStealthMode, FALSE, 0);
//iPathCost = EstimatePathCostToLocation( pSoldier, sTargetSpot, pSoldier->pathing.bLevel, TRUE, &fClimbingNecessary, &sClimbGridNo );
pSoldier->sGridNo = sRealGridNo;
// skip location if no path to target spot
if (iPathCost == 0)
{
//ScreenMsg(FONT_ORANGE, MSG_INTERFACE, L"cannot find path to destination %d at %d", sTargetSpot, sGridNo);
continue;
}
//if( sBestSpot == NOWHERE || iPathCost < iBestPathCost )
if (iPathCost < iBestPathCost)
{
sBestSpot = sGridNo;
iBestPathCost = iPathCost;
}
}
}
gubNPCAPBudget = 0;
gubNPCDistLimit = 0;
return(sBestSpot);
}
// find spot with cover, max dist from opponents
INT32 FindRetreatSpot(SOLDIERTYPE *pSoldier)
{
INT32 sGridNo;
INT32 sBestSpot = NOWHERE;
INT32 iSearchRange = MAX_TILES_MOVE_TURN;
INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
INT16 usMovementMode;
INT32 iRoamRange, iDistFromOrigin, sOrigin;
//BOOLEAN fClimbingNecessary;
//INT32 sClimbGridNo;
UINT8 ubReserveAP;
INT8 bAction = AI_ACTION_TAKE_COVER;
INT32 sClosestOpponent;
INT16 sDistance, sClosestDistance;
if (!pSoldier)
{
return NOWHERE;
}
sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
if (TileIsOutOfBounds(sClosestOpponent))
{
// no one to hide from
return NOWHERE;
}
sClosestDistance = PythSpacesAway(pSoldier->sGridNo, sClosestOpponent);
//ubReserveAP = GetAPsCrouch(pSoldier, TRUE) + GetAPsProne(pSoldier, TRUE) + APBPConstants[AP_CHANGE_FACING];
ubReserveAP = GetAPsCrouch(pSoldier, TRUE) + APBPConstants[AP_CHANGE_FACING];
if (pSoldier->bActionPoints <= ubReserveAP)
{
return NOWHERE;
}
iRoamRange = RoamingRange(pSoldier, &sOrigin);
// set AP limit
gubNPCAPBudget = pSoldier->bActionPoints;
// set the distance limit of the square region
gubNPCDistLimit = (UINT8)iSearchRange;
// determine maximum horizontal limits
sMaxLeft = min(iSearchRange, (pSoldier->sGridNo % MAXCOL));
//NumMessage("sMaxLeft = ",sMaxLeft);
sMaxRight = min(iSearchRange, MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
//NumMessage("sMaxRight = ",sMaxRight);
// determine maximum vertical limits
sMaxUp = min(iSearchRange, (pSoldier->sGridNo / MAXROW));
//NumMessage("sMaxUp = ",sMaxUp);
sMaxDown = min(iSearchRange, MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
//NumMessage("sMaxDown = ",sMaxDown);
// Call FindBestPath to set flags in all locations that we can
// walk into within range. We have to set some things up first...
// reset the "reachable" flags in the region we're looking at
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
{
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
{
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
if (!(sGridNo >= 0 && sGridNo < WORLD_MAX))
{
continue;
}
gpWorldLevelData[sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
}
}
usMovementMode = DetermineMovementMode(pSoldier, bAction);
FindBestPath(pSoldier, GRIDSIZE, pSoldier->pathing.bLevel, usMovementMode, COPYREACHABLE, 0);
// Turn off the "reachable" flag for his current location
// so we don't consider it
gpWorldLevelData[pSoldier->sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
{
for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
{
// calculate the next potential gridno
sGridNo = pSoldier->sGridNo + sXOffset + (MAXCOL * sYOffset);
if (!(sGridNo >= 0 && sGridNo < WORLD_MAX))
{
continue;
}
if (!(gpWorldLevelData[sGridNo].uiFlags & MAPELEMENT_REACHABLE))
{
continue;
}
if (sGridNo == pSoldier->pathing.sBlackList)
{
continue;
}
// check roaming range
if (iRoamRange < MAX_ROAMING_RANGE && !TileIsOutOfBounds(sOrigin))
{
iDistFromOrigin = SpacesAway(sOrigin, sGridNo);
if (iDistFromOrigin > iRoamRange)
{
continue;
}
}
// exclude locations with tear/mustard gas (at this point, smoke is cool!)
if (InGas(pSoldier, sGridNo))
{
continue;
}
// skip lighted spots
if (InLightAtNight(sGridNo, pSoldier->pathing.bLevel) &&
!InLightAtNight(pSoldier->sGridNo, pSoldier->pathing.bLevel))
continue;
// skip deep water
if (DeepWater(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// skip water
if (Water(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
// avoid locations near fresh corpses
if (!pSoldier->IsZombie() && GetNearestRottingCorpseAIWarning(sGridNo) > 0)
//if (CorpseWarning(pSoldier, sGridNo, pSoldier->pathing.bLevel, TRUE))
{
continue;
}
if (!pSoldier->IsZombie() && NumberOfTeamMatesAdjacent(pSoldier, sGridNo) > 0)
{
continue;
}
if (!CheckNPCDestination(pSoldier, sGridNo))
{
continue;
}
// avoid smoke
/*if( InSmoke(sGridNo, pSoldier->pathing.bLevel, FALSE) )
{
continue;
}*/
if (!LegalNPCDestination(pSoldier, sGridNo, IGNORE_PATH, NOWATER, 0))
{
continue; // skip on to the next potential grid
}
// skip if too close to enemy
//sClosestOpponent = ClosestKnownOpponent(pSoldier, NULL, NULL);
sDistance = PythSpacesAway(sGridNo, sClosestOpponent);
if (sDistance < (INT16)(DAY_VISION_RANGE / 2))
{
continue;
}
// skip if this spot has no cover nearby
if (!FindObstacleNearSpot(sGridNo, pSoldier->pathing.bLevel))
{
continue;
}
if (!AnyCoverAtSpot(pSoldier, sGridNo))
{
continue;
}
// check prone sight cover at spot
/*if( !ProneSightCoverAtSpot(pSoldier, sGridNo) )
{
continue;
}*/
if (sDistance > sClosestDistance)
{
sBestSpot = sGridNo;
sClosestDistance = sDistance;
}
}
}
gubNPCAPBudget = 0;
gubNPCDistLimit = 0;
return(sBestSpot);
}
+1
View File
@@ -45,6 +45,7 @@
<ClCompile Include="PanicButtons.cpp" />
<ClCompile Include="QuestDebug.cpp" />
<ClCompile Include="Realtime.cpp" />
<ClCompile Include="ZombieDecideAction.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B369A125-E62E-46AE-9285-58003D688301}</ProjectGuid>
@@ -71,5 +71,8 @@
<ClCompile Include="Realtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ZombieDecideAction.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
+1
View File
@@ -45,6 +45,7 @@
<ClCompile Include="PanicButtons.cpp" />
<ClCompile Include="QuestDebug.cpp" />
<ClCompile Include="Realtime.cpp" />
<ClCompile Include="ZombieDecideAction.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B369A125-E62E-46AE-9285-58003D688301}</ProjectGuid>
@@ -71,5 +71,8 @@
<ClCompile Include="Realtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ZombieDecideAction.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
+1
View File
@@ -45,6 +45,7 @@
<ClCompile Include="PanicButtons.cpp" />
<ClCompile Include="QuestDebug.cpp" />
<ClCompile Include="Realtime.cpp" />
<ClCompile Include="ZombieDecideAction.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B369A125-E62E-46AE-9285-58003D688301}</ProjectGuid>
File diff suppressed because it is too large Load Diff
+53 -4
View File
@@ -5,6 +5,7 @@
#include "worlddef.h"
#include "Soldier Control.h"
#include "Isometric Utils.h"
#include "Rotting Corpses.h"
#define TESTAICONTROL
@@ -109,6 +110,7 @@ typedef enum
AI_ACTION_DOCTOR_SELF, // added by Flugente: AI-ONLY! bandage/surgery on self. DO NOT USE THIS FOR MERCS!!!
AI_ACTION_SELFDETONATE, // added by Flugente: blow up an explosive in own inventory
AI_ACTION_STOP_MEDIC, // sevenfm: stop giving aid animation
AI_ACTION_LAST = AI_ACTION_STOP_MEDIC
} ActionType;
@@ -153,6 +155,12 @@ enum QuoteActionType
extern INT8 gbDiff[MAX_DIFF_PARMS][5];
enum{
ADVANCE_SPOT_SIGHT_COVER,
ADVANCE_SPOT_PRONE_COVER,
ADVANCE_SPOT_ANY_COVER
};
void ActionDone(SOLDIERTYPE *pSoldier);
INT16 ActionInProgress(SOLDIERTYPE *pSoldier);
@@ -173,7 +181,9 @@ INT32 ClosestUnDisguisedPC( SOLDIERTYPE *pSoldier, INT32 * psDistance ); // Flug
BOOLEAN CanAutoBandage( BOOLEAN fDoFullCheck );
void DebugAI( STR szOutput );
INT8 DecideAction(SOLDIERTYPE *pSoldier);
enum { AI_MSG_START, AI_MSG_DECIDE, AI_MSG_INFO, AI_MSG_TOPIC };
void DebugAI(INT8 bMsgType, SOLDIERTYPE *pSoldier, STR szOutput, INT8 bAction = -1);
INT8 DecideAction(SOLDIERTYPE *pSoldier);
INT8 DecideActionBlack(SOLDIERTYPE *pSoldier);
INT8 DecideActionEscort(SOLDIERTYPE *pSoldier);
INT8 DecideActionGreen(SOLDIERTYPE *pSoldier);
@@ -293,8 +303,13 @@ BOOLEAN AICheckIsFlanking( SOLDIERTYPE *pSoldier );
INT8 CalcMoraleNew(SOLDIERTYPE *pSoldier);
BOOLEAN ProneSightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot );
BOOLEAN SightCoverAtSpot( SOLDIERTYPE *pSoldier, INT32 sSpot );
BOOLEAN ProneSightCoverAtSpot(SOLDIERTYPE *pSoldier, INT32 sSpot, BOOLEAN fUnlimited);
BOOLEAN SightCoverAtSpot(SOLDIERTYPE *pSoldier, INT32 sSpot, BOOLEAN fUnlimited);
BOOLEAN AnyCoverAtSpot(SOLDIERTYPE *pSoldier, INT32 sSpot);
BOOLEAN AnyCoverFromSpot(INT32 sSpot, INT8 bLevel, INT32 sThreatLoc, INT8 bThreatLevel);
INT32 FindAdvanceSpot(SOLDIERTYPE *pSoldier, INT32 sTargetSpot, INT8 bAction, UINT8 ubType, BOOLEAN fUnlimited);
INT32 FindRetreatSpot(SOLDIERTYPE *pSoldier);
BOOLEAN CheckDoorAtGridno(UINT32 usGridNo);
BOOLEAN CheckDoorNearGridno(UINT32 usGridNo);
@@ -307,19 +322,53 @@ BOOLEAN FindNearbyExplosiveStructure(INT32 sSpot, INT8 bLevel);
INT16 DistanceToClosestActiveOpponent(SOLDIERTYPE *pSoldier, INT32 sSpot);
BOOLEAN ValidOpponent(SOLDIERTYPE* pSoldier, SOLDIERTYPE* pOpponent);
BOOLEAN AnyCoverFromSpot( INT32 sSpot, INT8 bLevel, INT32 sThreatLoc, INT8 bThreatLevel );
UINT8 CountSeenEnemiesLastTurn( SOLDIERTYPE *pSoldier );
BOOLEAN FindObstacleNearSpot(INT32 sSpot, INT8 bLevel);
BOOLEAN CheckNPCDestination(SOLDIERTYPE *pSoldier, INT32 sGridNo);
UINT8 SpotDangerLevel(SOLDIERTYPE *pSoldier, INT32 sGridNo);
BOOLEAN AllowDeepWaterFlanking(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckUnderground(void);
BOOLEAN CorpseWarning(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel);
BOOLEAN CorpseEnemyTeam(ROTTING_CORPSE *pCorpse);
BOOLEAN CorpseMilitiaTeam(ROTTING_CORPSE *pCorpse);
BOOLEAN NorthSpot(INT32 sSpot, INT8 bLevel);
BOOLEAN SoldierAI(SOLDIERTYPE *pSoldier);
UINT8 AIDirection(INT32 sSpot1, INT32 sSpot2);
BOOLEAN AIGunScoped(SOLDIERTYPE *pSoldier);
BOOLEAN AIGunInHandScoped(SOLDIERTYPE *pSoldier);
UINT16 AIGunRange(SOLDIERTYPE *pSoldier); // gun range in tiles
UINT16 AIGunClass(SOLDIERTYPE *pSoldier);
UINT16 AIGunType(SOLDIERTYPE *pSoldier);
UINT16 AIGunAmmo(SOLDIERTYPE *pSoldier);
BOOLEAN AIGunAutofireCapable(SOLDIERTYPE *pSoldier);
UINT8 AIGunDeadliness(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckNVG(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckHasWeaponOfType(SOLDIERTYPE *pSoldier, UINT8 ubWeaponType);
BOOLEAN AICheckHasGun(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckShortWeaponRange(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsSniper(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsMarksman(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsMachinegunner(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsRadioOperator(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsMedic(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsMortarOperator(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsGLOperator(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsOfficer(SOLDIERTYPE *pSoldier);
BOOLEAN AICheckIsCommander(SOLDIERTYPE *pSoldier);
#define MAX_FLANKS_RED 25
#define MAX_FLANKS_YELLOW 25
#define MAX_TILES_MOVE_TURN (APBPConstants[AP_MAXIMUM] / (APBPConstants[AP_MOVEMENT_FLAT] + APBPConstants[AP_MODIFIER_RUN]))
// vision range defines
#define DAY_VISION_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO * 2)
#define NIGHT_VISION_RANGE (gGameExternalOptions.ubStraightSightRange * STRAIGHT_RATIO )
+31
View File
@@ -26,6 +26,9 @@
#include "Campaign Types.h"
#include "Tactical Save.h"
#include "strategicmap.h"
// sevenfm
#include "environment.h"
#include "Render Fun.h"
#endif
#include "SaveLoadGame.h"
@@ -1188,6 +1191,34 @@ BOOL GetRandomSignalSmokeGridNo(INT32* psGridNo)
return TRUE;
}
// sevenfm: check smoke effect of certain type at gridno
BOOLEAN CheckSmokeEffect(INT32 sGridNo, INT8 bLevel, INT8 bType)
{
UINT32 uiCnt;
INT8 bSmokeEffectLevel;
//loop through all smoke effects
for (uiCnt = 0; uiCnt < guiNumSmokeEffects; uiCnt++)
{
if (gSmokeEffectData[uiCnt].fAllocated &&
gSmokeEffectData[uiCnt].sGridNo == sGridNo &&
gSmokeEffectData[uiCnt].bType == bType)
{
if (gSmokeEffectData[uiCnt].bFlags & SMOKE_EFFECT_ON_ROOF)
bSmokeEffectLevel = 1;
else
bSmokeEffectLevel = 0;
if (bSmokeEffectLevel == bLevel)
{
return TRUE;
}
}
}
return FALSE;
}
BOOLEAN FindVisibleSmokeEffect(INT8 bType)
{
UINT32 uiCnt;
+2
View File
@@ -71,6 +71,8 @@ void UpdateSmokeEffectGraphics( );
// Flugente: get the gridno and blevel of a random smoke signal, if one exists
BOOL GetRandomSignalSmokeGridNo(INT32* psGridNo);
// sevenfm: check if smoke effect of certain type is present at gridno
BOOLEAN CheckSmokeEffect(INT32 sGridNo, INT8 bLevel, INT8 bType);
// find smoke effect on visible screen
BOOLEAN FindVisibleSmokeEffect(INT8 bType);
+20 -3
View File
@@ -793,6 +793,7 @@ void PhysicsDeleteObject( REAL_OBJECT *pObject )
}
}
INT16 gsWaterSplashSoundNum = -1;
BOOLEAN PhysicsCheckForCollisions( REAL_OBJECT *pObject, INT32 *piCollisionID )
{
@@ -1113,9 +1114,25 @@ BOOLEAN PhysicsCheckForCollisions( REAL_OBJECT *pObject, INT32 *piCollisionID )
// sevenfm: also play sound
CHAR8 zFilename[512];
sprintf(zFilename, "sounds\\misc\\Splash%d.ogg", Random(3) + 1);
if (FileExists(zFilename))
PlayJA2SampleFromFile(zFilename, RATE_11025, SoundVolume(MIDVOLUME, pObject->sGridNo), 1, SoundDir(pObject->sGridNo));
// prepare water splash sound
if (gsWaterSplashSoundNum < 0)
{
gsWaterSplashSoundNum = 0;
do
{
gsWaterSplashSoundNum++;
sprintf(zFilename, "sounds\\misc\\Splash%d.ogg", gsWaterSplashSoundNum);
} while (FileExists(zFilename));
gsWaterSplashSoundNum--;
}
if (gsWaterSplashSoundNum > 0)
{
sprintf(zFilename, "sounds\\misc\\Splash%d.ogg", Random(gsWaterSplashSoundNum) + 1);
if (FileExists(zFilename))
{
PlayJA2SampleFromFile(zFilename, RATE_11025, SoundVolume(MIDVOLUME, pObject->sGridNo), 1, SoundDir(pObject->sGridNo));
}
}
}
}