Files
source/TacticalAI/Knowledge.cpp
T
Wanne 14750c6903 **********************************************************
** Big Maps Projects code (incl. Multiplayer v1.5 **
**********************************************************
- Merged Big Maps Project code from BMP+MP trunk (Revision: 3340)
o Complete SVN Revision history: https://81.169.133.124/source/ja2/branches/Wanne/JA2%201.13%20MP
- Before THIS merge, I made a branch of the existing 1.13 source
o SVN Branch: https://81.169.133.124/source/ja2/branches/JA2_rev.3336/src
- Removed old VS 6.0 and VS 2003 project and solutions files, because compilation is broken long time ago
- I will add VS 2010 projects and solution file in the next few days

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@3341 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2010-02-28 18:38:52 +00:00

409 lines
12 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "AI All.h"
#else
#include "ai.h"
#include "AIInternals.h"
#include "opplist.h"
#include "Soldier Profile.h"
#include "los.h"
#include "NPC.h"
#include "Quests.h"
#include "Render Fun.h"
#include "Soldier macros.h"
#endif
void CallAvailableEnemiesTo( INT32 sGridNo )
{
INT32 iLoop;
INT32 iLoop2;
SOLDIERTYPE * pSoldier;
// All enemy teams become aware of a very important "noise" coming from here!
for (iLoop = 0; iLoop < LAST_TEAM; iLoop++)
{
// if this team is active
if (gTacticalStatus.Team[iLoop].bTeamActive)
{
// if this team is computer-controlled, and isn't the CIVILIAN "team"
if (!(gTacticalStatus.Team[iLoop].bHuman) && (iLoop != CIV_TEAM))
{
// make this team (publicly) aware of the "noise"
gsPublicNoiseGridNo[iLoop] = sGridNo;
gubPublicNoiseVolume[iLoop] = MAX_MISC_NOISE_DURATION;
// new situation for everyone;
iLoop2 = gTacticalStatus.Team[ iLoop ].bFirstID;
for ( pSoldier = MercPtrs[iLoop2]; iLoop2 <= gTacticalStatus.Team[ iLoop ].bLastID; iLoop2++, pSoldier++ )
{
if (pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife >= OKLIFE)
{
SetNewSituation( pSoldier );
WearGasMaskIfAvailable( pSoldier );
}
}
}
}
}
}
void CallAvailableTeamEnemiesTo( INT32 sGridNo, INT8 bTeam )
{
INT32 iLoop2;
SOLDIERTYPE * pSoldier;
// All enemy teams become aware of a very important "noise" coming from here!
// if this team is active
if (gTacticalStatus.Team[bTeam].bTeamActive)
{
// if this team is computer-controlled, and isn't the CIVILIAN "team"
if (!(gTacticalStatus.Team[bTeam].bHuman) && (bTeam != CIV_TEAM))
{
// make this team (publicly) aware of the "noise"
gsPublicNoiseGridNo[bTeam] = sGridNo;
gubPublicNoiseVolume[bTeam] = MAX_MISC_NOISE_DURATION;
// new situation for everyone;
iLoop2 = gTacticalStatus.Team[ bTeam ].bFirstID;
for ( pSoldier = MercPtrs[iLoop2]; iLoop2 <= gTacticalStatus.Team[ bTeam ].bLastID; iLoop2++, pSoldier++ )
{
if (pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife >= OKLIFE)
{
SetNewSituation( pSoldier );
WearGasMaskIfAvailable( pSoldier );
}
}
}
}
}
void CallAvailableKingpinMenTo( INT32 sGridNo )
{
// like call all enemies, but only affects civgroup KINGPIN guys with
// NO PROFILE
INT32 iLoop2;
SOLDIERTYPE * pSoldier;
// All enemy teams become aware of a very important "noise" coming from here!
// if this team is active
if (gTacticalStatus.Team[CIV_TEAM].bTeamActive)
{
// make this team (publicly) aware of the "noise"
gsPublicNoiseGridNo[CIV_TEAM] = sGridNo;
gubPublicNoiseVolume[CIV_TEAM] = MAX_MISC_NOISE_DURATION;
// new situation for everyone...
iLoop2 = gTacticalStatus.Team[ CIV_TEAM ].bFirstID;
for ( pSoldier = MercPtrs[iLoop2]; iLoop2 <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; iLoop2++, pSoldier++ )
{
if (pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife >= OKLIFE && pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP && pSoldier->ubProfile == NO_PROFILE)
{
SetNewSituation( pSoldier );
}
}
}
}
void CallEldinTo( INT32 sGridNo )
{
// like call all enemies, but only affects Eldin
SOLDIERTYPE * pSoldier;
// Eldin becomes aware of a very important "noise" coming from here!
// So long as he hasn't already heard a noise a sec ago...
if (gTacticalStatus.Team[CIV_TEAM].bTeamActive)
{
// new situation for Eldin
pSoldier = FindSoldierByProfileID( ELDIN, FALSE );
if ( pSoldier && pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife >= OKLIFE && (pSoldier->aiData.bAlertStatus == STATUS_GREEN || pSoldier->aiData.ubNoiseVolume < (MAX_MISC_NOISE_DURATION / 2) ) )
{
if ( SoldierTo3DLocationLineOfSightTest( pSoldier, sGridNo, 0, 0, TRUE ) )
{
// sees the player now!
TriggerNPCWithIHateYouQuote( ELDIN );
SetNewSituation( pSoldier );
}
else
{
pSoldier->aiData.sNoiseGridno = sGridNo;
pSoldier->aiData.ubNoiseVolume = MAX_MISC_NOISE_DURATION;
pSoldier->aiData.bAlertStatus = STATUS_RED;
if ( (pSoldier->aiData.bAction != AI_ACTION_GET_CLOSER) || CheckFact( FACT_MUSEUM_ALARM_WENT_OFF, 0 ) == FALSE )
{
CancelAIAction( pSoldier, TRUE );
pSoldier->aiData.bNextAction = AI_ACTION_GET_CLOSER;
pSoldier->aiData.usNextActionData = sGridNo;
RESETTIMECOUNTER( pSoldier->timeCounters.AICounter, 100 );
}
// otherwise let AI handle this normally
// SetNewSituation( pSoldier );
// reduce any delay to minimal
}
SetFactTrue( FACT_MUSEUM_ALARM_WENT_OFF );
}
}
}
INT32 MostImportantNoiseHeard( SOLDIERTYPE *pSoldier, INT32 *piRetValue, BOOLEAN * pfClimbingNecessary, BOOLEAN * pfReachable )
{
UINT32 uiLoop;
INT8 * pbPersOL, * pbPublOL;
INT32 *psLastLoc,*psNoiseGridNo;
INT8 * pbNoiseLevel;
INT8 *pbLastLevel;
UINT8 *pubNoiseVolume;
INT32 iDistAway;
INT32 iNoiseValue, iBestValue = -10000;
INT32 sBestGridNo = NOWHERE;
INT8 bBestLevel = 0;
INT32 sClimbingGridNo;
BOOLEAN fClimbingNecessary = FALSE;
SOLDIERTYPE * pTemp;
pubNoiseVolume = &gubPublicNoiseVolume[pSoldier->bTeam];
psNoiseGridNo = &gsPublicNoiseGridNo[pSoldier->bTeam];
pbNoiseLevel = &gbPublicNoiseLevel[pSoldier->bTeam];
psLastLoc = gsLastKnownOppLoc[pSoldier->ubID];
// hang pointers at start of this guy's personal and public opponent opplists
pbPersOL = pSoldier->aiData.bOppList;
pbPublOL = gbPublicOpplist[pSoldier->bTeam];
// look through this man's personal & public opplists for opponents heard
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
pTemp = MercSlots[ uiLoop ];
// if this merc is inactive, at base, on assignment, or dead
if (!pTemp || !pTemp->stats.bLife)
continue; // next merc
// if this merc is neutral/on same side, he's not an opponent
if ( CONSIDERED_NEUTRAL( pSoldier, pTemp ) || (pSoldier->bSide == pTemp->bSide))
continue; // next merc
pbPersOL = pSoldier->aiData.bOppList + pTemp->ubID;
pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pTemp->ubID;
psLastLoc = gsLastKnownOppLoc[pSoldier->ubID] + pTemp->ubID;
pbLastLevel = gbLastKnownOppLevel[pSoldier->ubID] + pTemp->ubID;
// if this guy's been personally heard within last 3 turns
if (*pbPersOL < NOT_HEARD_OR_SEEN)
{
// calculate how far this noise was, and its relative "importance"
iDistAway = SpacesAway(pSoldier->sGridNo,*psLastLoc);
iNoiseValue = (*pbPersOL) * iDistAway; // always a negative number!
if (iNoiseValue > iBestValue)
{
iBestValue = iNoiseValue;
sBestGridNo = *psLastLoc;
bBestLevel = *pbLastLevel;
}
}
// if this guy's been publicly heard within last 3 turns
if (*pbPublOL < NOT_HEARD_OR_SEEN)
{
// calculate how far this noise was, and its relative "importance"
iDistAway = SpacesAway(pSoldier->sGridNo,gsPublicLastKnownOppLoc[pSoldier->bTeam][pTemp->ubID]);
iNoiseValue = (*pbPublOL) * iDistAway; // always a negative number!
if (iNoiseValue > iBestValue)
{
iBestValue = iNoiseValue;
sBestGridNo = gsPublicLastKnownOppLoc[pSoldier->bTeam][pTemp->ubID];
bBestLevel = gbPublicLastKnownOppLevel[pSoldier->bTeam][pTemp->ubID];
}
}
}
// if any "misc. noise" was also heard recently
if (!TileIsOutOfBounds(pSoldier->aiData.sNoiseGridno))
{
if ( pSoldier->bNoiseLevel != pSoldier->pathing.bLevel || PythSpacesAway( pSoldier->sGridNo, pSoldier->aiData.sNoiseGridno ) >= 6 || SoldierTo3DLocationLineOfSightTest( pSoldier, pSoldier->aiData.sNoiseGridno, pSoldier->bNoiseLevel, 0, FALSE, NO_DISTANCE_LIMIT ) == 0 )
{
// calculate how far this noise was, and its relative "importance"
iDistAway = SpacesAway(pSoldier->sGridNo,pSoldier->aiData.sNoiseGridno);
iNoiseValue = ((pSoldier->aiData.ubNoiseVolume / 2) - 6) * iDistAway;
if (iNoiseValue > iBestValue)
{
iBestValue = iNoiseValue;
sBestGridNo = pSoldier->aiData.sNoiseGridno;
bBestLevel = pSoldier->bNoiseLevel;
}
}
else
{
// we are there or near
pSoldier->aiData.sNoiseGridno = NOWHERE; // wipe it out, not useful anymore
pSoldier->aiData.ubNoiseVolume = 0;
}
}
// if any recent PUBLIC "misc. noise" is also known
if ( (pSoldier->bTeam != CIV_TEAM) || ( pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP ) )
{
if (!TileIsOutOfBounds(*psNoiseGridNo))
{
// if we are NOT there (at the noise gridno)
if ( *pbNoiseLevel != pSoldier->pathing.bLevel || PythSpacesAway( pSoldier->sGridNo, *psNoiseGridNo ) >= 6 || SoldierTo3DLocationLineOfSightTest( pSoldier, *psNoiseGridNo, *pbNoiseLevel, 0, FALSE, NO_DISTANCE_LIMIT ) == 0 )
{
// calculate how far this noise was, and its relative "importance"
iDistAway = SpacesAway(pSoldier->sGridNo,*psNoiseGridNo);
iNoiseValue = ((*pubNoiseVolume / 2) - 6) * iDistAway;
if (iNoiseValue > iBestValue)
{
iBestValue = iNoiseValue;
sBestGridNo = *psNoiseGridNo;
bBestLevel = *pbNoiseLevel;
}
}
}
}
if (!TileIsOutOfBounds(sBestGridNo) && pfReachable )
{
*pfReachable = TRUE;
// make civs not walk to noises outside their room if on close patrol/onguard
if ( pSoldier->aiData.bOrders <= CLOSEPATROL && (pSoldier->bTeam == CIV_TEAM || pSoldier->ubProfile != NO_PROFILE ) )
{
UINT8 ubRoom, ubNewRoom;
// any other combo uses the default of ubRoom == 0, set above
if ( InARoom( pSoldier->aiData.sPatrolGrid[0], &ubRoom ) )
{
if ( !InARoom( pSoldier->aiData.sPatrolGrid[0], &ubNewRoom ) || ubRoom != ubNewRoom )
{
*pfReachable = FALSE;
}
}
}
if ( *pfReachable )
{
// if there is a climb involved then we should store the location
// of where we have to climb to instead
sClimbingGridNo = GetInterveningClimbingLocation( pSoldier, sBestGridNo, bBestLevel, &fClimbingNecessary );
if ( fClimbingNecessary )
{
if (TileIsOutOfBounds(sClimbingGridNo))
{
// can't investigate!
*pfReachable = FALSE;
}
else
{
sBestGridNo = sClimbingGridNo;
fClimbingNecessary = TRUE;
}
}
else
{
fClimbingNecessary = FALSE;
}
}
}
if ( piRetValue )
{
*piRetValue = iBestValue;
}
if ( pfClimbingNecessary )
{
*pfClimbingNecessary = fClimbingNecessary;
}
#ifdef DEBUGDECISIONS
if (!TileIsOutOfBounds(sBestGridNo))
AINumMessage("MOST IMPORTANT NOISE HEARD FROM GRID #",sBestGridNo);
#endif
return(sBestGridNo);
}
INT16 WhatIKnowThatPublicDont(SOLDIERTYPE *pSoldier, UINT8 ubInSightOnly)
{
UINT8 ubTotal = 0;
UINT32 uiLoop;
INT8 *pbPersOL,*pbPublOL;
SOLDIERTYPE * pTemp;
// if merc knows of a more important misc. noise than his team does
if (!(CREATURE_OR_BLOODCAT( pSoldier )) && (pSoldier->aiData.ubNoiseVolume > gubPublicNoiseVolume[pSoldier->bTeam]))
{
// the difference in volume is added to the "new info" total
ubTotal += pSoldier->aiData.ubNoiseVolume - gubPublicNoiseVolume[pSoldier->bTeam];
}
// hang pointers at start of this guy's personal and public opponent opplists
pbPersOL = &(pSoldier->aiData.bOppList[0]);
pbPublOL = &(gbPublicOpplist[pSoldier->bTeam][0]);
// for every opponent
// for (iLoop = 0; iLoop < MAXMERCS; iLoop++,pbPersOL++,pbPublOL++)
// {
// pTemp = &(Menptr[iLoop]);
for (uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
{
pTemp = MercSlots[ uiLoop ];
// if this merc is inactive, at base, on assignment, or dead
if (!pTemp)
{
continue; // next merc
}
// if this merc is neutral/on same side, he's not an opponent
if ( CONSIDERED_NEUTRAL( pSoldier, pTemp ) || (pSoldier->bSide == pTemp->bSide))
{
continue; // next merc
}
pbPersOL = pSoldier->aiData.bOppList + pTemp->ubID;
pbPublOL = gbPublicOpplist[pSoldier->bTeam] + pTemp->ubID;
// if we're only interested in guys currently is sight, and he's not
if (ubInSightOnly)
{
if ((*pbPersOL == SEEN_CURRENTLY) && (*pbPublOL != SEEN_CURRENTLY))
{
// just count the number of them
ubTotal++;
}
}
else
{
// add value of personal knowledge compared to public knowledge to total
ubTotal += gubKnowledgeValue[*pbPublOL - OLDEST_HEARD_VALUE][*pbPersOL - OLDEST_HEARD_VALUE];
}
}
#ifdef DEBUGDECISIONS
if (ubTotal > 0)
{
AINumMessage("WHAT I KNOW THAT PUBLIC DON'T = ",ubTotal);
}
#endif
return(ubTotal);
}