#include "builddefines.h"

#ifdef PRECOMPILEDHEADERS
	#include "Strategic All.h"
#else
	#include "Strategic Town Loyalty.h"
	#include "strategicmap.h"
	#include "Overhead.h"
	#include "Assignments.h"
	#include "strategic.h"
	#include "Queen Command.h"
	#include "Animation Data.h"
	#include "Quests.h"
	#include "Font.h"
	#include "Message.h"
	#include "LOS.h"
	#include "World Items.h"
	#include "Tactical Save.h"
	#include "Soldier Profile.h"
	#include "Handle Items.h"
	#include "random.h"
	#include "strategic movement.h"
	#include "Strategic Pathing.h"
	#include "vehicles.h"
	#include "Game Clock.h"
	#include "Game Event Hook.h"
	#include "Morale.h"
	#include "GameSettings.h"
	#include "Text.h"
	#include "MessageBoxScreen.h"
	#include "Town Militia.h"
	#include "history.h"
	#include "meanwhile.h"
	#include "Strategic Status.h"
	// HEADROCK HAM B1: Added include for Dynamic Roaming Militia
	#include "MilitiaSquads.h"
	// HEADROCK HAM 3.6: Include for Facility Debt
	#include "Facilities.h"
	#include "CampaignStats.h"		// added by Flugente
	#include "DynamicDialogue.h"			// added by Flugente
	#include "Rebel Command.h"
#endif

#include "Luaglobal.h"
#include "LuaInitNPCs.h"
#include "Interface.h"

#include "GameInitOptionsScreen.h"
extern WorldItems gAllWorldItems;

// loyalty Omerta drops to and maxes out at if the player betrays the rebels
#define HOSTILE_OMERTA_LOYALTY_RATING		10

// the loyalty threshold below which theft of unsupervised items in a town sector can occur
#define THRESHOLD_FOR_TOWN_THEFT 50

#define LOYALTY_EVENT_DISTANCE_THRESHOLD		3			// in sectors


// effect of unintentional killing /100 vs intentional murder
#define REDUCTION_FOR_UNINTENTIONAL_KILLING 50
// the effect of a murder by rebel / 100 vs player murdering a civ
#define REDUCTION_FOR_MURDER_BY_REBEL 70
// reduction % for being falsely blamed for a murder we didn't do, out of 100
#define REDUCTION_FOR_MURDER_NOT_OUR_FAULT 50
// reduction % if enemies kills civs in our sector / 100
#define REDUCTION_FOR_MURDER_OF_INNOCENT_BY_ENEMY_IN_OUR_SECTOR 25
// how much worse loyalty hit is for trauma of someone killed by a monster
#define MULTIPLIER_FOR_MURDER_BY_MONSTER 2



// gain for hiring an NPC from a particular town (represents max. at 100% town attachment)
#define MULTIPLIER_LOCAL_RPC_HIRED											25		// 5%

// multiplier for causing pts of damage directly done to a building
#define MULTIPLIER_FOR_DAMAGING_A_BUILDING							10		// 50 pts = 1%
// multiplier for not preventing pts of damage to a building
#define MULTIPLIER_FOR_NOT_PREVENTING_BUILDING_DAMAGE		3			// 167 pts = 1%

// divisor for dmg to a building by allied rebel
#define DIVISOR_FOR_REBEL_BUILDING_DMG									2


/*
// max number of soldiers counted towards town loyalty gain
#define MAX_SOLDIER_COUNT_FOR_IN_TOWN_LOYALTY_GAIN 6
// max levels of town militia civs counted towards town loyalty gain
#define MAX_MILITIA_VALUE_FOR_IN_TOWN_LOYALTY_GAIN	18
// max # occupying enemy ranks that count for loyalty penalty
#define MAX_ENEMY_SOLDIER_RANKS_FOR_IN_TOWN_LOYALTY_DROP 15
// number of ranks counted per regular
#define NUMBER_OF_RANKS_PER_REGULAR 2
// number of ranks counted per elite
#define NUMBER_OF_RANKS_PER_ELITE 4

// THESE VALUES ARE ALL AFFECTED BY CHANGES to "GAIN_PTS_PER_LOYALTY_PT", SO BEWARE IF THAT SHOULD CHANGE
//
// max. rate of loyalty gain for local RPC being stationed in his home town
#define HOURLY_GAIN_FOR_LOCAL_NPC_IN_TOWN 20	// roughly 1.00% / day max.
// number of gain pts per merc in town
#define HOURLY_GAIN_PER_MERC_IN_TOWN 5				// roughly 0.25% / day
// number of gain pts per friendly in town
#define HOURLY_GAIN_PER_MILITIA_IN_TOWN 1			// roughly 0.05% / day
// loyalty loss for enemies in town
#define HOURLY_DROP_PER_ENEMY_RANK_IN_TOWN 2	// roughly 0.10% / day
*/
//
// THESE VALUES ARE ALL AFFECTED BY CHANGES to "GAIN_PTS_PER_LOYALTY_PT", SO BEWARE IF THAT SHOULD CHANGE


/* Delayed loyalty effects elimininated.	Sep.12/98.	ARM

// macros for delayed town loyalty events
// get increment
#define GET_TOWN_INCREMENT( iValue ) ( ( uiValue << 31 ) >> 31 )
// town id
#define GET_TOWN_ID_FOR_LOYALTY( iValue ) ( uiValue >> 24 )
// the amount
#define GET_TOWN_LOYALTY_CHANGE( iValue ) ( ( uiValue << 8 ) >> 9 )
*/


// town loyalty table
TOWN_LOYALTY gTownLoyalty[ MAX_TOWNS ];


// town name and locations arrays, for town theft and what not
INT32 pTownNamesList	 [ MAX_TOWN_SECTORS ];
INT32 pTownLocationsList [ MAX_TOWN_SECTORS ];

INT32 iTownDistances[ MAX_TOWNS ][ MAX_TOWNS ];


#define BASIC_COST_FOR_CIV_MURDER	(10 * GAIN_PTS_PER_LOYALTY_PT)

UINT32 uiPercentLoyaltyDecreaseForCivMurder[]={
	// These get multiplied by GAIN_PTS_PER_LOYALTY_PT so they're in % of loyalty decrease (for an average town)
	(5 * GAIN_PTS_PER_LOYALTY_PT),	// fat civ
	(7 * GAIN_PTS_PER_LOYALTY_PT),	// man civ
	(8 * GAIN_PTS_PER_LOYALTY_PT),	// min civ
	(10 * GAIN_PTS_PER_LOYALTY_PT),	// dress (woman)
	(20 * GAIN_PTS_PER_LOYALTY_PT), // hat kid
	(20 * GAIN_PTS_PER_LOYALTY_PT), // kid
	(20 * GAIN_PTS_PER_LOYALTY_PT),	// cripple
	 (1 * GAIN_PTS_PER_LOYALTY_PT),	// cow
};


// on a scale of 1-100, this is a measure of how much each town hates the Queen's opression & is willing to stand against it
// it primarily controls the RATE of loyalty change in each town: the loyalty effect of the same events depends on it
UINT8 gubTownRebelSentiment[ MAX_TOWNS ]; // =
//{
//	0,	// not a town - blank sector index
// 90,	// OMERTA,	- They ARE the rebels!!!
// 30,	// DRASSEN,	- Rebel friendly, makes it pretty easy to get first mine's income going at the start
// 12,	// ALMA			- Military town, high loyalty to Queen, need quests to get 100%
// 15,	// GRUMM,		- Close to Meduna, strong influence
// 20,	// TIXA,		- Not a real town
// 15,	// CAMBRIA, - Artificially much lower 'cause it's big and central and too easy to get loyalty up there
// 20,	// SAN_MONA,- Neutral ground, loyalty doesn't vary
// 20,	// ESTONI,	- Not a real town
// 20,	// ORTA,		- Not a real town
// 12,	// BALIME,	- Rich town, high loyalty to Queen
// 10,	// MEDUNA,	- Enemy HQ, for God's sake!
// 35,	// CHITZENA, - Artificially high 'cause there's not enough fights near it to get the loyalty up otherwise
//};

BOOLEAN gfTownUsesLoyalty[ MAX_TOWNS ];// =
//{
//	FALSE,		// not a town - blank sector index
//	TRUE	,		// OMERTA
//	TRUE,			// DRASSEN
//	TRUE,			// ALMA
//	TRUE,			// GRUMM
//	FALSE,		// TIXA
//	TRUE,			// CAMBRIA
//	FALSE,		// SAN_MONA
//	FALSE,		// ESTONI
//	FALSE,		// ORTA
//	TRUE,			// BALIME
//	TRUE,			// MEDUNA
//	TRUE,			// CHITZENA
//};

// location of first enocunter with enemy
INT16 sWorldSectorLocationOfFirstBattle = 0;

// preprocess sector for mercs in it
extern BOOLEAN fSectorsWithSoldiers[ MAP_WORLD_X * MAP_WORLD_X ][ 4 ];

extern CHAR16 pTownNames[MAX_TOWNS][MAX_TOWN_NAME_LENGHT];



// update the loyalty rating of the passed town id
void UpdateTownLoyaltyRating( INT8 bTownId );





// update town loyalty based on number of friendlies in this town
void UpdateTownLoyaltyBasedOnFriendliesInTown( INT8 bTownId );

// update town loyalty based on number of bad guys in this town
void UpdateTownLoyaltyBasedOnBadGuysInTown( INT8 bTownId );

/* ARM: Civilian theft of items was removed
// handle theft by civi in a town sector
void HandleTheftByCiviliansInSector( INT16 sX, INT16 sY, INT32 iLoyalty );

// handle theft in all towns
void HandleTownTheft( void );
*/

extern void MapScreenDefaultOkBoxCallback( UINT8 bExitValue );

extern void CheckConditionsForTriggeringCreatureQuest( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ );

void InitTownLoyalty( void )
{
	UINT8 ubTown = 0;

	// set up town loyalty table
	for( ubTown = FIRST_TOWN; ubTown < NUM_TOWNS; ubTown++ )
	{
		gTownLoyalty[ ubTown ].ubRating = 0;
		gTownLoyalty[ ubTown ].sChange = 0;
		gTownLoyalty[ ubTown ].fStarted = FALSE;
//		gTownLoyalty[ ubTown ].ubRebelSentiment = gubTownRebelSentiment[ ubTown ];
		gTownLoyalty[ ubTown ].fLiberatedAlready = FALSE;
	}

	return;
}


void StartTownLoyaltyIfFirstTime( INT8 bTownId )
{
	Assert( ( bTownId >= FIRST_TOWN ) && ( bTownId < NUM_TOWNS ) );

	// if loyalty tracking hasn't yet been started for this town, and the town does use loyalty
	if (!gTownLoyalty[ bTownId ].fStarted && gfTownUsesLoyalty[ bTownId ])
	{
		// set starting town loyalty now, equally to that town's current rebel sentiment - not all towns begin equally loyal
		gTownLoyalty[ bTownId ].ubRating = gubTownRebelSentiment[ bTownId ];

		// if player hasn't made contact with Miguel yet, or the rebels hate the player
		if (!CheckFact(FACT_MIGUEL_READ_LETTER, 0) || CheckFact( FACT_REBELS_HATE_PLAYER, 0 ))
		{
			// if town is Omerta
			if (bTownId == OMERTA)
			{
				// start loyalty there at 0, since rebels distrust the player until Miguel receives the letter
				gTownLoyalty[ bTownId ].ubRating	= 0;
			}
			else
			{
				// starting loyalty is halved - locals not sure what to make of the player's presence
				gTownLoyalty[ bTownId ].ubRating /= 2;
			}
		}

		gTownLoyalty[ bTownId ].sChange = 0;

		// remember we've started
		gTownLoyalty[ bTownId ].fStarted = TRUE;
	}
}


// set a specified town's loyalty rating (ignores previous loyalty value - probably NOT what you want)
void SetTownLoyalty( INT8 bTownId, UINT8 ubNewLoyaltyRating )
{
	Assert( ( bTownId >= FIRST_TOWN ) && ( bTownId < NUM_TOWNS ) );

	// if the town does use loyalty
	if (gfTownUsesLoyalty[ bTownId ])
	{
		gTownLoyalty[ bTownId ].ubRating = ubNewLoyaltyRating;
		gTownLoyalty[ bTownId ].sChange = 0;

		// this is just like starting the loyalty if it happens first
		gTownLoyalty[ bTownId ].fStarted = TRUE;
	}

	return;
}


// increments the town's loyalty rating by that many HUNDREDTHS of loyalty pts
void IncrementTownLoyalty( INT8 bTownId, UINT32 uiLoyaltyIncrease )
{
	UINT32 uiRemainingIncrement;
	INT16 sThisIncrement;

	AssertGE (bTownId, BLANK_SECTOR);
	AssertLT (bTownId, NUM_TOWNS);

	// doesn't affect towns where player hasn't established a "presence" yet
	if (!gTownLoyalty[ bTownId ].fStarted)
	{
		return;
	}

	// if rebel command is enabled, apply gain modifier
	uiLoyaltyIncrease *= RebelCommand::GetLoyaltyGainModifier();

	// modify loyalty change by town's individual attitude toward rebelling (20 is typical)
	uiLoyaltyIncrease *= (5 * gubTownRebelSentiment[ bTownId ]);
	uiLoyaltyIncrease /= 100;


	// this whole thing is a hack to avoid rolling over the -32 to 32k range on the sChange value
	// only do a maximum of 10000 pts at a time...
	uiRemainingIncrement = uiLoyaltyIncrease;
	while ( uiRemainingIncrement )
	{
		sThisIncrement = ( INT16 ) min( uiRemainingIncrement, 10000 );

		// up the gain value
		gTownLoyalty[ bTownId ].sChange += (INT16) sThisIncrement;
		// update town value now
		UpdateTownLoyaltyRating( bTownId );

		uiRemainingIncrement -= sThisIncrement;
	}

	return;
}

// decrements the town's loyalty rating by that many HUNDREDTHS of loyalty pts
//NOTE: This function expects a POSITIVE number for a decrease!!!
void DecrementTownLoyalty( INT8 bTownId, UINT32 uiLoyaltyDecrease )
{
	UINT32 uiRemainingDecrement;
	INT16 sThisDecrement;

	Assert( ( bTownId >= FIRST_TOWN ) && ( bTownId < NUM_TOWNS ) );

	// doesn't affect towns where player hasn't established a "presence" yet
	if (!gTownLoyalty[ bTownId ].fStarted)
	{
		return;
	}

	// anv: snitches spreading propaganda affect decrease amount
	uiLoyaltyDecrease = HandlePropagandaBlockingBadNewsInTown( bTownId, uiLoyaltyDecrease );

	// modify loyalty change by town's individual attitude toward rebelling (20 is typical)
	uiLoyaltyDecrease *= 100;
	uiLoyaltyDecrease /= (5 * gubTownRebelSentiment[ bTownId ]);

	// this whole thing is a hack to avoid rolling over the -32 to 32k range on the sChange value
	// only do a maximum of 10000 pts at a time...
	uiRemainingDecrement = uiLoyaltyDecrease;
	while ( uiRemainingDecrement )
	{
		sThisDecrement = ( INT16 ) min( uiRemainingDecrement, 10000 );

		// down the gain value
		gTownLoyalty[ bTownId ].sChange -= sThisDecrement;
		// update town value now
		UpdateTownLoyaltyRating( bTownId );

		uiRemainingDecrement -= sThisDecrement;
	}
}


// update town loyalty rating based on gain values
void UpdateTownLoyaltyRating( INT8 bTownId )
{
	// check gain value and update loyaty
	UINT8 ubOldLoyaltyRating = 0;
	INT16 sRatingChange = 0;
	UINT8 ubMaxLoyalty = 0;

	Assert( ( bTownId >= FIRST_TOWN ) && ( bTownId < NUM_TOWNS ) );

	// remember previous loyalty value
	ubOldLoyaltyRating = gTownLoyalty[ bTownId ].ubRating;

	sRatingChange = gTownLoyalty[ bTownId ].sChange / GAIN_PTS_PER_LOYALTY_PT;

	// if loyalty is ready to increase
	if( sRatingChange > 0 )
	{
		// if the town is Omerta, and the rebels are/will become hostile
		if ((bTownId == OMERTA) && (gTacticalStatus.fCivGroupHostile[ REBEL_CIV_GROUP ] != CIV_GROUP_NEUTRAL))
		{
			// maximum loyalty is much less than normal
			ubMaxLoyalty = HOSTILE_OMERTA_LOYALTY_RATING;
		}
		else
		{
			ubMaxLoyalty = MAX_LOYALTY_VALUE;
		}

		// if playing with rebel command, limit max loyalty
		ubMaxLoyalty = min(ubMaxLoyalty, RebelCommand::GetMaxTownLoyalty(bTownId));

		// check if we'd be going over the max
		if( (gTownLoyalty[ bTownId ].ubRating + sRatingChange ) >= ubMaxLoyalty )
		{
			// set to max and null out gain pts
			gTownLoyalty[ bTownId ].ubRating = ubMaxLoyalty;
			gTownLoyalty[ bTownId ].sChange = 0;
		}
		else
		{
			// increment loyalty rating, reduce sChange
			gTownLoyalty[ bTownId ].ubRating += sRatingChange;
			gTownLoyalty[ bTownId ].sChange %= GAIN_PTS_PER_LOYALTY_PT;
		}
	}
	else
	// if loyalty is ready to decrease
	if( sRatingChange < 0 )
	{
		// check if we'd be going below zero
		if( ( gTownLoyalty[ bTownId ].ubRating + sRatingChange ) < 0 )
		{
			// set to zero and null out gain pts
			gTownLoyalty[ bTownId ].ubRating = 0;
			gTownLoyalty[ bTownId ].sChange = 0;
		}
		else
		{
			// decrement loyalty rating, reduce sChange
			gTownLoyalty[ bTownId ].ubRating += sRatingChange;
			gTownLoyalty[ bTownId ].sChange %= GAIN_PTS_PER_LOYALTY_PT;
		}
	}


	// check old aginst new, if diff, dirty map panel
	if( ubOldLoyaltyRating != gTownLoyalty[ bTownId ].ubRating )
	{
		fMapPanelDirty = TRUE;
	}
}


// strategic handler, goes through and handles all strategic events for town loyalty updates...player controlled, monsters
void HandleTownLoyalty( void )
{

/* ARM: removed to experiment with keeping loyalty from drifing without any direct causes from the player
	for( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; bTownId++ )
	{
		// update loyalty of controlled/uncontrolled towns
		UpdateLoyaltyBasedOnControl( bTownId );

		// update based on number of good guys in sector
		UpdateTownLoyaltyBasedOnFriendliesInTown( bTownId );

		// update bad on number of badies in sector
		UpdateTownLoyaltyBasedOnBadGuysInTown( bTownId );

		// finally update town loyalty
		UpdateTownLoyaltyRating( bTownId );
	}
*/

	// now all loyalty is done, handle other stuff

/* ARM: Civilian theft of items was removed
	// only check for theft every 12 hours, otherwise it's way too slow
	if ( (GetWorldHour() % 12) == 0)
	{
		HandleTownTheft( );
	}
*/

	return;
}



/*
// update of town loyalty based on if the player controls the town's sectors
void UpdateLoyaltyBasedOnControl( INT8 bTownId )
{
	// compare current loyalty to percent of sectors controlled, adjust in that direction
	INT32 iUnderControl = 0;

	Assert( ( bTownId >= FIRST_TOWN ) && ( bTownId < NUM_TOWNS ) );

	// find how much of town is under control
	iUnderControl = GetTownSectorsUnderControl( bTownId );
	iUnderControl *= 100;
	iUnderControl /= GetTownSectorSize( bTownId );

	// the gain value will be the difference in iUnderControl and the current Loyalty rating / hours_per_day
	if( gTownLoyalty[ bTownId ].ubRating < ( UINT8 ) iUnderControl )
	{
		// we control more of the town, start moving to more loyal
		IncrementTownLoyalty( bTownId, ( iUnderControl - gTownLoyalty[ bTownId ].ubRating) );
	}
	else
	{
		// mere lack of sector control never reduces loyalty below the town's current rebel sentiment
		if (gTownLoyalty[ bTownId ].ubRating > gubTownRebelSentiment[ bTownId ])
		{
			// we control less of the town, decrement gain
			DecrementTownLoyalty( bTownId, gTownLoyalty[ bTownId ].ubRating - iUnderControl );
		}
	}

	return;
}


// updates the loyalty of the town in a sector based on number of friendly troops in that sector ...to a point, after max number to
void UpdateTownLoyaltyBasedOnFriendliesInTown( INT8 bTownId )
{
	// check if valid town
	INT32 iUnderControl = 0;
	SOLDIERTYPE *pSoldier;
	INT32 iCounter = 0;
	INT32 iSoldierCount = 0;
	INT32 iLocalNPCBonus = 0;
	INT32 iMilitiaValue = 0;
	UINT8 ubMilitiaLevel = 0;


	Assert( ( bTownId >= FIRST_TOWN ) && ( bTownId < NUM_TOWNS ) );

	// find how much of town is under control
	iUnderControl = GetTownSectorsUnderControl( bTownId );
	iUnderControl *= 100;
	iUnderControl /= GetTownSectorSize( bTownId );


	// count how many of player's mercs are active in this town, then factor by number of sectors under control

	// run through list of grunts on team
	for (iCounter = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; iCounter <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; iCounter++ )
	{
		pSoldier = MercPtrs[ iCounter ];

		if ( pSoldier->stats.bLife >= OKLIFE && pSoldier->bActive )
		{
			// if soldier is in this sector
			if( SectorIsPartOfTown( bTownId, pSoldier->sSectorX, pSoldier->sSectorY ) == TRUE )
			{
				// if onduty or in a vehicle
				if( ( pSoldier->bAssignment < ON_DUTY ) || pSoldier->bAssignment == VEHICLE ) )
				{
					// increment soldier count
					iSoldierCount++;
				}

				// local influence: if the town is the character's home town
				if( bTownId == gMercProfiles[ pSoldier->ubProfile ].bTown )
				{
					// he needn't be soldiering to have an impact...	presence is enough
					if( pSoldier->bAssignment < ASSIGNMENT_DEAD )
					{
						iLocalNPCBonus = HOURLY_GAIN_FOR_LOCAL_NPC_IN_TOWN;
						iLocalNPCBonus *= gMercProfiles[ pSoldier->ubProfile ].bTownAttachment;
						iLocalNPCBonus /= 100;

						// adjust for % town control
						iLocalNPCBonus *= iUnderControl;
						iLocalNPCBonus /= 100;

						// do we actually gain from this?
						if( iLocalNPCBonus > 0 )
						{
							// debug message
							ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"%s influences loyalty in home town of %s, worth %d", pSoldier->name, pTownNames[ bTownId ], iLocalNPCBonus);

							// increment town loyalty
							IncrementTownLoyalty( bTownId, iLocalNPCBonus );
						}
					}
				}
			}
		}
	}


	// soldier count is limited by a max number
	if( iSoldierCount > MAX_SOLDIER_COUNT_FOR_IN_TOWN_LOYALTY_GAIN )
	{
		iSoldierCount = MAX_SOLDIER_COUNT_FOR_IN_TOWN_LOYALTY_GAIN;
	}

	// adjust for % town control
	iSoldierCount *= iUnderControl;
	iSoldierCount /= 100;

	// add loyalty gain due to mercs
	IncrementTownLoyalty( bTownId, iSoldierCount * HOURLY_GAIN_PER_MERC_IN_TOWN );


	// find out if there are any militia anywhere in this town
	iMilitiaValue = 0;
	for( ubMilitiaLevel = 0; ubMilitiaLevel < MAX_MILITIA_LEVELS; ubMilitiaLevel++ )
	{
		// regulars/elites are worth double/triple normal
		iMilitiaValue += ((ubMilitiaLevel + 1) * GetMilitiaCountAtLevelAnywhereInTown( bTownId, ubMilitiaLevel ));
	}

	// militia count is limited by a max number
	if( iMilitiaValue > MAX_MILITIA_VALUE_FOR_IN_TOWN_LOYALTY_GAIN )
	{
		iMilitiaValue = MAX_MILITIA_VALUE_FOR_IN_TOWN_LOYALTY_GAIN;
	}

	// adjust for % town control
	iMilitiaValue *= iUnderControl;
	iMilitiaValue /= 100;

	// add loyalty gain due to militia
	IncrementTownLoyalty( bTownId, iMilitiaValue * HOURLY_GAIN_PER_MILITIA_IN_TOWN );

	return;
}


void UpdateTownLoyaltyBasedOnBadGuysInTown( INT8 bTownId )
{
	// will update a town's loyalty based on number and type of bad guys in it
	INT32 iTotalEnemies = 0;
	INT16 sSectorX =0, sSectorY = 0;
	UINT8 ubAdmins, ubRegs, ubElites;
	INT32 iUnderControl;

	// find which sectors are this town and look at bad guys in those sectors
	// adjust for number of sectors bad guys control...like the player
	for( sSectorX = 0; sSectorX < MAP_WORLD_X; sSectorX++ )
	{
		for( sSectorY = 0; sSectorY < MAP_WORLD_Y; sSectorY++ )
		{
			// check if sector belongs to this town and is controlled by bad guys
			if ( ( SectorIsPartOfTown( bTownId, sSectorX, sSectorY ) == TRUE ) && ( StrategicMap[ sSectorX + sSectorY * MAP_WORLD_X ].fEnemyControlled == TRUE ) )
			{
				// controlled by bad guys..adjust numbers for type
				GetNumberOfEnemiesInSector( sSectorX, sSectorY, &ubAdmins, &ubRegs, &ubElites );

				// administrators
				iTotalEnemies += ( INT32 ) ubAdmins;
				// regulars
				iTotalEnemies += ( INT32 ) ubRegs * NUMBER_OF_RANKS_PER_REGULAR;
				// elites
			iTotalEnemies += ( INT32 ) ubElites * NUMBER_OF_RANKS_PER_ELITE;
			}
		}
	}


	// check vs. maximum influence possible
	if( iTotalEnemies > MAX_ENEMY_SOLDIER_RANKS_FOR_IN_TOWN_LOYALTY_DROP )
	{
		// we have, adjust
		iTotalEnemies = MAX_ENEMY_SOLDIER_RANKS_FOR_IN_TOWN_LOYALTY_DROP;
	}


	// find how much of town is under enemy control
	iUnderControl = GetTownSectorSize( bTownId ) - GetTownSectorsUnderControl( bTownId );
	iUnderControl *= 100;
	iUnderControl /= GetTownSectorSize( bTownId );

	// adjust number of enemies for town control
	iTotalEnemies *= iUnderControl;
	iTotalEnemies /= 100;

	// adjust loyalty downward for enemies
	DecrementTownLoyalty( bTownId, iTotalEnemies * HOURLY_DROP_PER_ENEMY_RANK_IN_TOWN );
}
*/



void HandleMurderOfCivilian( SOLDIERTYPE *pSoldier, BOOLEAN fIntentional )
{
	// handle the impact on loyalty of the murder of a civilian
	INT8 bTownId = 0;
	INT32 iLoyaltyChange = 0;
	INT8 bSeenState = 0;
	INT32 iCounter = 0;
	SOLDIERTYPE *pCivSoldier = NULL;
	UINT32 uiChanceFalseAccusal = 0;
	INT8 bKillerTeam = 0;
	BOOLEAN fIncrement = FALSE;
	
	// ubAttacker CAN be NOBODY...	Don't treat is as murder if NOBODY killed us...
	if ( pSoldier->ubAttackerID == NOBODY )
	{
		return;
	}

	// ignore murder of non-civilians!
	if ( ( pSoldier->bTeam != CIV_TEAM ) || ( pSoldier->ubBodyType == CROW ) )
	{
		return;
	}

	// if this is a profiled civilian NPC
	if ( pSoldier->ubProfile != NO_PROFILE )
	{
		// ignore murder of NPCs if they're not loyal to the rebel cause - they're really just enemies in civilian clothing
		if ( gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags3 & PROFILE_MISC_FLAG3_TOWN_DOESNT_CARE_ABOUT_DEATH )
		{
			return;
		}
	}

	// Flugente: if a civilian's group has Loyalty set to 0, there will be no morale penalty/loyalty change if he is killed. Pretty odd, but at least not as bugged as it was before. 
	// if civilian belongs to a civilian group
	if ( pSoldier->ubCivilianGroup != NON_CIV_GROUP && pSoldier->ubCivilianGroup < NUM_CIV_GROUPS )
	{
		if ( !zCivGroupName[pSoldier->ubCivilianGroup].Loyalty )
			return;
	}
	
	/*
	// if civilian belongs to a civilian group
	if ( pSoldier->ubCivilianGroup != NON_CIV_GROUP )
	{
		// and it's one that is hostile to the player's cause
		switch ( pSoldier->ubCivilianGroup )
		{
			case KINGPIN_CIV_GROUP:
			case ALMA_MILITARY_CIV_GROUP:
			case HICKS_CIV_GROUP:
			case WARDEN_CIV_GROUP:
				return;
		}
		
		//New Group by Jazz
		for( iCounter2 = UNNAMED_CIV_GROUP_15; iCounter2 < NUM_CIV_GROUPS; iCounter2++ )	
			{	
			if (pSoldier->ubCivilianGroup == iCounter2)
					return;		
			}
		
		
	}
	*/
	// set killer team
	bKillerTeam = Menptr[ pSoldier->ubAttackerID ].bTeam;


	// if the player did the killing
	if( bKillerTeam == OUR_TEAM )
	{
		SOLDIERTYPE *pKiller = MercPtrs[ pSoldier->ubAttackerID ];

		// apply morale penalty for killing a civilian!
		HandleMoraleEvent( pKiller, MORALE_KILLED_CIVILIAN, pKiller->sSectorX, pKiller->sSectorY, pKiller->bSectorZ );
	}


	// get town id
	bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY );

	// if civilian is NOT in a town
	if( bTownId == BLANK_SECTOR )
	{
		return;
	}

	// check if this town does use loyalty (to skip a lot of unnecessary computation)
	if (!gfTownUsesLoyalty[ bTownId ])
	{
		return;
	}


	if( ( pSoldier->ubBodyType >= FATCIV) && ( pSoldier->ubBodyType <= COW ) )
	{
		// adjust value for killer and type
		iLoyaltyChange = uiPercentLoyaltyDecreaseForCivMurder[ pSoldier->ubBodyType - FATCIV ];
	}
	else
	{
		iLoyaltyChange = BASIC_COST_FOR_CIV_MURDER;
	}

	if( !fIntentional )
	{
		// accidental killing, reduce value
		iLoyaltyChange *= REDUCTION_FOR_UNINTENTIONAL_KILLING;
		iLoyaltyChange /= 100;
	}

	// check if LOS between any civ, killer and killed
	// if so, then do not adjust

	for( iCounter = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; iCounter <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; iCounter++ )
	{
		// set current civ soldier
		pCivSoldier = MercPtrs[ iCounter ];

		if ( pCivSoldier == pSoldier )
		{
			continue;
		}

		// killer seen by civ?
		if ( SoldierToSoldierLineOfSightTest( pCivSoldier, MercPtrs[ pSoldier->ubAttackerID ], TRUE, gGameExternalOptions.ubStraightSightRange ) != 0 )
		{
			bSeenState |= 1;
		}

		// victim seen by civ?
		if( SoldierToSoldierLineOfSightTest( pCivSoldier, pSoldier, TRUE, gGameExternalOptions.ubStraightSightRange ) != 0 )
		{
			bSeenState |= 2;
		}
	}

	// if player didn't do it
	if( bKillerTeam != OUR_TEAM )
	{
		// If the murder is not fully witnessed, there's a chance of player being blamed for it even if it's not his fault
		switch (bSeenState)
		{
			case 0:
				// nobody saw anything.	When body is found, chance player is blamed depends on the town's loyalty at this time
				uiChanceFalseAccusal = MAX_LOYALTY_VALUE - gTownLoyalty[ bTownId ].ubRating;
				break;
			case 1:
				// civilians saw the killer, but not the victim. 10 % chance of blaming player whether or not he did it
				uiChanceFalseAccusal = 10;
				break;
			case 2:
				// civilians only saw the victim.	50/50 chance...
				uiChanceFalseAccusal = 50;
				break;
			case 3:
				// civilians have seen it all, and in this case, they're always honest
				uiChanceFalseAccusal = 0;
				break;
			default:
				Assert(FALSE);
				return;
		}

		// check whether player is falsely accused
		if( Random(100) < uiChanceFalseAccusal )
		{
			// blame player whether or not he did it - set killer team as our team
			bKillerTeam = OUR_TEAM;

			// not really player's fault, reduce penalty, because some will believe it to be a lie
			iLoyaltyChange *= REDUCTION_FOR_MURDER_NOT_OUR_FAULT;
			iLoyaltyChange /= 100;

			// debug message
			ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"You're being blamed for a death you didn't cause!");
		}
	}


	// check who town thinks killed the civ
	switch (bKillerTeam)
	{
		case OUR_TEAM:
			// town thinks player committed the murder, bad bad bad
			fIncrement = FALSE;

			// debug message
			ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Civilian killed by friendly forces.");
			break;

		case ENEMY_TEAM:
			// check whose sector this is
			if( StrategicMap[( pSoldier->sSectorX ) + ( MAP_WORLD_X * ( pSoldier->sSectorY ) )].fEnemyControlled == TRUE )
			{
				// enemy soldiers... in enemy controlled sector.	Gain loyalty
				fIncrement = TRUE;

				// debug message
				ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Enemy soldiers murdered a civilian. Town loyalty increases");
			}
			else
			{
				// reduce, we're expected to provide some protection, but not miracles
				iLoyaltyChange *= REDUCTION_FOR_MURDER_OF_INNOCENT_BY_ENEMY_IN_OUR_SECTOR;
				iLoyaltyChange /= 100;

				// lose loyalty
				fIncrement = FALSE;

				// debug message
				ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Town holds you responsible for murder by enemy.");
			}
			break;

		case MILITIA_TEAM:
			// the rebels did it... check if are they on our side
			if( CheckFact( FACT_REBELS_HATE_PLAYER, 0 ) == FALSE )
			{
				// on our side, penalty
				iLoyaltyChange *= REDUCTION_FOR_MURDER_BY_REBEL;
				iLoyaltyChange /= 100;

				// lose loyalty
				fIncrement = FALSE;

				// debug message
				ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Town holds you responsible for murder by rebels.");
			}
			break;

	case CREATURE_TEAM:
			// killed by a monster - make sure it was one
			if( ( Menptr[ pSoldier->ubAttackerID ].ubBodyType >= ADULTFEMALEMONSTER ) && ( Menptr[ pSoldier->ubAttackerID ].ubBodyType <= QUEENMONSTER ) )
			{
				// increase for the extreme horror of being killed by a monster
				iLoyaltyChange *= MULTIPLIER_FOR_MURDER_BY_MONSTER;

				// check whose sector this is
				if( StrategicMap[CALCULATE_STRATEGIC_INDEX( pSoldier->sSectorX, pSoldier->sSectorY )].fEnemyControlled == TRUE )
				{
					// enemy controlled sector - gain loyalty
					fIncrement = TRUE;
				}
				else
				{
					// our sector - lose loyalty
					fIncrement = FALSE;
				}
			}
			break;

	// Flugente: civilians might also kill somebody, no matter how unlikely that sounds
	// punish the player - he didnt stop the bloodshed
	case CIV_TEAM:
		{
			// on our side, penalty
			iLoyaltyChange *= REDUCTION_FOR_MURDER_NOT_OUR_FAULT;
			iLoyaltyChange /= 100;

			// lose loyalty
			fIncrement = FALSE;

			// debug message
			ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Town holds you responsible for not stopping the murder.");
		}
		break;

		default:
			Assert(FALSE);
			return;
	}


/* Delayed loyalty effects elimininated.	Sep.12/98.	ARM
	// figure out how long before the news of the murder spreads and lowers town loyalty
	if (bSeenState == 0)
	{
		// nobody saw nothing.	Will be some time before the grisly remains are discovered...
		uiLoyaltyEffectDelay = 60 * (1 + Random(12));	// 1-12 hrs in minutes
	}
	else
	{
		// there are witnesses, news reported & spread very quickly
		uiLoyaltyEffectDelay = 30;
	}


	// create the event value, with bTownId & for iLoyaltyChange as data
	uiValue = BuildLoyaltyEventValue( bTownId , iLoyaltyChange, fIncrement );
	// post the event
	AddStrategicEvent( EVENT_TOWN_LOYALTY_UPDATE, GetWorldTotalMin() + uiLoyaltyEffectDelay, uiValue );

	// ideally, we'd like to also call AffectAllTownsLoyaltyByDistanceFrom() here to spread the news to all other towns,
	// but we don't have that available on a delay right now, and it would be a bit of a pain, since we only have 32 bits
	// of event data to pass in, and that would have to store iLoyaltyChange, sSectorX, and sSectorY (64 bits)
*/


	// if it's a decrement, negate the value
	if ( !fIncrement )
	{
		iLoyaltyChange *= -1;
	}

	// this is a hack: to avoid having to adjust the values, divide by 1.75 to compensate for the distance 0
	iLoyaltyChange *= 100;
	iLoyaltyChange /= (100 + ( 25 * LOYALTY_EVENT_DISTANCE_THRESHOLD ) );

	AffectAllTownsLoyaltyByDistanceFrom( iLoyaltyChange, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
}



// check town and raise loyalty value for hiring a merc from a town...not a lot of a gain, but some
void HandleTownLoyaltyForNPCRecruitment( SOLDIERTYPE *pSoldier )
{
	INT8 bTownId = 0;
	UINT32 uiLoyaltyValue = 0;

	// get town id civilian
	bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY );

	// is the merc currently in their home town?
	if( bTownId == gMercProfiles[ pSoldier->ubProfile ].bTown )
	{
		// yep, value of loyalty bonus depends on his importance to this to town
		uiLoyaltyValue = MULTIPLIER_LOCAL_RPC_HIRED * gMercProfiles[ pSoldier->ubProfile ].bTownAttachment;

		// increment town loyalty gain
		IncrementTownLoyalty( bTownId, uiLoyaltyValue );

		// DESIGN QUESTION: How easy is it to abuse this bonus by repeatedly hiring the guy over & over
		// (at worst daily? even more often if terminated & rehired?)	(ARM)
	}

	return;
}


BOOLEAN HandleLoyaltyAdjustmentForRobbery( SOLDIERTYPE *pSoldier )
{
	// not to be implemented at this time
	return( FALSE );

/*
	// this function will handle robbery by the passed soldiertype of an object from a town he/she is in
	// it will check LOS code for all civilians around the stealing soldier if any of them have LOS to the soldier
	// then roll against thief's dexterity, if fail then you were caught( adjust loyalty base on theft and value of the object and if the object is owned by one of the people with LOS) and return a TRUE
	// otherwise return false.

	INT8 bTownId = 0;

	// get town id
	bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY );


	// debug message
	ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Theft not yet implemented.");

	return ( FALSE );
*/
}


// handle loyalty adjustment for dmg inflicted on a building
void HandleLoyaltyForDemolitionOfBuilding( SOLDIERTYPE *pSoldier, INT16 sPointsDmg )
{
	// find this soldier's team and decrement the loyalty rating for them and for the people who police the sector
	// more penalty for the people who did it, a lesser one for those who should have stopped it
	INT16 sLoyaltyValue = 0;
	INT16 sPolicingLoyalty = 0;
	INT8 bTownId = 0;


	// hurt loyalty for damaging the building
	sLoyaltyValue = sPointsDmg * MULTIPLIER_FOR_DAMAGING_A_BUILDING;

	// penalty for not preventing the action
	sPolicingLoyalty = sPointsDmg * MULTIPLIER_FOR_NOT_PREVENTING_BUILDING_DAMAGE;

	// get town id
	bTownId = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY );

	// Flugente: for safety reasons, exit if not in a town
	if( bTownId == BLANK_SECTOR )
		return;

	// penalize the side that did it
	if( pSoldier->bTeam == OUR_TEAM )
	{
		DecrementTownLoyalty( bTownId, sLoyaltyValue );
	}
	else if( pSoldier->bTeam == ENEMY_TEAM )
	{
		// enemy damaged sector, it's their fault
		IncrementTownLoyalty( bTownId, sLoyaltyValue );
	}
	else if ( pSoldier->ubCivilianGroup == REBEL_CIV_GROUP )
	{
		// the rebels did it...are they on our side
		if( CheckFact( FACT_REBELS_HATE_PLAYER, 0 ) == FALSE )
		{
			sLoyaltyValue /= DIVISOR_FOR_REBEL_BUILDING_DMG;

			// decrement loyalty value for rebels on our side dmging town
			DecrementTownLoyalty( bTownId, sLoyaltyValue );
		}
	}


	// penalize the side that should have stopped it
	if( StrategicMap[CALCULATE_STRATEGIC_INDEX( pSoldier->sSectorX, pSoldier->sSectorY )].fEnemyControlled == TRUE )
	{
		// enemy should have prevented it, let them suffer a little
		IncrementTownLoyalty( bTownId, sPolicingLoyalty );
	}
	else
	{
		// we should have prevented it, we shall suffer
		DecrementTownLoyalty( bTownId, sPolicingLoyalty );
	}

	return;
}


static BOOLEAN CanRemoveWorldItem( WORLDITEM *worlditem, UINT32 uiMaxWeight, UINT32 *pStackWeight )
{
	BOOLEAN result = FALSE;
	UINT32 uiChance = 10UL * Item[worlditem->object.usItem].ubCoolness;

	UINT32 uiStackWeight = 0;
	for ( int objIndex = 0; objIndex < worlditem->object.ubNumberOfObjects; objIndex++ )
		uiStackWeight += worlditem->object.GetWeightOfObjectInStack( objIndex );

	if ( Chance( uiChance ) && uiStackWeight <= uiMaxWeight )
		result = TRUE;

	*pStackWeight = uiStackWeight;
	return result;
}


void RemoveRandomItemsInSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ, UINT32 uiNumEnemies )
{
	const UINT32 uiCarryWeightPerSoldier = 30;  // 3 kg

	// remove random items in sector
	UINT32 uiNumberOfItems = 0, iCounter = 0, uiStackWeight = 0;
	UINT32 uiWeightToSteal = uiCarryWeightPerSoldier * uiNumEnemies;
	std::vector<WORLDITEM> pItemList;//dnl ch75 271013
	UINT32 uiNewTotal = 0;
	CHAR16 wSectorName[ 128 ];

	if(gGameExternalOptions.fNoRemoveRandomSectorItems)//dnl ch68 090913
		return;

	// stealing should fail anyway 'cause there shouldn't be a temp file for unvisited sectors, but let's check anyway
	Assert( GetSectorFlagStatus( sSectorX, sSectorY, ( UINT8 ) sSectorZ, SF_ALREADY_VISITED ) == TRUE );

	// get sector name string
	GetSectorIDString( sSectorX, sSectorY, ( INT8 ) sSectorZ, wSectorName, TRUE );

	// go through list of items in sector and randomly remove them

	// if unloaded sector
	if( gWorldSectorX != sSectorX || gWorldSectorY != sSectorY || gbWorldSectorZ != sSectorZ )
	{
		// if the player has never been there, there's no temp file, and 0 items will get returned, preventing any stealing
		const auto ii = FindWorldItemSector(sSectorX, sSectorY, (UINT8)sSectorZ);
		if (ii != -1)
		{
			uiNumberOfItems = gAllWorldItems.NumItems[ii];
			pItemList = gAllWorldItems.Items[ii];
		}

		if( uiNumberOfItems == 0 )
		{
			return;
		}


		uiNewTotal = uiNumberOfItems;
		// set up item list ptrs
		for( iCounter = 0; iCounter < uiNumberOfItems ; ++iCounter )
		{
			//if the item exists, and is visible and reachable, see if it should be stolen
			if ( pItemList[ iCounter ].fExists && pItemList[ iCounter ].bVisible == TRUE && pItemList[ iCounter ].usFlags & WORLD_ITEM_REACHABLE )
			{
				if ( CanRemoveWorldItem( &pItemList[iCounter], uiWeightToSteal, &uiStackWeight ) )
				{
					// remove
					--uiNewTotal;
					pItemList[ iCounter ].fExists = FALSE;
					uiWeightToSteal -= uiStackWeight;

					// debug message
					ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"%s stolen in %s!", ItemNames[ pItemList[ iCounter ].object.usItem ], wSectorName );
				}
			}
		}

		// only save if something was stolen
		if (uiNewTotal < uiNumberOfItems)
		{
			AddWorldItemsToUnLoadedSector( sSectorX, sSectorY, ( UINT8 )sSectorZ, 0, uiNumberOfItems, pItemList, TRUE );
		}
	}
	else	// handle a loaded sector
	{
		for( iCounter = 0; iCounter < guiNumWorldItems; ++iCounter )
		{
			// note, can't do reachable test here because we'd have to do a path call...
			if ( gWorldItems[ iCounter ].fExists && gWorldItems[ iCounter ].bVisible == TRUE )
			{
				if ( CanRemoveWorldItem( &gWorldItems[iCounter], uiWeightToSteal, &uiStackWeight ) )
				{
					RemoveItemFromPool( gWorldItems[ iCounter ].sGridNo , iCounter, gWorldItems[ iCounter ].ubLevel );
					uiWeightToSteal -= uiStackWeight;
					
					// debug message
					ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"%s stolen in %s!", ItemNames[ gWorldItems[ iCounter ].object.usItem ], wSectorName );
				}
			}
		}
	}
}

/* ARM: Civilian theft of items was removed
void HandleTheftByCiviliansInSector( INT16 sX, INT16 sY, INT32 iLoyalty )
{
	UINT8 ubChance = 0;

	// any loyalty under the theft threshhold is chance that thefts will occur
	if( iLoyalty < THRESHOLD_FOR_TOWN_THEFT )
	{
		// sectors with mercs are immune
		if( fSectorsWithSoldiers[ sX + ( MAP_WORLD_X * sY ) ][ 0 ] == FALSE )
		{
			// sectors not yet visited by player are also immune (other sectors in town could have been visited)
			if ( GetSectorFlagStatus( sX, sY, 0, SF_ALREADY_VISITED ) == TRUE )
			{
				ubChance = 5 + ( THRESHOLD_FOR_TOWN_THEFT - iLoyalty ) / 2;

				// remove items from sector
				RemoveRandomItemsInSector( sX, sY, 0, ubChance);
			}
		}
	}

	return;
}

void HandleTownTheft( void )
{
	INT32 iCounter = 0;
	UINT8 ubTown;

	// init sectors with soldiers list
	InitSectorsWithSoldiersList( );

	// build list
	BuildSectorsWithSoldiersList(	);

	while( pTownNamesList[ iCounter ] != 0 )
	{
		ubTown = StrategicMap[ pTownLocationsList[ iCounter ] ].bNameId;

		// if this town tracks loyalty, and tracking has started
		if ( ( ubTown != BLANK_SECTOR ) && gfTownUsesLoyalty[ ubTown ] && gTownLoyalty[ ubTown ].fStarted )
		{
			// handle theft in this town sector
			HandleTheftByCiviliansInSector( ( INT16 )( pTownLocationsList[ iCounter ] % MAP_WORLD_X ), ( INT16 )( pTownLocationsList[ iCounter ] / MAP_WORLD_X ), gTownLoyalty[ ubTown ].ubRating );
		}

		++iCounter;
	}
}
*/

void BuildListOfTownSectors( void )
{
	INT32 iCounterX = 0, iCounterY = 0, iCounter = 0;
	UINT16 usSector;

	// initialize
	memset( pTownNamesList, BLANK_SECTOR, sizeof( pTownNamesList ) );
	memset( pTownLocationsList, 0xFF, sizeof( pTownLocationsList ) );

	// run through list
	for( iCounterX = 0; iCounterX < MAP_WORLD_X; iCounterX++ )
	{
		for( iCounterY = 0; iCounterY < MAP_WORLD_Y; iCounterY++ )
		{
			usSector = CALCULATE_STRATEGIC_INDEX( iCounterX, iCounterY );

			if( ( StrategicMap[ usSector ].bNameId >= FIRST_TOWN ) && ( StrategicMap[ usSector ].bNameId < NUM_TOWNS ) )
			{
				pTownNamesList[ iCounter] = StrategicMap[ usSector ].bNameId;
				pTownLocationsList[ iCounter ] = usSector;

				iCounter++;
			}
		}
	}
}

//#ifdef JA2TESTVERSION
void CalcDistancesBetweenTowns( void )
{
	// run though each town sector and compare it to the next in terms of distance
	UINT8 ubTownA, ubTownB;
	UINT32 uiCounterA, uiCounterB;
	UINT8 ubTempGroupId = 0;
	INT32 iDistance = 0;


	// this is a little bit tricky, since towns can have multiple sectors, we have to measure all possible combinations

	// preset all distances between towns to high values to prepare for search for the minimum distance
	for( ubTownA = FIRST_TOWN; ubTownA < NUM_TOWNS; ubTownA++ )
	{
		for( ubTownB = FIRST_TOWN; ubTownB < NUM_TOWNS; ubTownB++ )
		{
			iTownDistances[ ubTownA ][ ubTownB ] = 999999;
		}
	}


	// create a temporary group (needed to plot strategic paths)
	ubTempGroupId = CreateNewPlayerGroupDepartingFromSector( 1, 1 );


	// now, measure distance from every town sector to every other town sector!
	// The minimum distances between towns get stored.
	uiCounterA = 0;
	uiCounterB = 0;

	while( pTownNamesList[ uiCounterA ] != 0 )
	{
		// reset second counter
		uiCounterB = uiCounterA;

		while( pTownNamesList[ uiCounterB ] != 0)
		{
			ubTownA = (UINT8) pTownNamesList[ uiCounterA ];
			ubTownB = (UINT8) pTownNamesList[ uiCounterB ];

			// if they're not in the same town
			if (ubTownA != ubTownB)
			{
				// calculate fastest distance between them (in sectors) - not necessarily shortest distance, roads are faster!
				iDistance = FindStratPath( ( INT16 )pTownLocationsList[uiCounterA ], ( INT16 )pTownLocationsList[ uiCounterB ], ubTempGroupId, FALSE );
			}
			else
			{
				// same town, distance is 0 by definition
				iDistance = 0;
			}

			// if it's the fastest route so far, store its length
			if( iDistance < iTownDistances[ ubTownA ][ ubTownB ] )
			{
				// store it going both ways!	The inner while loop is optimized to search each pair only once.
				iTownDistances[ ubTownA ][ ubTownB ] = iDistance;
				iTownDistances[ ubTownB ][ ubTownA ] = iDistance;
			}

			uiCounterB++;
		}

		uiCounterA++;
	}


	RemoveGroup( ubTempGroupId );
}


void WriteOutDistancesBetweenTowns( void )
{
	HWFILE hFileHandle;

	hFileHandle = FileOpen( "BinaryData\\TownDistances.dat", FILE_ACCESS_WRITE|FILE_OPEN_ALWAYS, FALSE );

	FileWrite( hFileHandle, &( iTownDistances ),	( sizeof( INT32 ) * MAX_TOWNS * MAX_TOWNS ), NULL );

	// close file
	FileClose( hFileHandle );

	return;
}


void DumpDistancesBetweenTowns(void)
{
	CHAR8 zPrintFileName[60];
	FILE *FDump;
	UINT8 ubTownA, ubTownB;
	CHAR16 wHeading[4];

	// open output file
 	strcpy(zPrintFileName, "TownDistances.txt");
	FDump = fopen(zPrintFileName, "wt");

	if (FDump == NULL)
	{
	return;
	}

	// print headings
	fprintf(FDump, "			");
	for( ubTownB = FIRST_TOWN; ubTownB < NUM_TOWNS; ubTownB++ )
	{
		wcsncpy(wHeading, pTownNames[ubTownB], 3);
		wHeading[3] = '\0';

	fprintf(FDump, " %ls", wHeading);
	}
	fprintf(FDump, "\n");

	fprintf(FDump, "			");
	for( ubTownB = FIRST_TOWN; ubTownB < NUM_TOWNS; ubTownB++ )
	{
	fprintf(FDump, " ---");
	}
	fprintf(FDump, "\n");

	for( ubTownA = FIRST_TOWN; ubTownA < NUM_TOWNS; ubTownA++ )
	{
		fprintf(FDump, "%12ls", pTownNames[ubTownA]);

		for( ubTownB = FIRST_TOWN; ubTownB < NUM_TOWNS; ubTownB++ )
		{
			fprintf(FDump, " %3d", iTownDistances[ ubTownA ][ ubTownB ]);
		}

		fprintf(FDump, "\n");
	}

	fclose(FDump);
}
//#endif

void ReadInDistancesBetweenTowns( void )
{
	HWFILE hFileHandle;

	hFileHandle = FileOpen( "BinaryData\\TownDistances.dat", FILE_ACCESS_READ, FALSE );

	FileRead( hFileHandle, &( iTownDistances ),	( sizeof( INT32 ) * MAX_TOWNS * MAX_TOWNS ), NULL );

	// close file
	FileClose( hFileHandle );

	return;
}


INT32 GetTownDistances( UINT8 ubTown, UINT8 ubTownA )
{
	return( iTownDistances[ ubTown ][ ubTownA ] );
}


/* Delayed loyalty effects elimininated.	Sep.12/98.	ARM
void HandleDelayedTownLoyaltyEvent( UINT32 uiValue )
{
	INT8 bTownId = 0;
	UINT32 uiLoyaltyChange = 0;
	BOOLEAN fIncrement = FALSE;

	// first 8 bits are town id, 23 are amount and last is increment or decrement

	// get increment
	fIncrement = ( BOOLEAN )( GET_TOWN_INCREMENT( iValue ) ) ;

	// town id
	bTownId = ( INT8 )( GET_TOWN_ID_FOR_LOYALTY( iValue ) );

	// the amount
	uiLoyaltyChange = ( INT32 )( GET_TOWN_LOYALTY_CHANGE( iValue ) );

	if (fIncrement)
	{
		IncrementTownLoyalty( bTownId, uiLoyaltyChange );
	}
	else
	{
		DecrementTownLoyalty( bTownId, uiLoyaltyChange );
	}
}


UINT32 BuildLoyaltyEventValue( INT8 bTownValue, UINT32 uiValue, BOOLEAN fIncrement )
{
	UINT32 uiReturnValue	= 0;
	UINT32 uiTempValue = 0;

	// the town name in 8 most sig bits
	uiTempValue = ( UINT32 )( bTownValue );

	uiReturnValue += ( uiTempValue << 24 );

	// the incrment decrement amount is
	uiTempValue = ( UINT32 )( uiValue );

	uiTempValue = ( ( uiTempValue << 8 ) >> 7 );
	uiReturnValue += uiTempValue;

	uiReturnValue += ( fIncrement != 0 ? 1: 0 );

	return( uiReturnValue );
}
*/


BOOLEAN SaveStrategicTownLoyaltyToSaveGameFile( HWFILE hFile )
{
	UINT32	uiNumBytesWritten;

	//Save the Town Loyalty
	FileWrite( hFile, gTownLoyalty, sizeof( TOWN_LOYALTY ) * MAX_TOWNS, &uiNumBytesWritten );
	if( uiNumBytesWritten != sizeof( TOWN_LOYALTY ) * MAX_TOWNS )
	{
		return( FALSE );
	}

	return( TRUE );
}

BOOLEAN LoadStrategicTownLoyaltyFromSavedGameFile( HWFILE hFile )
{
	UINT32	uiNumBytesRead;

	//Restore the Town Loyalty
	FileRead( hFile, gTownLoyalty, sizeof( TOWN_LOYALTY ) * MAX_TOWNS, &uiNumBytesRead );
	if( uiNumBytesRead != sizeof( TOWN_LOYALTY ) * MAX_TOWNS )
	{
		return( FALSE );
	}

	return( TRUE );
}



void ReduceLoyaltyForRebelsBetrayed(void)
{
	INT8 bTownId;

	// reduce loyalty to player all across Arulco
	for( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; bTownId++ )
	{
		if (bTownId == OMERTA)
		{
			// if not already really low
			if (gTownLoyalty[ bTownId ].ubRating > HOSTILE_OMERTA_LOYALTY_RATING)
			{
				// loyalty in Omerta tanks big time, and will stay low permanently since this becomes its maximum
				SetTownLoyalty(bTownId, HOSTILE_OMERTA_LOYALTY_RATING);
				// note that rebel sentiment isn't affected - they remain loyal to themselves, after all!
			}
		}
		else
		{

			// loyalty in other places is also strongly affected by this falling out with rebels, but this is not permanent
			SetTownLoyalty(bTownId, (UINT8) (gTownLoyalty[ bTownId ].ubRating / 3));
		}
	}
}

INT32 GetNumberOfWholeTownsUnderControl( void )
{
	INT32 iNumber = 0;
	INT8 bTownId = 0;

	// run through the list of towns..if the entire town is under player control, then increment the number of towns under player control

	// make sure that each town is one for which loyalty matters
	for ( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; bTownId++ )
	{
		if ( IsTownUnderCompleteControlByPlayer( bTownId ) && gfTownUsesLoyalty[ bTownId ] )
		{
			iNumber++;
		}
	}

	return( iNumber );
}


INT32 GetNumberOfWholeTownsUnderControlButExcludeCity( INT8 bCityToExclude )
{
	INT32 iNumber = 0;
	INT8 bTownId = 0;

	// run through the list of towns..if the entire town is under player control, then increment the number of towns under player control
	for( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; bTownId++ )
	{
		if ( IsTownUnderCompleteControlByPlayer( bTownId ) && ( bCityToExclude != bTownId ) && gfTownUsesLoyalty[ bTownId ] )
		{
			iNumber++;
		}
	}

	return( iNumber );
}

// is the ENTIRE town under player control?
INT32 IsTownUnderCompleteControlByPlayer( INT8 bTownId )
{
	if( GetTownSectorSize( bTownId ) == GetTownSectorsUnderControl( bTownId ) )
	{
		return( TRUE );
	}

	return( FALSE );
}

// is the ENTIRE town under enemy control?
INT32 IsTownUnderCompleteControlByEnemy( INT8 bTownId )
{
	if ( GetTownSectorsUnderControl( bTownId ) == 0 )
	{
		return( TRUE );
	}

	return( FALSE );
}

void AdjustLoyaltyForCivsEatenByMonsters( INT16 sSectorX, INT16 sSectorY, UINT8 ubHowMany, BOOLEAN aBandits)
{
	INT8 bTownId = 0;
	UINT32 uiLoyaltyChange = 0;
	CHAR16 str[256];
	CHAR16 pSectorString[128];
	
	// get town id
	bTownId = GetTownIdForSector( sSectorX, sSectorY );

	// if NOT in a town
	if( bTownId == BLANK_SECTOR )
	{
		return;
	}

	//Report this to player
	GetSectorIDString( sSectorX, sSectorY, 0, pSectorString, TRUE );

	if ( aBandits )
		swprintf( str, gpStrategicString[STR_PB_BANDIT_KILLCIVS_IN_SECTOR], ubHowMany, pSectorString );
	else
	{
#ifdef CHINESE
		//diffrent order of words in Chinese
		swprintf( str, gpStrategicString[STR_DIALOG_CREATURES_KILL_CIVILIANS], pSectorString, ubHowMany );
#else
		swprintf( str, gpStrategicString[STR_DIALOG_CREATURES_KILL_CIVILIANS], ubHowMany, pSectorString );
#endif
	}

	DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback );

	// use same formula as if it were a civilian "murder" in tactical!!!
	uiLoyaltyChange = ubHowMany * BASIC_COST_FOR_CIV_MURDER * MULTIPLIER_FOR_MURDER_BY_MONSTER;
	DecrementTownLoyalty( bTownId, uiLoyaltyChange );
}


// this applies the SAME change to every town equally, regardless of distance from the event
void IncrementTownLoyaltyEverywhere( UINT32 uiLoyaltyIncrease )
{
	INT8 bTownId;

	for( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; bTownId++ )
	{
		IncrementTownLoyalty( bTownId, uiLoyaltyIncrease );
	}
}

void DecrementTownLoyaltyEverywhere( UINT32 uiLoyaltyDecrease )
{
	INT8 bTownId;

	for( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; bTownId++ )
	{
		DecrementTownLoyalty( bTownId, uiLoyaltyDecrease );
	}
}
// this applies the change to every town differently, depending on the distance from the event
void HandleGlobalLoyaltyEvent( UINT8 ubEventType, INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ)
{	
	INT8 bTownId = 0;

	if( bSectorZ == 0 )
	{
		// grab town id, if this event occured within one
		bTownId = GetTownIdForSector( sSectorX, sSectorY );
	}

	// should other towns ignore events occuring in this town?
	if( bTownId == SAN_MONA )
	{
		return;
	}
			
	//move to lua scripts StrategicTownLoyalty.lua
#ifdef LUA_STRATEGY_TOWN_LOYALTY
	LuaHandleGlobalLoyaltyEvent( ubEventType, sSectorX, sSectorY, bSectorZ, 0 );
#else
	INT32 iLoyaltyChange;

	// determine what the base loyalty change of this event type is worth
	// these are->hundredths<- of loyalty points, so choose appropriate values accordingly!
	// the closer a town is to the event, the more pronounced the effect upon that town is
	switch (ubEventType)
	{
		case GLOBAL_LOYALTY_BATTLE_WON:
			CheckConditionsForTriggeringCreatureQuest( sSectorX, sSectorY, bSectorZ );
			iLoyaltyChange = 500;
			break;
		case GLOBAL_LOYALTY_QUEEN_BATTLE_WON:
			CheckConditionsForTriggeringCreatureQuest( sSectorX, sSectorY, bSectorZ );
			iLoyaltyChange = 1000;
			break;
		case GLOBAL_LOYALTY_BATTLE_LOST:
			iLoyaltyChange = -750;
			break;
		case GLOBAL_LOYALTY_ENEMY_KILLED:
			iLoyaltyChange = 50;
			break;
		case GLOBAL_LOYALTY_NATIVE_KILLED:
			// note that there is special code (much more severe) for murdering civilians in the currently loaded sector.
			// this event is intended more for processing militia casualties, and the like
			iLoyaltyChange = -50;
			break;
		case GLOBAL_LOYALTY_ABANDON_MILITIA:
			// it doesn't matter how many of them are being abandoned
			iLoyaltyChange = -750;
			break;
		case GLOBAL_LOYALTY_GAIN_TOWN_SECTOR:
			iLoyaltyChange = 500;
			break;
		case GLOBAL_LOYALTY_LOSE_TOWN_SECTOR:
			iLoyaltyChange = -1000;
			break;
		case GLOBAL_LOYALTY_LIBERATE_WHOLE_TOWN:
			iLoyaltyChange = 1000;
			break;
		case GLOBAL_LOYALTY_GAIN_MINE:
			iLoyaltyChange = 1000;
			break;
		case GLOBAL_LOYALTY_LOSE_MINE:
			iLoyaltyChange = -1500;
			break;
		case GLOBAL_LOYALTY_GAIN_SAM:
			iLoyaltyChange = 250;
			break;
		case GLOBAL_LOYALTY_LOSE_SAM:
			iLoyaltyChange = -250;
			break;
		// Flugente: morale drop for torturing prisoners
		case GLOBAL_LOYALTY_PRISONERS_TORTURED:
			iLoyaltyChange = -25;
			break;

		default:
			Assert(FALSE);
			return;
	}

	AffectAllTownsLoyaltyByDistanceFrom( iLoyaltyChange, sSectorX, sSectorY, bSectorZ);
	
#endif
}


void AffectAllTownsLoyaltyByDistanceFrom( INT32 iLoyaltyChange, INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ)
{
	INT16 sEventSector;
	UINT8 ubTempGroupId;
	INT8 bTownId;
	UINT32 uiIndex;
	INT32 iThisDistance;
	INT32 iShortestDistance[MAX_TOWNS];
	INT32 iPercentAdjustment;
	INT32 iDistanceAdjustedLoyalty;

	// preset shortest distances to high values prior to searching for a minimum
	for( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; ++bTownId )
	{
		iShortestDistance[ bTownId ] = 999999;
	}

	sEventSector = CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY );

	// need a temporary group create to use for laying down distance paths
	ubTempGroupId = CreateNewPlayerGroupDepartingFromSector( (UINT8) sSectorX, (UINT8) sSectorY );

	// calc distance to the event sector from EACH SECTOR of each town, keeping only the shortest one for every town
	uiIndex = 0;
	while( pTownNamesList[ uiIndex ] != 0 )
	{
		bTownId = (UINT8) pTownNamesList[ uiIndex ];

		// Flugente: added to speed up function, remove if removed in loop below this one
		// doesn't affect towns where player hasn't established a "presence" yet
		if ( !gTownLoyalty[bTownId].fStarted )
		{
			++uiIndex;
			continue;
		}

		// skip path test if distance is already known to be zero to speed this up a bit
		if (iShortestDistance[ bTownId ] > 0 )
		{
			// calculate across how many sectors the fastest travel path from event to this town sector
			iThisDistance = FindStratPath( sEventSector, ( INT16 )pTownLocationsList[ uiIndex ], ubTempGroupId, FALSE );

			if (iThisDistance < iShortestDistance[ bTownId ])
			{
				iShortestDistance[ bTownId ] = iThisDistance;
			}
		}

		++uiIndex;
	}

	// must always remove that temporary group!
	RemoveGroup( ubTempGroupId );
	
	for( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; ++bTownId )
	{
		// doesn't affect towns where player hasn't established a "presence" yet
		if (!gTownLoyalty[ bTownId ].fStarted)
		{
			continue;
		}

		// if event was underground, double effective distance
		if (bSectorZ != 0)
		{
			iShortestDistance[ bTownId ] *= 2;
		}

		// This number here controls how many sectors away is the "norm" for the unadjusted loyalty change value
		if (iShortestDistance[ bTownId ] < LOYALTY_EVENT_DISTANCE_THRESHOLD)
		{
			// add 25% per sector below the threshold
			iPercentAdjustment = 25 * (LOYALTY_EVENT_DISTANCE_THRESHOLD - iShortestDistance[ bTownId ]);
		}
		else if (iShortestDistance[ bTownId ] > LOYALTY_EVENT_DISTANCE_THRESHOLD)
		{
			// subtract 10% per sector above the threshold
			iPercentAdjustment = -10 * (iShortestDistance[ bTownId ] - LOYALTY_EVENT_DISTANCE_THRESHOLD);

			// don't allow it to go below -100, that would change the sign of the loyalty effect!
			if (iPercentAdjustment < -100)
			{
				iPercentAdjustment = -100;
			}
		}
		else
		{
			// no distance adjustment necessary
			iPercentAdjustment = 0;
		}
		
		// calculate loyalty affects as adjusted for distance to this town
		iDistanceAdjustedLoyalty = (iLoyaltyChange * (100 + iPercentAdjustment)) / 100;

		if (iDistanceAdjustedLoyalty == 0)
		{
			// no measurable effect, skip this town
			continue;
		}

		if ( iDistanceAdjustedLoyalty > 0)
		{
			IncrementTownLoyalty( bTownId, iDistanceAdjustedLoyalty );
		}
		else
		{
			// the decrement amount MUST be positive
			iDistanceAdjustedLoyalty *= -1;
			DecrementTownLoyalty( bTownId, iDistanceAdjustedLoyalty );
		}
	}
}

void AffectClosestTownLoyaltyByDistanceFrom( INT32 iLoyaltyChange, INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
{
	INT16 sEventSector;
	UINT8 ubTempGroupId;
	INT8 bTownId;
	UINT32 uiIndex;
	INT32 iThisDistance;
	INT32 iShortestDistance[MAX_TOWNS];
	INT32 iPercentAdjustment;
	INT32 iDistanceAdjustedLoyalty;

	INT32 sDistanceToClosestTown = 999999;
	INT8 bClosestTown = NUM_TOWNS;

	// preset shortest distances to high values prior to searching for a minimum
	for ( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; ++bTownId )
	{
		iShortestDistance[bTownId] = 999999;
	}

	sEventSector = CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY );

	// need a temporary group create to use for laying down distance paths
	ubTempGroupId = CreateNewPlayerGroupDepartingFromSector( (UINT8)sSectorX, (UINT8)sSectorY );

	// calc distance to the event sector from EACH SECTOR of each town, keeping only the shortest one for every town
	uiIndex = 0;
	while ( pTownNamesList[uiIndex] != 0 )
	{
		bTownId = (UINT8)pTownNamesList[uiIndex];

		// Flugente: added to speed up function, remove if removed in loop below this one
		// doesn't affect towns where player hasn't established a "presence" yet
		if ( !gTownLoyalty[bTownId].fStarted )
		{
			++uiIndex;
			continue;
		}

		// skip path test if distance is already known to be zero to speed this up a bit
		if ( iShortestDistance[bTownId] > 0 )
		{
			// calculate across how many sectors the fastest travel path from event to this town sector
			iThisDistance = FindStratPath( sEventSector, (INT16)pTownLocationsList[uiIndex], ubTempGroupId, FALSE );

			if ( iThisDistance < iShortestDistance[bTownId] )
			{
				iShortestDistance[bTownId] = iThisDistance;
			}

			if ( iThisDistance < sDistanceToClosestTown )
			{
				bClosestTown = bTownId;
				sDistanceToClosestTown = iThisDistance;
			}
		}

		++uiIndex;
	}

	// must always remove that temporary group!
	RemoveGroup( ubTempGroupId );

	if ( bClosestTown < NUM_TOWNS )
	{
		// if event was underground, double effective distance
		if ( bSectorZ != 0 )
		{
			sDistanceToClosestTown *= 2;
		}

		// This number here controls how many sectors away is the "norm" for the unadjusted loyalty change value
		if ( sDistanceToClosestTown < LOYALTY_EVENT_DISTANCE_THRESHOLD )
		{
			// add 25% per sector below the threshold
			iPercentAdjustment = 25 * ( LOYALTY_EVENT_DISTANCE_THRESHOLD - sDistanceToClosestTown );
		}
		else if ( sDistanceToClosestTown > LOYALTY_EVENT_DISTANCE_THRESHOLD )
		{
			// subtract 10% per sector above the threshold
			iPercentAdjustment = -10 * ( sDistanceToClosestTown - LOYALTY_EVENT_DISTANCE_THRESHOLD );

			// don't allow it to go below -100, that would change the sign of the loyalty effect!
			if ( iPercentAdjustment < -100 )
			{
				iPercentAdjustment = -100;
			}
		}
		else
		{
			// no distance adjustment necessary
			iPercentAdjustment = 0;
		}

		// calculate loyalty affects as adjusted for distance to this town
		iDistanceAdjustedLoyalty = ( iLoyaltyChange * ( 100 + iPercentAdjustment ) ) / 100;

		if ( iDistanceAdjustedLoyalty == 0 )
		{
			// no measurable effect, skip this town
			return;
		}

		if ( iDistanceAdjustedLoyalty > 0 )
		{
			IncrementTownLoyalty( bClosestTown, iDistanceAdjustedLoyalty );
		}
		else
		{
			// the decrement amount MUST be positive
			iDistanceAdjustedLoyalty *= -1;
			DecrementTownLoyalty( bClosestTown, iDistanceAdjustedLoyalty );
		}
	}	
}

// to be called whenever player gains control of a sector in any way
void CheckIfEntireTownHasBeenLiberated( INT8 bTownId, INT16 sSectorX, INT16 sSectorY )
{
	// the whole town is under our control, check if we never libed this town before
	if ( !gTownLoyalty[ bTownId ].fLiberatedAlready && IsTownUnderCompleteControlByPlayer ( bTownId ) )
	{
		if ( MilitiaTrainingAllowedInSector( sSectorX, sSectorY, 0 ) )
		{
			// give a loyalty bonus
			HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_LIBERATE_WHOLE_TOWN, sSectorX, sSectorY, 0 );

			// set fact is has been lib'ed and set history event
			AddHistoryToPlayersLog( HISTORY_LIBERATED_TOWN, bTownId, GetWorldTotalMin(), sSectorX, sSectorY );

#ifdef JA2UB
//Ja25: No meanhwiles
#else
			HandleMeanWhileEventPostingForTownLiberation( bTownId );
#endif

			// Flugente: campaign stats: if we captured an entire city for the first time, take note of that. We don't have to note the city here, as that is obvious from the sector anyway
			gCurrentIncident.usOneTimeEventFlags |= INCIDENT_ONETIMEEVENT_CITY_LIBERATED;
		}

		// even taking over non-trainable "towns" like Orta/Tixa for the first time should count as "player activity"
		
		if ( gGameOptions.ubDifficultyLevel == DIF_LEVEL_HARD || gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE)
		//if ( gGameOptions.ubDifficultyLevel >= DIF_LEVEL_HARD )
		{
			UpdateLastDayOfPlayerActivity( ( UINT16 ) ( GetWorldDay() + 4 ) );
		}
		else
		{
			UpdateLastDayOfPlayerActivity( ( UINT16 ) ( GetWorldDay() + 2 ) );
		}
		
		// set flag even for towns where you can't train militia, useful for knowing Orta/Tixa were previously controlled
		gTownLoyalty[ bTownId ].fLiberatedAlready = TRUE;
	}
}

void CheckIfEntireTownHasBeenLost( INT8 bTownId, INT16 sSectorX, INT16 sSectorY )
{
	// NOTE:	only towns which allow you to train militia are important enough to get
	// reported here (and they're the only ones you can protect)
	if ( MilitiaTrainingAllowedInSector( sSectorX, sSectorY, 0 ) && IsTownUnderCompleteControlByEnemy(bTownId) )
	{

#ifdef JA2UB
//Ja25 No meanwhile
#else
		// the whole town is under enemy control, check if we libed this town before
		if ( gTownLoyalty[ bTownId ].fLiberatedAlready )
		{
			HandleMeanWhileEventPostingForTownLoss( bTownId );
		}
#endif
	}
}




void HandleLoyaltyChangeForNPCAction( UINT8 ubNPCProfileId )
{
	//move to lua scripts StrategicTownLoyalty.lua

#ifdef LUA_STRATEGY_TOWN_LOYALTY
		LetHandleLoyaltyChangeForNPCAction( ubNPCProfileId, 0 );
#else

	switch ( ubNPCProfileId )
	{
		case MIGUEL:
			// Omerta loyalty increases when Miguel receives letter from Enrico
			IncrementTownLoyalty( OMERTA, LOYALTY_BONUS_MIGUEL_READS_LETTER );
			break;

		case DOREEN:
			// having freed the child labourers... she is releasing them herself!
			IncrementTownLoyalty( DRASSEN, LOYALTY_BONUS_CHILDREN_FREED_DOREEN_SPARED );
			break;

		case MARTHA:
			// if Joey is still alive
			if ( gMercProfiles[ JOEY ].bMercStatus != MERC_IS_DEAD )
			{
				IncrementTownLoyalty( CAMBRIA, LOYALTY_BONUS_MARTHA_WHEN_JOEY_RESCUED );
			}
			break;

		case KEITH:
			// Hillbilly problem solved
			IncrementTownLoyalty( CAMBRIA, LOYALTY_BONUS_KEITH_WHEN_HILLBILLY_SOLVED );
			break;

		case YANNI:
			// Chalice of Chance returned to Chitzena
			IncrementTownLoyalty( CHITZENA, LOYALTY_BONUS_YANNI_WHEN_CHALICE_RETURNED_LOCAL );
			// NOTE: This affects Chitzena,too, a second time, so first value is discounted for it
			IncrementTownLoyaltyEverywhere( LOYALTY_BONUS_YANNI_WHEN_CHALICE_RETURNED_GLOBAL );
			break;
#ifdef JA2UB
// ja25 UB
#else      
		case AUNTIE:
			// Bloodcats killed
			IncrementTownLoyalty( ALMA, LOYALTY_BONUS_AUNTIE_WHEN_BLOODCATS_KILLED );
			break;
#endif
		case MATT:
			// Brother Dynamo freed
			IncrementTownLoyalty( ALMA, LOYALTY_BONUS_MATT_WHEN_DYNAMO_FREED );
			break;

	}
#endif
}



// set the location of the first encounter with enemy
void SetTheFirstBattleSector( INT16 sSectorValue )
{
	if( sWorldSectorLocationOfFirstBattle == 0 )
	{
		sWorldSectorLocationOfFirstBattle = sSectorValue;
	}

	return;
}

// did first battle take place here
BOOLEAN DidFirstBattleTakePlaceInThisTown( INT8 bTownId )
{
	INT8 bTownBattleId = 0;

	// get town id for sector
	bTownBattleId = GetTownIdForSector( ( INT16 ) ( sWorldSectorLocationOfFirstBattle % MAP_WORLD_X ), ( INT16 ) ( sWorldSectorLocationOfFirstBattle / MAP_WORLD_X ) );

	return( bTownId == bTownBattleId );
}

UINT32 PlayerStrength( void )
{
	UINT8						ubLoop;
	SOLDIERTYPE *		pSoldier;
	UINT32					uiStrength, uiTotal = 0;

	for ( ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ubLoop++ )
	{
		pSoldier = MercPtrs[ ubLoop ];
		if ( pSoldier->bActive )
		{
			if ( pSoldier->bInSector || (pSoldier->flags.fBetweenSectors && SECTORX( pSoldier->ubPrevSectorID ) == gWorldSectorX && SECTORY( pSoldier->ubPrevSectorID ) == gWorldSectorY && (pSoldier->bSectorZ == gbWorldSectorZ)) )
			{
				// count this person's strength (condition), calculated as life reduced up to half according to maxbreath
				uiStrength = pSoldier->stats.bLife * ( pSoldier->bBreathMax + 100 ) / 200;
				uiTotal += uiStrength;
			}
		}
	}
	return( uiTotal );
}

UINT32 EnemyStrength( void )
{
	UINT8						ubLoop;
	SOLDIERTYPE *		pSoldier;
	UINT32					uiStrength, uiTotal = 0;

		for ( ubLoop = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; ubLoop <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; ubLoop++ )
		{
			pSoldier = MercPtrs[ ubLoop ];
			if ( pSoldier->bActive && pSoldier->bInSector && !pSoldier->aiData.bNeutral )
			{
				// count this person's strength (condition), calculated as life reduced up to half according to maxbreath
				uiStrength = pSoldier->stats.bLife * ( pSoldier->bBreathMax + 100 ) / 200;
				uiTotal += uiStrength;
			}
		}

	return( uiTotal );
}

//Function assumes that mercs have retreated already.	Handles two cases, one for general merc retreat
//which slightly demoralizes the mercs, the other handles abandonment of militia forces which poses
//as a serious loyalty penalty.
void HandleLoyaltyImplicationsOfMercRetreat( INT8 bRetreatCode, INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ )
{
	if ( NumNonPlayerTeamMembersInSector( sSectorX, sSectorY, MILITIA_TEAM ) )
	{ 
		//Big morale penalty!
		HandleGlobalLoyaltyEvent( GLOBAL_LOYALTY_ABANDON_MILITIA, sSectorX, sSectorY, (INT8)sSectorZ );
	}

	//Standard retreat penalty
	if ( bRetreatCode == RETREAT_TACTICAL_TRAVERSAL )
	{
		// if not worse than 2:1 odds, then penalize morale
		// SANDRO - Set the odds based on difficulty level
		
		UINT8 DiffLevel = gGameOptions.ubDifficultyLevel;
		if ( DiffLevel > DIF_LEVEL_INSANE )
			DiffLevel = 1;
		
		// sevenfm: if enemy was alerted
		if (gTacticalStatus.fEnemyInSector &&
			gTacticalStatus.Team[ENEMY_TEAM].bAwareOfOpposition &&
			((PlayerStrength() * (2 + DiffLevel)) >= EnemyStrength()))
		{
			HandleMoraleEvent(NULL, MORALE_RAN_AWAY, sSectorX, sSectorY, (INT8)sSectorZ);
		}
	}
	else
	{
		HandleMoraleEvent( NULL, MORALE_RAN_AWAY, sSectorX, sSectorY, (INT8)sSectorZ );
	}

	// Flugente: dynamic opinion: mercs retreated. Someone has to be blamed for this shameful defeat!
	// only do this if the enemy actually noticed us - no need to punish the player for a successful stealth operation
	if (gGameExternalOptions.fDynamicOpinions && gTacticalStatus.Team[ENEMY_TEAM].bAwareOfOpposition )
	{
		HandleDynamicOpinionRetreat();
	}
}


void MaximizeLoyaltyForDeidrannaKilled(void)
{
	INT8 bTownId;

	// max out loyalty to player all across Arulco
	for( bTownId = FIRST_TOWN; bTownId < NUM_TOWNS; bTownId++ )
	{
		// it's possible one of the towns still has creature problems, but it's too much of a pain to worry about it now
		SetTownLoyalty(bTownId, 100);
	}
}

// HEADROCK HAM 3.6: A loyalty hit based on outstanding debt for facility use.
void HandleFacilityDebtLoyaltyHit( void )
{
	// Loyalty hit is 1% per $1000 dollars owed.
	INT32 iLoyaltyHit = giTotalOwedForFacilityOperationsToday;

	if (giTotalOwedForFacilityOperationsToday > 500)
	{
		// But no less than 1%, if $500 are owed.
		iLoyaltyHit = __max(500,iLoyaltyHit);
	}

	if (iLoyaltyHit > 1)
	{
		DecrementTownLoyaltyEverywhere( iLoyaltyHit );
	}
}



