#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

extern SECTOR_EXT_DATA	SectorExternalData[256][4];

void CallAvailableEnemiesTo( INT32 sGridNo )
{
	// All enemy teams become aware of a very important "noise" coming from here!
	for ( INT32 iLoop = 0; iLoop < LAST_TEAM; ++iLoop )
	{
		CallAvailableTeamEnemiesTo( sGridNo, iLoop );
	}
}

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 )
				{
					DebugAI(AI_MSG_INFO, pSoldier, String("CancelAIAction: CallEldinTo"));
					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];

	// sevenfm: sector information
	UINT8 ubSectorId = SECTOR( gWorldSectorX, gWorldSectorY );
	UINT8 sectordata = SectorExternalData[ubSectorId][0].usCurfewValue;

	// 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

		// Flugente: chance to ignore the noise if the ceator is covert
		if ( pTemp->usSoldierFlagMask & (SOLDIER_COVERT_CIV|SOLDIER_COVERT_SOLDIER) )
		{
			// green  AI state: always ignore
			// yellow AI state: 50% chance to ignore
			if ( pSoldier->aiData.bAlertStatus == STATUS_GREEN || (pSoldier->aiData.bAlertStatus == STATUS_YELLOW && Random(2) ) )
				continue;

			// sevenfm: ignore noise if some friends already see opponent
			if ( CountTeamSeeSoldier( pSoldier->bTeam, pTemp ) > sectordata )
				continue;
		}

		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 ) )
		{
			//DBrot: More Rooms
			//UINT8	ubRoom, ubNewRoom;
			UINT16 usRoom, usNewRoom;

			// any other combo uses the default of ubRoom == 0, set above
			if ( InARoom( pSoldier->aiData.sPatrolGrid[0], &usRoom ) )
			{
				if ( !InARoom( pSoldier->aiData.sPatrolGrid[0], &usNewRoom ) || usRoom != usNewRoom )
				{
					*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);
}
