#include "builddefines.h" #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 "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 "Game Clock.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" #include "Facilities.h" #include "CampaignStats.h" // added by Flugente #include "DynamicDialogue.h" // added by Flugente #include "Rebel Command.h" #include "Luaglobal.h" #include "LuaInitNPCs.h" #include "Interface.h" #include #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_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; 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 = pSoldier->ubAttackerID->bTeam; // if the player did the killing if( bKillerTeam == OUR_TEAM ) { SOLDIERTYPE *pKiller = 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( SoldierID iCounter = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; iCounter <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; ++iCounter ) { // set current civ soldier pCivSoldier = iCounter; if ( pCivSoldier == pSoldier ) { continue; } // killer seen by civ? if ( SoldierToSoldierLineOfSightTest( pCivSoldier, 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( ( pSoldier->ubAttackerID->ubBodyType >= ADULTFEMALEMONSTER ) && ( 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 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 { if( g_lang == i18n::Lang::zh ) { //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 ); } } 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 ) { SOLDIERTYPE *pSoldier; UINT32 uiStrength, uiTotal = 0; for ( SoldierID ubLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; ubLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ++ubLoop ) { pSoldier = 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 ) { SOLDIERTYPE * pSoldier; UINT32 uiStrength, uiTotal = 0; for ( SoldierID ubLoop = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; ubLoop <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; ++ubLoop ) { pSoldier = 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 ); } }