#ifdef PRECOMPILEDHEADERS #include "Strategic All.h" #else #include "Town Militia.h" #include "Militia Control.h" #include "Campaign Types.h" #include "strategic.h" #include "strategicmap.h" #include "Overhead.h" #include "Strategic Town Loyalty.h" #include "Utilities.h" #include "random.h" #include "text.h" #include "Map Screen Interface.h" #include "Interface.h" #include "Laptopsave.h" #include "finances.h" #include "Game Clock.h" #include "Assignments.h" #include "squads.h" #include "Soldier Create.h" #include "Dialogue Control.h" #include "GameSettings.h" #include "Queen Command.h" #include "math.h" #include "Auto Resolve.h" #include "Vehicles.h" #include "Tactical Save.h" #endif #include "connect.h" #include "MilitiaSquads.h" #include "Reinforcement.h" // Debug defines //#define DEBUG_SHOW_RATINGS #define DEBUG_RATINGS_CONDITION !fForBattle // will create a squad each n hours (12 for example). Should divide 24 without remainder //#define CREATE_EACH_N_HOURS 12 // more these values - less the chances #define CHANCE_TO_MOVE_A_SQUAD 5 #define CHANCE_TO_MOVE_AN_UNFULL_SQUAD 40 //#define DIV_OF_ORIGINAL_MILITIA 2 #define BASE_DIR_PRIORITY 5 // little chance to move if below minimum //#define MINIMUM_MILITIA_SQUAD_SIZE 10 // should be 2* Minimum //#define MAXIMUM_MILITIA_SQUAD_SIZE 30 //#define MAXIMUM_MILITIA_SQUAD_SIZE_BATTLE MAXIMUM_MILITIA_SQUAD_SIZE #define DIR_WITH_UNFULL_SQUAD_RATING_BONUS 100 // rating of battle, when there's equal number of soldiers //#define DIR_RATING_OF_BATTLE 10 #define DIR_MIN_DIF 0.75 #define DIR_NORTH 0 #define DIR_EAST 1 #define DIR_SOUTH 2 #define DIR_WEST 3 INT32 iRestrictedSectorArraySize; UINT32 gRestrictMilitia[256]; // HEADROCK HAM 4: Yet ANOTHER array, this one holds player-set restrictions. UINT8 gubManualRestrictMilitia[ 256 ]; // HEADROCK HAM B1: Alternate array keeps track of dynamically unrestricted sectors BOOLEAN gDynamicRestrictMilitia[ 256 ]; // HEADROCK HAM B1: Function that dynamically unrestricts sectors as we take over towns. extern void AdjustRoamingRestrictions(); DYNAMICRESTRICTIONS gDynamicRestrictions[5001]; UINT8 gpAttackDirs[5][4]; // 0. Green Militia 1. Regular Militia 2. Elite Militia 3. Insertion code UINT8 guiDirNumber = 0; BOOLEAN gfMSBattle = FALSE; INT16 sMSMapX, sMSMapY; extern UINT8 GetTraversability( INT16 sStartSector, INT16 sEndSector ); extern void TriggerPrebattleInterface( UINT8 ubResult ); extern BOOLEAN gfEnterAutoResolveMode; //Counts the number of live mercs in any given sector. BOOLEAN PlayerMercsInSector_MSE( UINT8 ubSectorX, UINT8 ubSectorY, BOOLEAN fDontConsiderNextSector ) { GROUP *pGroup; PLAYERGROUP *pPlayer; UINT8 ubNumMercs = 0; pGroup = gpGroupList; while( pGroup ) { if( pGroup->fPlayer )// && !pGroup->fBetweenSectors) { if ( ( pGroup->ubSectorX == ubSectorX && pGroup->ubSectorY == ubSectorY && pGroup->ubSectorZ == 0 ) || ( !fDontConsiderNextSector && ( pGroup->ubNextX == ubSectorX && pGroup->ubNextY == ubSectorY && pGroup->ubSectorZ == 0 ) ) ) { //we have a group, make sure that it isn't a group containing only dead members. pPlayer = pGroup->pPlayerList; while( pPlayer ) { // robots count as mercs here, because they can fight, but vehicles don't if( ( pPlayer->pSoldier->stats.bLife ) && !( pPlayer->pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) ) { ubNumMercs++; } pPlayer = pPlayer->next; } } } pGroup = pGroup->next; } return ubNumMercs; } // get any enemy group id in the sector (if no group, will return 0 ) UINT8 GetEnemyGroupIdInSector( INT16 sMapX, INT16 sMapY ) { UINT8 ubRes = 0; GROUP *curr; curr = gpGroupList; while( curr ) { if( curr->ubSectorX == sMapX && curr->ubSectorY == sMapY && !curr->fPlayer ) if( !curr->ubGroupID ) return curr->ubGroupID; else ubRes = curr->ubGroupID; curr = curr->next; } return ubRes; } // Count all militia in the sector UINT8 CountMilitia(SECTORINFO *pSectorInfo) { return pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]+ pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]+ pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]; } // Creates militia at destination sector. The type and amount of militia depends on current sector's miltia type and amount // HEADROCK HAM 3.4: Added Leadership argument. void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, UINT8 ubBestLeadership ) { ////////////////////////////////////////////////////////////////////////////////////// // HEADROCK HAM 3.3: Introduced considerable changes here on top of HAM 2 code. You can replace // the entire function with this new code. // Mostly, the changes are geared towards variable militia QUANTITY due to variable trainer Leadership. // Also introduced minimum required leadership for training mobile militia, and minimum required // leadership for training a full squad. ////////////////////////////////////////////////////////////////////////////////////// SECTORINFO *pSourceSector = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] ); SECTORINFO *pTargetSector = &( SectorInfo[ SECTOR( sTMapX, sTMapY ) ] ); //HEADROCK HAM B2.7: Track the number of militia at the source. UINT16 usTotalMilitiaAtSource = pSourceSector->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] + pSourceSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] + pSourceSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; UINT16 usTotalMilitiaAtTarget = pTargetSector->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] + pTargetSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] + pTargetSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; // Desired number of Greens, Regulars and Elites to create. UINT8 ubTargetGreen, ubTargetRegular, ubTargetElite; // Upgrade points for replacing militia with better ones. UINT8 ubUpgradePoints; // Calculate default number of militia to place in Target Sector. UINT8 ubMilitiaToTrain = gGameExternalOptions.guiNumMobileMilitiaTrained; // Percentage of troops that should be of any one type: UINT8 ubTargetElitePercent = 0; UINT8 ubTargetRegularPercent = 0; UINT8 ubTempLeadership = ubBestLeadership; UINT8 ubActualyAdded = 0; // Added by SANDRO // Does trainer have enough leadership to train a squad? if ( ubBestLeadership < gGameExternalOptions.ubMinimumLeadershipToTrainMobileMilitia ) { // This is punishment, basically. The merc still gets the benefit of improving his leadership through the training // session, but a lot of money has basically gone to waste. This is something to watch out for, shall we say? ubMilitiaToTrain = 0; } else { // HEADROCK HAM 3.6: New function to do Quantity Calculation based on leadership ubMilitiaToTrain = CalcNumMilitiaTrained(ubBestLeadership, TRUE); } // HEADROCK HAM 3.4: Composition of new Mobile Militia groups is now dictated by two INI settings controlling // the percentage of Elites and Regulars within the group. If the percentage for either is above 0, at least // one militia of that type will be created every time. Green militia are created based on whatever remains. // HEADROCK HAM 3.5: Base percentages off the trainer's Leadership... if ( gGameExternalOptions.fLeadershipAffectsMobileMilitiaQuality && (ubBestLeadership >= gGameExternalOptions.ubMinimumLeadershipToTrainMobileMilitia) ) { // Find out how far between the minimum and maximum our character is. INT8 bRange = gGameExternalOptions.ubReqLeadershipForFullMobileTraining - gGameExternalOptions.ubMinimumLeadershipToTrainMobileMilitia; ubBestLeadership = __min(ubBestLeadership, gGameExternalOptions.ubReqLeadershipForFullMobileTraining); ubBestLeadership -= gGameExternalOptions.ubMinimumLeadershipToTrainMobileMilitia; UINT8 ubBestLeadershipPercentage = 0; if (bRange > 0) ubBestLeadershipPercentage = (ubBestLeadership * 100) / bRange; else bRange = 0; // There are five "grades", which in practice actually give a smooth transition from All-Green to All-Elite. // Please note that if elite training is disabled, all elites will later be converted to Regulars. In that case, // a character needs to have at least 60% of the total leadership range to train a full squad of regulars. if (ubBestLeadershipPercentage >= 0 && ubBestLeadershipPercentage < 20) // First step, Greens { ubTargetElitePercent = 0; // No elites ubTargetRegularPercent = 0; // No regulars // All green. } else if (ubBestLeadershipPercentage >= 20 && ubBestLeadershipPercentage < 40) { ubTargetElitePercent = 0; // No elites ubTargetRegularPercent = __max(1,(((ubBestLeadershipPercentage-19) * 50) / 20)); // Up to 50% regulars // Green is 100%-ubTargetRegularPercent. So it's between 0/0/100 and 0/50/50 } else if (ubBestLeadershipPercentage >= 40 && ubBestLeadershipPercentage < 60) { ubTargetElitePercent = __max(1,(((ubBestLeadershipPercentage-39) * 33) / 20)); // Up to 33% elites ubTargetRegularPercent = __max(1,(100 - ubTargetElitePercent)/2); // Half of what's left is regulars // And half is green. This gives anywhere between 1/49/49 and 33/33/33. } else if (ubBestLeadershipPercentage >= 60 && ubBestLeadershipPercentage < 80) { UINT8 ubTargetGreenPercent = __max(1, (((20 - (ubBestLeadershipPercentage-60))*33) / 20)); ubTargetElitePercent = (100-ubTargetGreenPercent)/2; ubTargetRegularPercent = ubTargetElitePercent; // Green percent drops as your leadership goes up, while Elite and Regular are equal. // This gives anywhere between 33/33/33 and 49/49/1 } else if (ubBestLeadershipPercentage >= 80 && ubBestLeadershipPercentage < 100) { ubTargetElitePercent = 50 + (((ubBestLeadershipPercentage-80) * 50) / 20); ubTargetRegularPercent = 100 - ubTargetElitePercent; // Elites increase from 50% to 100% while regulars drop to 0%. No greens. } else // Best possible leadership = Full Elites { ubTargetElitePercent = 100; ubTargetRegularPercent = 0; // All elites. } } else // INI-defined percentages { ubTargetElitePercent = gGameExternalOptions.ubPercentRoamingMilitiaElites; ubTargetRegularPercent = gGameExternalOptions.ubPercentRoamingMilitiaRegulars; } ubTargetElite = (ubMilitiaToTrain * ubTargetElitePercent) / 100; if (ubTargetElitePercent) // If percentage is positive ubTargetElite = __max(1, ubTargetElite); // Create at least one. ubTargetRegular = (ubMilitiaToTrain * ubTargetRegularPercent) / 100; if (ubTargetRegularPercent) // If percentage is positive ubTargetRegular = __max(1, ubTargetRegular); // Create at least one. // Greens are created based on what's left of the MilitiaToTrain that has not already // been allocated to Regulars or Elites. ubTargetGreen = ubMilitiaToTrain - (ubTargetRegular + ubTargetElite); if (ubTargetRegularPercent+ubTargetElitePercent < 100) // If there's also a remainder, percentage-wise ubTargetGreen = __max(1, ubTargetGreen); // Create at least one. // Is Elite Militia allowed at all? if (!gGameExternalOptions.gfTrainVeteranMilitia || GetWorldDay( ) < gGameExternalOptions.guiTrainVeteranMilitiaDelay) { // Convert all Elites to Regulars ubTargetRegular += ubTargetElite; ubTargetElite = 0; } // Make sure we're not training more militia than we're allowed. This kicks in when the number of militia // to train is lower than 3, for any reason (particularly, not meeting the minimum required leadership). while (ubTargetRegular+ubTargetElite+ubTargetGreen > ubMilitiaToTrain) { // Reduce target elite count first. if (ubTargetElite > 0) { ubTargetElite--; continue; } // If no more elites but still over the allowed value, then reduce one regular. if (ubTargetRegular > 0) { ubTargetRegular--; continue; } // No elites/regulars - reduce target green! ubTargetGreen--; } while (ubMilitiaToTrain > 0) { //////////////////////////////// // Create Heterogenous Group // // As of HAM 3.4, homogenous groups are not treated any differently than these. //////////////////////////////// // Do we have room in the target sector? if ( usTotalMilitiaAtTarget < gGameExternalOptions.iMaxMilitiaPerSector ) { if (ubTargetElite > 0) { // Add elite. StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, 1); ubTargetElite--; ubMilitiaToTrain--; ubActualyAdded++; } else if (ubTargetRegular > 0) { // Add regular. StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, 1); ubTargetRegular--; ubMilitiaToTrain--; ubActualyAdded++; } else if (ubTargetGreen > 0) { // Add green. StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, 1); ubTargetGreen--; ubMilitiaToTrain--; ubActualyAdded++; } else { // No one to add? Break the cycle. ubMilitiaToTrain = 0; } } // Full militia group? See if you can upgrade some. else { // Are we dealing with a full-size militia group that ISN'T full-quality yet? if (pTargetSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] < usTotalMilitiaAtTarget && pTargetSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] < gGameExternalOptions.iMaxMilitiaPerSector) // failsafe { // Have we got any Elites to add? if (ubTargetElite > 0) { // Add elite. This will effectively replace one lower class militia StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, 1); ubTargetElite--; ubMilitiaToTrain--; ubActualyAdded++; } else if (ubTargetRegular > 0 && // Got a regular (!gGameExternalOptions.gfTrainVeteranMilitia || // Not allowed to train Elites GetWorldDay( ) < gGameExternalOptions.guiTrainVeteranMilitiaDelay )) // Or not YET allowed to train elites { // Add a regular. This will effectively replace one Green militia StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, 1); ubTargetElite--; ubMilitiaToTrain--; ubActualyAdded++; } // Else, we've got more men to train but no room for them. In this case, upgrade existing // militia to the next class, using our remainder men as "upgrade points". else { // Calculate upgrade points. 1 point per Green, 2 per Regular. ubUpgradePoints = ubTargetGreen + (2 * ubTargetRegular); // Is Elite training allowed? if ( gGameExternalOptions.gfTrainVeteranMilitia && // Elite training allowed (GetWorldDay( ) >= gGameExternalOptions.guiTrainVeteranMilitiaDelay) // Elite training allowed (time based) && ubUpgradePoints >= 2) // Enough upgrade points to create an elite { // Add one elite StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, 1); if (ubTargetRegular > 0) { // Comes at a cost of one regular that we were supposed to train. ubTargetRegular--; ubMilitiaToTrain--; ubActualyAdded++; } else { // No regulars to train? Remove two greens instead. ubTargetGreen -= 2; ubMilitiaToTrain -= 2; ubActualyAdded += 2; } } // Else elite training was not allowed or we simply did not have enough points to add an elite else if (ubUpgradePoints >= 1) { // Add one regular instead of a green. StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, 1); ubTargetGreen--; ubMilitiaToTrain--; ubActualyAdded++; } else { // No more men to train. Break the cycle. ubMilitiaToTrain = 0; } } } else { // Sector is full-sized and full-quality. Break the cycle. ubMilitiaToTrain = 0; } } } // SANDRO - merc records (num militia trained) if( ubActualyAdded > 0 ) RecordNumMilitiaTrainedForMercs( sMapX, sMapY, 0, ubActualyAdded, TRUE ); // This reduces the group back to "maximum" size. It starts by eliminating extra greens, then regulars, then elites. // That produces a group of max size, with only the best troops remaining. while( CountMilitia(pTargetSector) > gGameExternalOptions.iMaxMilitiaPerSector ) { if(pTargetSector->ubNumberOfCivsAtLevel[GREEN_MILITIA]) { --pTargetSector->ubNumberOfCivsAtLevel[GREEN_MILITIA]; } else if(pTargetSector->ubNumberOfCivsAtLevel[REGULAR_MILITIA]) { --pTargetSector->ubNumberOfCivsAtLevel[REGULAR_MILITIA]; } else if(pTargetSector->ubNumberOfCivsAtLevel[ELITE_MILITIA]) { --pTargetSector->ubNumberOfCivsAtLevel[ELITE_MILITIA]; } } // Update the militia if the current sector is affected if (gfStrategicMilitiaChangesMade) { ResetMilitia(); } } // Creates militia at destination sector and removes it from starting sector void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOLEAN fAlternativeMax ) { SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] ); SECTORINFO *pTSectorInfo = &( SectorInfo[ SECTOR( sTMapX, sTMapY ) ] ); UINT8 bGreensSourceTeam = 0, bGreensDestTeam = 0; UINT8 bRegularsSourceTeam = 0, bRegularsDestTeam = 0; UINT8 bElitesSourceTeam = 0, bElitesDestTeam = 0; UINT8 bTotalGreens = 0, bTotalRegulars = 0, bTotalElites = 0; UINT8 bTotalGreensPercent = 0, bTotalRegularsPercent = 0, bTotalElitesPercent = 0; INT8 bNewSourceGroupSize = 0, bNewDestGroupSize = 0, bGroupSizeRatio = 0; UINT8 ubChanceToSpreadOut; ////////////////////////////////////////////////////////// // We begin by determining the group's chance to "spread out" into the next sector rather // than moving in en-masse. This is mainly affected by the presence of enemies. // Is the target a city or SAM site? if (IsThisSectorASAMSector( sTMapX, sTMapY, 0 ) || ( GetTownIdForSector( sTMapX, sTMapY ) != BLANK_SECTOR && gfMilitiaAllowedInTown[GetTownIdForSector( sTMapX, sTMapY )] )) { // Mobile->Town movement is always en-masse. We do not want to reduce the number // of Garrison troops in the town! They can only be increased. ubChanceToSpreadOut = 0; } else { // Wilderness->Wilderness movement. Calculate Spread Out chance based on threats. UINT8 ubNumEnemiesNearOrigin = NumEnemiesInFiveSectors( sMapX, sMapY ); UINT8 ubNumEnemiesNearTarget = NumEnemiesInFiveSectors( sTMapX, sTMapY ); if (ubNumEnemiesNearOrigin == 0 && ubNumEnemiesNearTarget > 0 ) { // Target sector is threatened, while source sector is not. // Target sector gets all the militia it can, in preperation for a possible enemy attack. ubChanceToSpreadOut = 0; } else if (ubNumEnemiesNearOrigin > 0 && ubNumEnemiesNearTarget == 0 ) { // Source sector is threatened, while destination is not. The group does not // move at all, as it is already in a good position to defend. return; } else if (ubNumEnemiesNearOrigin > 0 && ubNumEnemiesNearTarget > 0 ) { // Both sectors threatened. if (gGameExternalOptions.gfAllowReinforcements && !gGameExternalOptions.gfAllowReinforcementsOnlyInCity) { // Militia will spread out between the two sectors to maximize defensive capability, since they can // always reinforce one another if either sector is attacked. ubChanceToSpreadOut = 100; } else { // Militia chance to spread out is based on whether the Origin or the Destination are under greater // threat. If the destination is more threatened, militia are more likely to reinforce it // (move en-masse). If the origin is more threatened, militia are more likely to spread out evenly // between the two sectors, to give at least some chance of defeating ONE of the enemy groups. UINT16 usTotalNumEnemies = ubNumEnemiesNearTarget + ubNumEnemiesNearOrigin; ubChanceToSpreadOut = __min( 100, (ubNumEnemiesNearOrigin * 100) / usTotalNumEnemies ); } } else if (NumEnemiesInFiveSectors( sMapX, sMapY ) > 0 && NumEnemiesInFiveSectors( sTMapX, sTMapY ) > 0 ) { // Both sectors threatened. Militia will spread out between them, to maximize defensive capability. ubChanceToSpreadOut = 100; } else if (NumEnemiesInFiveSectors( sMapX, sMapY ) > 0 && NumEnemiesInFiveSectors( sTMapX, sTMapY ) == 0 ) { // Source sector is threatened, while destination is not. The group does not // move at all, as it is already in a good position to defend. return; } else { /////////////////////////////////////////////////////// // Both sectors not threatened. // Alter spreadout chance based on number of troops in Source and destination sectors. if ( CountMilitia(pSectorInfo) >= gGameExternalOptions.iMaxMilitiaPerSector && CountMilitia(pTSectorInfo) == 0 ) { // Source group is full, target sector is empty. The source group gets 30% chance to // spread out to that empty sector. ubChanceToSpreadOut = 30; } else if ( CountMilitia(pSectorInfo) && CountMilitia(pTSectorInfo) ) { // Source and destination groups both exist. 50% chance to spread out evenly between them. ubChanceToSpreadOut = 50; } else { // Target sector is empty, source group is not full. Do not spread out. ubChanceToSpreadOut = 0; } // HEADROCK HAM 4: Groups moving into a NO_LEAVE sector will always reinforce it. // this ignores any of the possible options above. if (gubManualRestrictMilitia[ SECTOR(sTMapX, sTMapY) ] == MANUAL_MOBILE_NO_LEAVE ) { ubChanceToSpreadOut = 0; } } } // Should we spread out into the target sector, or move en-masse? if (PreRandom(100) < ubChanceToSpreadOut) { if ( CountMilitia(pSectorInfo) && CountMilitia(pTSectorInfo) ) { // Source and Target sectors both have groups in them. Source group will attempt to spread out, // averaging the size and composition of both groups. bGreensSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; bGreensDestTeam = pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; bRegularsSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; bRegularsDestTeam = pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; bElitesSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; bElitesDestTeam = pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; bTotalGreens = bGreensSourceTeam + bGreensDestTeam; bTotalRegulars = bRegularsSourceTeam + bRegularsDestTeam; bTotalElites = bElitesSourceTeam + bElitesDestTeam; // Source team gets half of each group, rounded down bGreensSourceTeam = bTotalGreens / 2; bRegularsSourceTeam = bTotalRegulars / 2; bElitesSourceTeam = bTotalElites / 2; // Destination team gets half of each group, rounded up bGreensDestTeam = bTotalGreens - bGreensSourceTeam; bRegularsDestTeam = bTotalRegulars - bRegularsSourceTeam; bElitesDestTeam = bTotalElites - bElitesSourceTeam; // Erase ALL militia from both locations. StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, GREEN_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, REGULAR_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, ELITE_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); // Add new militia. StrategicAddMilitiaToSector( sMapX, sMapY, GREEN_MILITIA, bGreensSourceTeam ); StrategicAddMilitiaToSector( sMapX, sMapY, REGULAR_MILITIA, bRegularsSourceTeam ); StrategicAddMilitiaToSector( sMapX, sMapY, ELITE_MILITIA, bElitesSourceTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, bGreensDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, bRegularsDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, bElitesDestTeam ); } else { // Target sector is empty. Source group will split up into two equally-sized groups. bGreensSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; bGreensDestTeam = bGreensSourceTeam / 2; bRegularsSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; bRegularsDestTeam = bRegularsSourceTeam / 2; bElitesSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; bElitesDestTeam = bElitesSourceTeam / 2; // Add half team to target sector StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, bGreensDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, bRegularsDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, bElitesDestTeam ); // Remove half team from source sector StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, bGreensDestTeam ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, bRegularsDestTeam ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, bElitesDestTeam ); // Units that move into an empty square do not get to move again. AddToBlockMoveList( sTMapX, sTMapY ); } } else { // Source team moves En-Masse to target sector. bNewDestGroupSize = __min((CountMilitia(pTSectorInfo) + CountMilitia(pSectorInfo)), (UINT8)gGameExternalOptions.iMaxMilitiaPerSector); bNewSourceGroupSize = __max(0,(CountMilitia(pTSectorInfo) + CountMilitia(pSectorInfo)) - gGameExternalOptions.iMaxMilitiaPerSector); // If there are still going to be two teams after the transfer if ( bNewSourceGroupSize > 0 ) { // Make sure that the two teams end up with the same ratio of Greens/Regulars/Elites bGreensSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; bGreensDestTeam = pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; bRegularsSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; bRegularsDestTeam = pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; bElitesSourceTeam = pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; bElitesDestTeam = pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; bTotalGreens = bGreensSourceTeam + bGreensDestTeam; bTotalRegulars = bRegularsSourceTeam + bRegularsDestTeam; bTotalElites = bElitesSourceTeam + bElitesDestTeam; bGroupSizeRatio = __max(1,(bNewDestGroupSize / bNewSourceGroupSize)); // Destination team gets the majority of troops (based on the ratios), // but will always leave at least 1 troop of each type (if available) for the source team bGreensDestTeam = __min(__min(bTotalGreens, ((bTotalGreens * bGroupSizeRatio) / (bGroupSizeRatio + 1) +1) ), __max(0,bTotalGreens-1)); bRegularsDestTeam = __min(__min(bTotalRegulars, ((bTotalRegulars * bGroupSizeRatio) / (bGroupSizeRatio + 1) +1) ), __max(0,bTotalRegulars-1)); bElitesDestTeam = __min(__min(bTotalElites, ((bTotalElites * bGroupSizeRatio) / (bGroupSizeRatio + 1) +1) ), __max(0,bTotalElites-1)); // Source team gets the remainder (no less than one troop of each type, if available) bGreensSourceTeam = bTotalGreens - bGreensDestTeam; bRegularsSourceTeam = bTotalRegulars - bRegularsDestTeam; bElitesSourceTeam = bTotalElites - bElitesDestTeam; // Correct fractions to full squad size at the target. while (bGreensSourceTeam + bRegularsSourceTeam + bElitesSourceTeam > 3 && bGreensDestTeam + bRegularsDestTeam + bElitesDestTeam < bNewDestGroupSize) { if (bGreensSourceTeam > 0) { // Move a green to the dest team bGreensSourceTeam--; bGreensDestTeam++; continue; } if (bRegularsSourceTeam > 0) { // Move a regular to the dest team bRegularsSourceTeam--; bRegularsDestTeam++; continue; } if (bElitesSourceTeam > 0) { // Move an elite to the dest team bElitesSourceTeam--; bElitesDestTeam++; continue; } } // Erase ALL militia from both locations. StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, GREEN_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, REGULAR_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicRemoveMilitiaFromSector( sTMapX, sTMapY, ELITE_MILITIA, pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); // Add new militia. StrategicAddMilitiaToSector( sMapX, sMapY, GREEN_MILITIA, bGreensSourceTeam ); StrategicAddMilitiaToSector( sMapX, sMapY, REGULAR_MILITIA, bRegularsSourceTeam ); StrategicAddMilitiaToSector( sMapX, sMapY, ELITE_MILITIA, bElitesSourceTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, bGreensDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, bRegularsDestTeam ); StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, bElitesDestTeam ); } else { // Entire group moves from Source to Target, leaving no one behind. StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ); // Block target group from moving. It has just assimilated another group. AddToBlockMoveList( sTMapX, sTMapY ); } } while( !fAlternativeMax && CountMilitia(pTSectorInfo) > gGameExternalOptions.iMaxMilitiaPerSector ) { if(pTSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]) { --pTSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]; ++pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]; } else if(pTSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]) { --pTSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]; ++pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]; } else if(pTSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]) { --pTSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]; ++pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]; } } if (gfStrategicMilitiaChangesMade) { ResetMilitia(); } } BOOLEAN MoveOneBestMilitiaMan(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY) { SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] ); // SECTORINFO *pTSectorInfo = &( SectorInfo[ SECTOR( sTMapX, sTMapY ) ] ); if( pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ) { StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, 1 ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, 1 ); return TRUE; } if( pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ) { StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, 1 ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, 1 ); return TRUE; } if( pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ) { StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, 1 ); StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, 1 ); return TRUE; } return FALSE; } // Generate rating for a direction UINT16 CountDirectionEnemyRating( INT16 sMapX, INT16 sMapY, UINT8 uiDir ) { DOUBLE ddMinAngle, ddMaxAngle; DOUBLE ddRes = 0; DOUBLE pix2 = 3.1415926 * 2; // pi * 2 //UINT16 uiSector = 0; INT16 sLMX, sLMY; switch(uiDir) { case DIR_NORTH: ddMinAngle = -2.3561944; // -135 degrees ddMaxAngle = -0.7853981; // -45 degrees break; case DIR_EAST: ddMinAngle = -0.7853981; // -45 degrees ddMaxAngle = 0.7853981; // 45 degrees break; case DIR_SOUTH: ddMinAngle = 0.7853981; // 45 degrees ddMaxAngle = 2.3561944; // 135 degrees break; case DIR_WEST: ddMinAngle = 2.3561944; // 135 degrees ddMaxAngle = -2.3561944 + pix2; // 225 degrees(-135) break; default: return 0; } //for( uiSector = 0 ; uiSector < 256 ; ++uiSector ) for( sLMX = MINIMUM_VALID_X_COORDINATE; sLMX <= MAXIMUM_VALID_X_COORDINATE ; ++sLMX ) for( sLMY = MINIMUM_VALID_Y_COORDINATE; sLMY <= MAXIMUM_VALID_Y_COORDINATE ; ++sLMY ) { // SECTORINFO *pSectorInfo = &( SectorInfo[ uiSector ] ); UINT8 uiSumOfEnemyTroops = NumEnemiesInSector( sLMX, sLMY ); //pSectorInfo->ubNumAdmins + pSectorInfo->ubNumTroops + pSectorInfo->ubNumElites; // there's an enemy if( uiSumOfEnemyTroops ) { DOUBLE ddAngle; if( sLMX - sMapX == 0 )ddAngle = ( sLMY - sMapY > 0 ) ? 1.57079632 : -1.57079632; else ddAngle = atan2( (double)(sLMY - sMapY), (double)(sLMX - sMapX) ); if( ddAngle < 0 && uiDir == DIR_WEST )ddAngle += pix2; // is it in a right direction? if( ddAngle >= ddMinAngle && ddAngle <= ddMaxAngle ) { DOUBLE ddDistance = sqrt( pow( (double)(sLMY - sMapY), 2 ) + pow( (double)(sLMX - sMapX), 2 ) ); ddRes += (DOUBLE)uiSumOfEnemyTroops / pow( ddDistance, 2 ); } } } return (UINT16) ddRes; } UINT16 CountDirectionRating( INT16 sMapX, INT16 sMapY, UINT8 uiDir ) { INT32 iRes = BASE_DIR_PRIORITY + CountDirectionEnemyRating( sMapX, sMapY, uiDir ); INT16 sDMapX, sDMapY; FLOAT iDiff; switch(uiDir) { case DIR_NORTH: sDMapX = sMapX; sDMapY = sMapY - 1; break; case DIR_EAST: sDMapX = sMapX + 1; sDMapY = sMapY; break; case DIR_SOUTH: sDMapX = sMapX; sDMapY = sMapY + 1; break; case DIR_WEST: sDMapX = sMapX - 1; sDMapY = sMapY; break; default: return iRes; } UINT8 ubMaxSquadSize = gGameExternalOptions.iMaxMilitiaPerSector; UINT8 ubSourceGreens = SectorInfo[SECTOR(sMapX, sMapY)].ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; UINT8 ubSourceRegulars = SectorInfo[SECTOR(sMapX, sMapY)].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; UINT8 ubSourceElites = SectorInfo[SECTOR(sMapX, sMapY)].ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; UINT8 ubTargetGreens = SectorInfo[SECTOR(sDMapX, sDMapY)].ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; UINT8 ubTargetRegulars = SectorInfo[SECTOR(sDMapX, sDMapY)].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; UINT8 ubTargetElites = SectorInfo[SECTOR(sDMapX, sDMapY)].ubNumberOfCivsAtLevel[ ELITE_MILITIA ]; UINT8 ubSourceMilitia = ubSourceGreens + ubSourceRegulars + ubSourceElites; UINT8 ubTargetMilitia = ubTargetGreens + ubTargetRegulars + ubTargetElites; UINT16 usSourceEnemiesInFiveSectors = NumEnemiesInFiveSectors( sMapX, sMapY ); UINT16 usTargetEnemiesInFiveSectors = NumEnemiesInFiveSectors( sDMapX, sDMapY ); UINT8 ubTargetEnemies = NumEnemiesInSector( sDMapX, sDMapY ); UINT16 usSourceMilitiaInFiveSectors = CountAllMilitiaInFiveSectors( sMapX, sMapY ); UINT16 usTargetMilitiaInFiveSectors = CountAllMilitiaInFiveSectors( sDMapX, sDMapY ); UINT8 bSourceTownId = GetTownIdForSector( sMapX, sMapY ); UINT8 bTargetTownId = GetTownIdForSector( sDMapX, sDMapY ); BOOLEAN fTargetSAMSite = IsThisSectorASAMSector( sDMapX, sDMapY, 0 ); // Is target sector a city/SAM where militia can be recruited? if ( (bTargetTownId != BLANK_SECTOR && gfMilitiaAllowedInTown[bTargetTownId]) || fTargetSAMSite ) { // Does roaming group have anything to contribute to this town/SAM? if ( ubTargetElites >= ubMaxSquadSize || // Target sector full of elites (ubTargetRegulars+ubTargetElites >= ubMaxSquadSize && ubSourceElites == 0) || // Target sector full of Elite+Regular, and source has no elites (ubTargetMilitia >= ubMaxSquadSize && ubSourceRegulars+ubSourceElites == 0) ) // Target sector is full of anything, Souce sector has no Elites or Regulars { // Ignore sector. return 0; } } // Else, target sector is a free-roaming sector. else { // Are both squads full of militia? if (ubSourceMilitia >= ubMaxSquadSize && ubTargetMilitia >= ubMaxSquadSize) { // Are both squads the same composition? if ( ubSourceGreens == ubTargetGreens && ubSourceRegulars == ubTargetRegulars && ubSourceElites == ubTargetElites ) { // No reason to merge or average the squads, so ignore this sector. return 0; } } // Does source group have anything to offer to the target group? else if (ubTargetElites >= ubMaxSquadSize || // Target full of elites already ( ubTargetRegulars+ubTargetElites >= ubMaxSquadSize && ubSourceElites == 0 ) || // Target full of regulars and elites, source group has no elites ( ubTargetMilitia >= ubMaxSquadSize && ubSourceElites + ubSourceRegulars == 0 ) ) // Target full of militia, and source group only has greens { // Source group has nothing to offer the target group. return 0; } // If source sector is threatened by enemies, while target sector is not, do not move away! else if (usSourceEnemiesInFiveSectors && !usTargetEnemiesInFiveSectors) { return 0; } } // Target sector has room for more militia? if( ubTargetMilitia < ubMaxSquadSize ) // Bonus rating iRes += DIR_WITH_UNFULL_SQUAD_RATING_BONUS; // Modify desire for militia-initiated attack if( ubTargetEnemies ) { iDiff = (FLOAT)(usTargetMilitiaInFiveSectors / usTargetEnemiesInFiveSectors); if( iDiff > (FLOAT)DIR_MIN_DIF ) iRes = (INT32)((FLOAT)iRes * iDiff); else iRes = 0; } // HEADROCK HAM 4: Increase desire to move into a NO-LEAVE sector (Player-Set restrictions system) if (gubManualRestrictMilitia[ SECTOR(sDMapX, sDMapY) ] == MANUAL_MOBILE_NO_LEAVE ) { iRes = (INT32)((FLOAT)iRes * 1.5); } // Test for player mercs in target sector. if( PlayerMercsInSector_MSE( (UINT8)sDMapX, (UINT8)sDMapY, FALSE )) { // There are mercs at the target. Increase movement desire. if ( bTargetTownId == BLANK_SECTOR && !fTargetSAMSite) { iRes += 15000;// should be enough } // If the target is a city, only 50% chance to increase the desire. This avoids situations where // a Mobile group is stuck next to a merc-occupied city sector, unable to get in and unable to // move away... else if (((bTargetTownId != BLANK_SECTOR && gfMilitiaAllowedInTown[bTargetTownId]) || fTargetSAMSite) && Random(2)==0) { iRes += 15000; } } // ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"M %ld, E%ld,%ld %ld, Diff %ld", CountAllMilitiaInSector( sMapX, sMapY ), sDMapX, sDMapY, NumEnemiesInSector( sDMapX, sDMapY ), ((INT32)CountAllMilitiaInSector( sMapX, sMapY )) - ((INT32)NumEnemiesInSector( sDMapX, sDMapY )) ); return iRes; } // Standard conditions the direction should fit ///////////////////////////////////////////////// // HEADROCK HAM 3.4: Please note that the following function is used for ANY restrictions on movement. It tests // everything from "groundbarriers" (impassable sector edge), to Player-Set Mobile Militia Restrictions, to hardcoded // stuff like whether militia can move in or out of cities (or any other sector, for that matter). // // If you wish to change where militia can and can't move to, THIS IS IT. // All sub-functions called here are related to the same purpose. // BOOLEAN CheckStandardConditionsForDirection( INT16 sSMapX, INT16 sSMapY, INT16 sMapX, INT16 sMapY, BOOLEAN fForTraining, BOOLEAN fForBattle ) { SECTORINFO * pTargetSector = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] ); if (fForBattle) { // When checking for reinforcements for battle, everything is THE OTHER WAY AROUND. Don't ask me why. INT16 sTempMap; sTempMap = sMapX; sMapX = sSMapX; sSMapX = sTempMap; sTempMap = sMapY; sMapY = sSMapY; sSMapY = sTempMap; } // At the end of the function we will check the status of these flags to determine // whether movement is indeed allowed. BOOLEAN fTraverseAllowed = FALSE; BOOLEAN fReadyForBattle = FALSE; BOOLEAN fRoamingAllowed = FALSE; BOOLEAN fCanBecomeGarrison = FALSE; BOOLEAN fCanSimpleMove = FALSE; ////////////////////////////////////////////////////////////////////////////////////////// // The first check makes sure that travel between the sectors is allowed at all. Ground-Barriers // are special because they also prevent merc and enemy movement. UINT8 uiTravab = GetTraversability( SECTOR( sSMapX , sSMapY ) , SECTOR( sMapX, sMapY ) ); if( uiTravab == GROUNDBARRIER || uiTravab == EDGEOFWORLD ) { fTraverseAllowed = FALSE; } else { fTraverseAllowed = TRUE; } ////////////////////////////////////////////////////////////////////////////////////////// // If militia aren't set-up for battle, they can't move into an enemy sector. This prevents combat ever // being instigated by militia unless specifically requested to do so by setting all the appropriate "ready!" // flags earlier in the code. if( !fForBattle && gfMSBattle && NumEnemiesInSector( sMapX, sMapY ) ) { fReadyForBattle = FALSE; } else { fReadyForBattle = TRUE; } ///////////////////////////////////////////////////////////////////////////////////////////////////// // Check player-set restrictions. These are read from XML, and can be (but not always) influenced by // which cities we've already liberated, and which sectors we've already explored. This is handled // by a separate function. if( gGameExternalOptions.gflimitedRoaming && !IsSectorRoamingAllowed( SECTOR(sMapX, sMapY) ) ) { // HEADROCK HAM 3.6: Training mobiles will also not take restrictions into account. if ( (fForBattle && fReadyForBattle) || fForTraining ) { // Allow movement into restricted sectors ONLY if they are for battle! fRoamingAllowed = TRUE; } else { fRoamingAllowed = FALSE; } } else { fRoamingAllowed = TRUE; } ///////////////////////////////////////////////////////////////////////////////////////////////////// // Movement into and out of city/sam sectors is more complicated to check. // // Movement out of a town/sam sector is only allowed when reinforcing other town/sam sectors. This means militia // don't move around town unless called to do so. // Movement from the wilderness into a town/sam is different in HAM 3.3, where roaming militias can go into town // and turn into Garrisons (regular standing militia). They can also reinforce the city when it comes under attack, // but only if allowed to do so by the current GameSettings. // Please note that Minor Cities are considered halfway between the two. These are cities like Estoni or // San Mona, where Militia cannot be trained. Roaming Militia can now be allowed to travel through these as they // would through wilderness areas. It also includes larger facilities like Tixa and Orta. Militia will not stay // there as garrisons, but may simply move through. //////////////////////////////////////////////////////////////////////////////////////////////// // Begin by setting flags for the source sector BOOLEAN fSourceIsMajorTown = FALSE; BOOLEAN fSourceIsMinorTown = FALSE; BOOLEAN fSourceIsSamSite = FALSE; if (GetTownIdForSector( sSMapX, sSMapY) != BLANK_SECTOR) { // A "Major" city is one where militia can be trained. Drassen, Chitzena, Meduna, etcetera. if (gfMilitiaAllowedInTown[GetTownIdForSector( sSMapX, sSMapY )]) { fSourceIsMajorTown = TRUE; fSourceIsMinorTown = FALSE; } // A "Minor" city cannot train militia. This includes places like San Mona, Omerta, // and even Tixa and Estoni else { fSourceIsMajorTown = FALSE; fSourceIsMinorTown = TRUE; } } else { fSourceIsSamSite = IsThisSectorASAMSector( sSMapX, sSMapY, 0 ); } // And the target sector BOOLEAN fTargetIsMajorTown = FALSE; BOOLEAN fTargetIsMinorTown = FALSE; BOOLEAN fTargetIsSamSite = FALSE; if (GetTownIdForSector( sMapX, sMapY) != BLANK_SECTOR) { if (gfMilitiaAllowedInTown[GetTownIdForSector( sMapX, sMapY )]) { fTargetIsMajorTown = TRUE; fTargetIsMinorTown = FALSE; } else { fTargetIsMajorTown = FALSE; fTargetIsMinorTown = TRUE; } } else { fTargetIsSamSite = IsThisSectorASAMSector( sMapX, sMapY, 0 ); } ////////////////////////////////////////////////////////////////////////////////////////////////// // First we run the test for battle reinforcement conditions. if ( fForBattle && fReadyForBattle ) { // Is reinforcement allowed only in cities? if (gGameExternalOptions.gfAllowReinforcementsOnlyInCity) { // Make sure both origin and destination are cities. if ( fSourceIsMajorTown && fTargetIsMajorTown ) { fReadyForBattle = TRUE; } else { // Reinforcing from Wilderness is impossible. fReadyForBattle = FALSE; } } // Battle reinforcements allowed from both cities and wilderness sectors. else { // Trying to do battle reinforcement from a town/sam to the wilderness? if ( ( fSourceIsMajorTown || fSourceIsSamSite ) && !(fTargetIsMajorTown || fTargetIsSamSite ) ) { // No! Static troops cannot reinforce Mobile troops for any reason. fReadyForBattle = FALSE; } else { fReadyForBattle = TRUE; } } // RETURN. if ( fTraverseAllowed && fReadyForBattle && fRoamingAllowed ) { return TRUE; } else { return FALSE; } } // Else, if we are doing a "peaceful" movement. else if ( !fForBattle ) { // Are we trying to bolster a local garrison? if (fTargetIsSamSite || fTargetIsMajorTown) { // Flags to store INI option settings. BOOLEAN fAllowReinforceCities = ( gGameExternalOptions.fAllowMobileReinforceCities && gGameExternalOptions.gfmusttrainroaming && gGameExternalOptions.gfAllowMilitiaGroups ); BOOLEAN fAllowReinforceSAM = ( gGameExternalOptions.fAllowMobileReinforceSAM && gGameExternalOptions.gfmusttrainroaming && gGameExternalOptions.gfAllowMilitiaGroups ); // Town->Town, Town->SAM, SAM->Town and SAM->SAM are disallowed for peaceful movement. if ( fSourceIsMajorTown || fSourceIsSamSite ) { fCanBecomeGarrison = FALSE; } // Wilderness->Sam/Town. else { if (!NumEnemiesInSector( sMapX, sMapY ) && // Is the sector under our control at the moment? pTargetSector->fSurfaceWasEverPlayerControlled && // Has it ever been under control? CountMilitia(pTargetSector) < gGameExternalOptions.iMaxMilitiaPerSector ) // Is there room here for more militia? { if (fTargetIsSamSite && fAllowReinforceSAM) { fCanBecomeGarrison = TRUE; } else if (fTargetIsMajorTown && fAllowReinforceCities) { fCanBecomeGarrison = TRUE; } } else { fCanBecomeGarrison = FALSE; } } // RETURN. if ( fTraverseAllowed && fRoamingAllowed && fCanBecomeGarrison ) { return TRUE; } else { return FALSE; } } // Trying to move peacefully out of a Town/SAM? else if (fSourceIsMajorTown || fSourceIsSamSite) { // This should ONLY be possible if we are explicitly training new militia groups. // Also, militia can no longer spawn directly into enemy-controlled territory. if (fForTraining && !(NumEnemiesInSector( sMapX, sMapY ))) { fCanSimpleMove = TRUE; } else { // Otherwise always impossible. We don't want militia leaving the cities for any other reason! fCanSimpleMove = FALSE; } } // Test for Wilderness/Minor -> Minor else if (fTargetIsMinorTown) { // Is allowed by INI? if (gGameExternalOptions.fAllowMilitiaMoveThroughMinorCities && pTargetSector->fSurfaceWasEverPlayerControlled ) // Have we visited this sector before? { // Fine then. Works the same as wilderness->wilderness. fCanSimpleMove = TRUE; } else { // Movement through Minor Cities isn't allowed. fCanSimpleMove = FALSE; } } // Wilderness/Minor -> Wilderness else { // Always possible. fCanSimpleMove = TRUE; } // RETURN. if ( fTraverseAllowed && fRoamingAllowed && fCanSimpleMove ) { return TRUE; } else { return FALSE; } } return FALSE; } void GenerateDirectionInfos( INT16 sMapX, INT16 sMapY, UINT8* uiDirNumber, UINT16 pMoveDir[4][3], BOOLEAN fForTraining, BOOLEAN fForBattle ) { *uiDirNumber = 0; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%ld,%ld-------------------", sMapX, sMapY ); #endif if(sMapY > MINIMUM_VALID_Y_COORDINATE && CheckStandardConditionsForDirection( sMapX, sMapY, sMapX, sMapY - 1, fForTraining, fForBattle ) ) { pMoveDir[ *uiDirNumber ][0] = SECTOR( sMapX, sMapY - 1 ); if( fForBattle ) pMoveDir[ *uiDirNumber ][1] = 0; else pMoveDir[ *uiDirNumber ][1] = CountDirectionRating( sMapX, sMapY, DIR_NORTH ); pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_NORTH; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); #endif ++(*uiDirNumber); } if(sMapX > MINIMUM_VALID_X_COORDINATE && CheckStandardConditionsForDirection( sMapX, sMapY, sMapX - 1, sMapY, fForTraining, fForBattle )) { pMoveDir[ *uiDirNumber ][0] = SECTOR( sMapX - 1, sMapY ); if( fForBattle ) pMoveDir[ *uiDirNumber ][1] = 0; else pMoveDir[ *uiDirNumber ][1] = CountDirectionRating( sMapX, sMapY, DIR_WEST ); pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_WEST; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of west direction - %ld", pMoveDir[ *uiDirNumber ][1]); #endif ++(*uiDirNumber); } if(sMapY < MAXIMUM_VALID_Y_COORDINATE && CheckStandardConditionsForDirection( sMapX, sMapY, sMapX, sMapY + 1, fForTraining, fForBattle )) { pMoveDir[ *uiDirNumber ][0] = SECTOR( sMapX, sMapY + 1 ); if( fForBattle ) pMoveDir[ *uiDirNumber ][1] = 0; else pMoveDir[ *uiDirNumber ][1] = CountDirectionRating( sMapX, sMapY, DIR_SOUTH ); pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_SOUTH; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of south direction - %ld", pMoveDir[ *uiDirNumber ][1]); #endif ++(*uiDirNumber); } if(sMapX < MAXIMUM_VALID_X_COORDINATE && CheckStandardConditionsForDirection( sMapX, sMapY, sMapX + 1, sMapY, fForTraining, fForBattle )) { pMoveDir[ *uiDirNumber ][0] = SECTOR( sMapX + 1, sMapY ); if( fForBattle ) pMoveDir[ *uiDirNumber ][1] = 0; else pMoveDir[ *uiDirNumber ][1] = CountDirectionRating( sMapX, sMapY, DIR_EAST ); pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_EAST; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of east direction - %ld", pMoveDir[ *uiDirNumber ][1]); #endif ++(*uiDirNumber); } } UINT8 pBlockMoveList[256]; UINT8 uiBlockMoveListPointer; void AddToBlockMoveList(INT16 sMapX, INT16 sMapY) { pBlockMoveList[ uiBlockMoveListPointer++ ] = SECTOR( sMapX, sMapY ); } void ClearBlockMoveList() { uiBlockMoveListPointer = 0; } BOOLEAN CheckInBlockMoveList(INT16 sMapX, INT16 sMapY) { UINT16 i; for( i = 0; i < uiBlockMoveListPointer ; ++i) if( sMapX == SECTORX(pBlockMoveList[i]) && sMapY == SECTORY(pBlockMoveList[i]) )return TRUE; return FALSE; } void UpdateMilitiaSquads(INT16 sMapX, INT16 sMapY ) { UINT16 pMoveDir[4][3]; UINT8 uiDirNumber = 0; UINT32 iRandomRes = 0, iRandom = 0; UINT8 x;//,y; SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] ); UINT8 uiMilitiaCount; BOOLEAN fSourceCityAllowsRoaming; // HEADROCK HAM 4: Player can set a sector to NO_LEAVE, forcing Mobiles to stay put if they enter the sector at all. if (gubManualRestrictMilitia[ SECTOR( sMapX, sMapY ) ] == MANUAL_MOBILE_NO_LEAVE ) { // NO_LEAVE zone. Militia can't even consider moving. return; } // If we don't want roaming militia // We shouldn't be here. if( !gGameExternalOptions.gfAllowMilitiaGroups ) return; if( sMapX == 1 && sMapY == 1 ) { ClearBlockMoveList(); gfMSBattle = FALSE; } if( CheckInBlockMoveList( sMapX, sMapY ) )return; uiMilitiaCount = CountMilitia(pSectorInfo); if( !uiMilitiaCount )return; // Kaiden: Moved Create Militia code into CreateMilitiaSquads // Removed the code from here and added the procedure call // If it's time, and this is a city sector create a new group. // But only if we're not training our own. if( (!gGameExternalOptions.gfmusttrainroaming) && ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ) ) { if (GetWorldDay( ) < gGameExternalOptions.guiAllowMilitiaGroupsDelay) return; if( GetWorldHour() % gGameExternalOptions.guiCreateEachNHours ) return; CreateMilitiaSquads( sMapX, sMapY ); if (gfStrategicMilitiaChangesMade) { ResetMilitia(); } } // HEADROCK HAM B2.7: If INI flag is set, allow militia to move out of Minor City sectors (Orta, Tixa, San Mona, etc). if (gGameExternalOptions.fAllowMilitiaMoveThroughMinorCities) fSourceCityAllowsRoaming = ( !gfMilitiaAllowedInTown[GetTownIdForSector( sMapX, sMapY )] ); else fSourceCityAllowsRoaming = ( GetTownIdForSector( sMapX, sMapY ) == BLANK_SECTOR ); // moving squad, if it is not a SAM site if( ( fSourceCityAllowsRoaming ) && (!IsThisSectorASAMSector( sMapX, sMapY, 0 )) ) { if( !PlayerMercsInSector_MSE( (UINT8)sMapX, (UINT8)sMapY, FALSE ) ) // and there's no player's mercs in the sector { if( GetWorldHour() % 2 )return; memset(pMoveDir, 0, sizeof(pMoveDir)); GenerateDirectionInfos( sMapX, sMapY, &uiDirNumber, pMoveDir, FALSE, FALSE ); if( uiDirNumber ) { for( x = 1; x < uiDirNumber ; ++x )pMoveDir[x][1] += pMoveDir[x-1][1]; // ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%ld,%ld", x, pMoveDir[x][1]); // HEADROCK HAM 3.6: Too many INI settings. Removed a couple, including MIN/MAX SQUAD SIZE. // iRandom = Random( pMoveDir[ uiDirNumber - 1 ][1] + // ( uiMilitiaCount >= gGameExternalOptions.guiMinMilitiaSquadSize ? CHANCE_TO_MOVE_A_SQUAD : CHANCE_TO_MOVE_AN_UNFULL_SQUAD ) ); iRandom = Random( pMoveDir[ uiDirNumber - 1 ][1] ); //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Roll %ld", iRandomRes); iRandomRes = 256; for( x = 0; x < uiDirNumber; ++x) if( iRandom < pMoveDir[x][1] ) { iRandomRes = x; break; } // "Decided" to stay here if(iRandomRes >= uiDirNumber) return; //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%ld,%ld:Dir count %ld, Rand %ld. Go to %ld,%ld. Have %ld militia men", sMapX, sMapY, uiDirNumber, iRandomRes, SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ), uiMilitiaCount); //Kaiden: if Restricted Sectors List option is turned on, // militia can't move to any sectors in the list. // Unless they are following a group of mercs. /////////////////////////////////////////////////////////////////// // HEADROCK HAM B1: Restrict roamers by XML as normal, but also // allow them to move through visited sectors. // Also, there's an option now to use dynamic restriction. The // AdjustRoamingRestrictions() function is used to dynamically // define which sectors are restricted, based on city conquest. /////////////////////////////////////////////////////////////////// // HEADROCK HAM 3.4: Moved the check to a separate function. if (gGameExternalOptions.gflimitedRoaming) { if (!IsSectorRoamingAllowed(pMoveDir[iRandomRes][0])) return; } // WDS bug fix for moving militia int targetX = SECTORX( pMoveDir[ iRandomRes ][0] ); int targetY = SECTORY( pMoveDir[ iRandomRes ][0] ); Assert(targetX >= 0 && targetX < MAP_WORLD_X); Assert(targetY >= 0 && targetY < MAP_WORLD_Y); MoveMilitiaSquad( sMapX, sMapY, targetX, targetY, FALSE ); if ( gfStrategicMilitiaChangesMade) { ResetMilitia(); } if( NumEnemiesInSector( targetX, targetY ) ) { extern GROUP *gpBattleGroup; gpBattleGroup = GetGroup( GetEnemyGroupIdInSector( targetX, targetY ) ); /* GROUP* pEnemyGroup = GetGroup( GetEnemyGroupIdInSector( targetX, targetY ) ); if(pEnemyGroup && pEnemyGroup->ubGroupID) { //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Attacking from %ld,%ld to %ld,%ld - enemy's group id %ld", sMapX, sMapY, targetX, SECTORY( pMoveDir[ iRandomRes ][0], pEnemyGroup->ubGroupID )); //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Arrival 1, Arrival 2"); pEnemyGroup->ubPrevX = sMapX; pEnemyGroup->ubPrevY = sMapY; pEnemyGroup->ubNextX = targetX; pEnemyGroup->ubNextY = targetY; */ gfMSBattle = TRUE; // GroupArrivedAtSector( pEnemyGroup->ubGroupID , TRUE, FALSE ); EnterAutoResolveMode( targetX, targetY ); // } } } else return; } } } // Kaiden: Roaming Militia Training: // If we're training roaming militia, // we'll get our squad from here: // Don't need to check for delay, as this function won't be // called if there is a delay set. void CreateMilitiaSquads(INT16 sMapX, INT16 sMapY ) { // Variables for Direction Rating checks UINT16 pMoveDir[4][3]; UINT8 uiDirNumber = 0; UINT32 iRandomRes = 0; UINT8 x;//,y; // Variables for checking other sectors in the same city UINT16 iCounter; UINT16 bTownId; // Tracks X/Y coordinates of another possible sector to train from (within same city) INT16 sCurrentX, sCurrentY; // Smart Militia Generator now locates the best sector for placement. UINT16 usBestLocalSectorID = 0; UINT16 usBestPerimeterSectorID = 0; UINT16 usBestLocalSectorRating = 0; UINT16 usBestPerimeterSectorRating = 0; // Selected Target coordinates UINT8 sTMapX = 0; UINT8 sTMapY = 0; // Leadership may affect quantity of militia generated. UINT8 ubBestLeadership = FindBestMilitiaTrainingLeadershipInSector ( sMapX, sMapY, 0, MOBILE_MILITIA ); // If we're not allowing roaming groups, then we're not creating them either. if( !gGameExternalOptions.gfAllowMilitiaGroups ) return; // Reset block move list. Does this cause issues with training in A1? Probably not. if( sMapX == 1 && sMapY == 1 ) { ClearBlockMoveList(); gfMSBattle = FALSE; } if( CheckInBlockMoveList( sMapX, sMapY ) )return; // Check all sectors adjacent to the training sector itself. GenerateDirectionInfosForTraining( sMapX, sMapY, &uiDirNumber, pMoveDir ); if(uiDirNumber)// At least one available sector found, adjacent to Source Training Sector { for ( x = 0; x < uiDirNumber; x++) { // Best rating so far? if (pMoveDir[x][1] > usBestLocalSectorRating) { // Mark this as the best "local" destination. usBestLocalSectorID = pMoveDir[x][0]; usBestLocalSectorRating = pMoveDir[x][1]; } } } // If we're training inside a city, check other city sectors for nearby available space. Maybe you'll find // a better place to put new mobiles? bTownId = GetTownIdForSector( sMapX, sMapY ); if( bTownId != BLANK_SECTOR ) { // We're training in a city. iCounter = 0; // Go through each city in the game while( pTownNamesList[ iCounter ] != 0 ) { // Are we in this city? if( pTownNamesList[ iCounter] == bTownId ) { sCurrentX = GET_X_FROM_STRATEGIC_INDEX( pTownLocationsList[ iCounter ] ); sCurrentY = GET_Y_FROM_STRATEGIC_INDEX( pTownLocationsList[ iCounter ] ); // if sector has enemies or hasn't already been taken at least once, then if ( !SectorInfo[ SECTOR(sCurrentX, sCurrentY) ].fSurfaceWasEverPlayerControlled || NumEnemiesInSector( sCurrentX, sCurrentY ) > 0 ) { // skip the rest. Generate no militia from this sector. iCounter++; continue; } // Find out if any adjacent sectors have room in them. GenerateDirectionInfosForTraining( sCurrentX, sCurrentY, &uiDirNumber, pMoveDir ); if(uiDirNumber)// At least one available adjacent sector has been found { // Go through each adjacent sector for( x = 0; x < uiDirNumber ; x++ ) { // Have we found the best rating so far? if ( pMoveDir[x][1] > usBestPerimeterSectorRating ) { usBestPerimeterSectorID = pMoveDir[x][0]; usBestPerimeterSectorRating = pMoveDir[x][1]; } } } } iCounter++; } } // Randomly choose whether to place locally or somewhere else around the city perimeter. // Local and Perimeter have separate "weights" in this randomization, but you're much more // likely to place militia near the original source sector. That is because it has increased // chance of being selected both as the best local and as the best overall, and so might be // selected as the result of either random choice. // Make sure at least one sector was found with more than 0 rating if (usBestLocalSectorRating > 0 || usBestPerimeterSectorRating > 0) { // Found at least one location. iRandomRes = PreRandom( usBestLocalSectorRating + usBestPerimeterSectorRating ); // Either go with randomization, or automatically select a Local sector if there is no better Perimeter sector. if (iRandomRes < usBestLocalSectorRating || usBestLocalSectorRating >= usBestPerimeterSectorRating) { sTMapX = SECTORX( usBestLocalSectorID ); sTMapY = SECTORY( usBestLocalSectorID ); // Generate militia in the best sector immediately adjacent to the Source Training Sector. GenerateMilitiaSquad( sMapX, sMapY, sTMapX, sTMapY, ubBestLeadership ); AddToBlockMoveList( sTMapX, sTMapY ); // Check for enemies in target sector if( NumEnemiesInSector( sTMapX, sTMapY ) ) { // Initiave battle gfMSBattle = TRUE; EnterAutoResolveMode( sTMapX, sTMapY ); } } else { sTMapX = SECTORX( usBestPerimeterSectorID ); sTMapY = SECTORY( usBestPerimeterSectorID ); // Generate militia in the best sector found in this city's perimeter. GenerateMilitiaSquad( sCurrentX, sCurrentY, sTMapX, sTMapY, ubBestLeadership ); AddToBlockMoveList( sTMapX, sTMapY ); // Check for enemies in target sector if( NumEnemiesInSector( sTMapX, sTMapY ) ) { // Initiave battle gfMSBattle = TRUE; EnterAutoResolveMode( sTMapX, sTMapY ); } } } return; } extern BOOLEAN gfMSResetMilitia; void DoMilitiaHelpFromAdjacentSectors( INT16 sMapX, INT16 sMapY ) { UINT16 pMoveDir[4][3]; UINT8 uiDirNumber = 0; UINT8 x; UINT8 uiNumGreen = 0, uiNumReg = 0, uiNumElite = 0; SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] ); BOOLEAN fMoreTroopsLeft[4] = {FALSE,FALSE,FALSE,FALSE}; BOOLEAN fFirstLoop = TRUE; BOOLEAN fMilitiaMoved = FALSE; guiDirNumber = 0; GenerateDirectionInfos( sMapX, sMapY, &uiDirNumber, pMoveDir, FALSE, TRUE ); ZeroMemory( gpAttackDirs, sizeof( gpAttackDirs ) ); gpAttackDirs[ guiDirNumber ][0] = uiNumGreen = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]; gpAttackDirs[ guiDirNumber ][1] = uiNumReg = pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]; gpAttackDirs[ guiDirNumber ][2] = uiNumElite = pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]; gpAttackDirs[ guiDirNumber ][3] = INSERTION_CODE_CENTER; guiDirNumber = uiDirNumber + 1; x = 0; while( CountMilitia(pSectorInfo ) < gGameExternalOptions.iMaxMilitiaPerSector && ( fMoreTroopsLeft[0] || fMoreTroopsLeft[1] || fMoreTroopsLeft[2] || fMoreTroopsLeft[3] || fFirstLoop ) ) { fMoreTroopsLeft[ x ] = MoveOneBestMilitiaMan( SECTORX( pMoveDir[ x ][0] ), SECTORY( pMoveDir[ x ][0] ), sMapX, sMapY ); if( fMoreTroopsLeft[ x ] ) { fMilitiaMoved = TRUE; gpAttackDirs[ x + 1 ][0] += pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA] - uiNumGreen; gpAttackDirs[ x + 1 ][1] += pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA] - uiNumReg; gpAttackDirs[ x + 1 ][2] += pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] - uiNumElite; gpAttackDirs[ x + 1 ][3] = (UINT8)pMoveDir[ x ][2]; uiNumGreen = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA]; uiNumReg = pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]; uiNumElite = pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]; } ++x; if( x >= uiDirNumber ) { x = 0; fFirstLoop = FALSE; } } if (is_networked) { if (gfStrategicMilitiaChangesMade) { RemoveMilitiaFromTactical(); if(is_server && gMilitiaEnabled == 1) PrepareMilitiaForTactical(FALSE); } } gfStrategicMilitiaChangesMade = FALSE; } void MSCallBack( UINT8 ubResult ) { if( ubResult == MSG_BOX_RETURN_YES ) { gTacticalStatus.uiFlags |= WANT_MILITIA_REINFORCEMENTS; DoMilitiaHelpFromAdjacentSectors( sMSMapX, sMSMapY ); } } BOOLEAN IsThereMilitiaInAdjacentSector( INT16 sMapX, INT16 sMapY ) { UINT16 pMoveDir[4][3]; UINT8 uiDirNumber = 0; UINT8 x; BOOLEAN fResult = FALSE; GenerateDirectionInfos( sMapX, sMapY, &uiDirNumber, pMoveDir, FALSE, TRUE ); for( x = 0; x < uiDirNumber ; ++x ) if( CountAllMilitiaInSector( SECTORX( pMoveDir[ x ][ 0 ] ), SECTORY( pMoveDir[ x ][ 0 ] ) ) ) fResult = TRUE; return fResult; } void MilitiaHelpFromAdjacentSectors( INT16 sMapX, INT16 sMapY ) { sMSMapX = sMapX; sMSMapY = sMapY; CHAR16 pStr[256]; CHAR16 pStr2[128]; GetSectorIDString( sMapX, sMapY, 0, pStr2, FALSE ); if( !gGameExternalOptions.gfAllowReinforcements ) return; gTacticalStatus.uiFlags &= (~WANT_MILITIA_REINFORCEMENTS); guiDirNumber = 0; // if( CountAllMilitiaInSector( sMapX, sMapY ) ) MSCallBack( MSG_BOX_RETURN_YES ); // This is no longer a question of simply whether to have a full militia count, but also whether we want // reinforcements. So if there are any available, always ask. if( IsThereMilitiaInAdjacentSector( sMapX, sMapY ) ) // && CountAllMilitiaInSector( sMapX, sMapY ) < gGameExternalOptions.guiMaxMilitiaSquadSizeBattle ) { // HEADROCK HAM 3.5: Added sector name to reinforcements string. swprintf(pStr, gzCWStrings[0], pStr2); //DoScreenIndependantMessageBox( gzCWStrings[0], MSG_BOX_FLAG_YESNO, MSCallBack ); DoScreenIndependantMessageBox( pStr, MSG_BOX_FLAG_YESNO, MSCallBack ); } } void MilitiaFollowPlayer( INT16 sMapX, INT16 sMapY, INT16 sDMapX, INT16 sDMapY ) { if( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR || IsThisSectorASAMSector( sMapX, sMapY, 0 ) || PlayerMercsInSector_MSE( (UINT8)sMapX, (UINT8)sMapY, TRUE ) )return; if( GetTownIdForSector( sDMapX, sDMapY ) != BLANK_SECTOR || IsThisSectorASAMSector( sDMapX, sDMapY, 0 ) )return; MoveMilitiaSquad( sMapX, sMapY, sDMapX, sDMapY, FALSE ); } // HEADROCK (HAM): New function to alter Restricted Roaming based on the recent capture of new towns. The area // around the town will be unrestricted, as well as some road sectors leading away from the town. // This function runs during sector conquest checks, and only if an entire town has been conquered. It also // runs at day end, as well as on load/save. // HEADROCK HAM 3.4: This hardcoded function has been made obsolete by XML externalization. /*void AdjustRoamingRestrictions() { if (!gGameExternalOptions.bDynamicRestrictRoaming) return; // to do: Add something to clean up the entire array before setting the flags... if ( IsTownUnderCompleteControlByPlayer(DRASSEN) ) { // DRASSEN OUTSKIRTS gDynamicRestrictMilitia[SECTOR(12,1)] = true; gDynamicRestrictMilitia[SECTOR(13,1)] = true; gDynamicRestrictMilitia[SECTOR(14,1)] = true; gDynamicRestrictMilitia[SECTOR(12,2)] = true; gDynamicRestrictMilitia[SECTOR(14,2)] = true; gDynamicRestrictMilitia[SECTOR(12,3)] = true; gDynamicRestrictMilitia[SECTOR(14,3)] = true; gDynamicRestrictMilitia[SECTOR(12,4)] = true; gDynamicRestrictMilitia[SECTOR(14,4)] = true; gDynamicRestrictMilitia[SECTOR(12,5)] = true; gDynamicRestrictMilitia[SECTOR(13,5)] = true; gDynamicRestrictMilitia[SECTOR(14,5)] = true; // SAM SITE DEFENSE gDynamicRestrictMilitia[SECTOR(15,5)] = true; // ROAD WEST TO OMERTA gDynamicRestrictMilitia[SECTOR(9,2)] = true; gDynamicRestrictMilitia[SECTOR(10,2)] = true; gDynamicRestrictMilitia[SECTOR(11,2)] = true; // ROAD SOUTH TO ALMA gDynamicRestrictMilitia[SECTOR(12,6)] = true; // HEADROCK HAM 3.3: Drassen SAM-Site (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(15,4)] = true; // HEADROCK HAM 3.3: Drassen ITSELF (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(13,2)] = true; gDynamicRestrictMilitia[SECTOR(13,3)] = true; gDynamicRestrictMilitia[SECTOR(13,4)] = true; // HEADROCK HAM 3.3: Forgot Omerta! gDynamicRestrictMilitia[SECTOR(9,1)] = true; gDynamicRestrictMilitia[SECTOR(10,1)] = true; } if ( IsTownUnderCompleteControlByPlayer(ALMA) ) { // ALMA OUTSKIRTS gDynamicRestrictMilitia[SECTOR(12,7)] = true; gDynamicRestrictMilitia[SECTOR(13,7)] = true; gDynamicRestrictMilitia[SECTOR(14,7)] = true; gDynamicRestrictMilitia[SECTOR(15,7)] = true; gDynamicRestrictMilitia[SECTOR(12,8)] = true; gDynamicRestrictMilitia[SECTOR(15,8)] = true; gDynamicRestrictMilitia[SECTOR(12,9)] = true; gDynamicRestrictMilitia[SECTOR(15,9)] = true; gDynamicRestrictMilitia[SECTOR(12,10)] = true; gDynamicRestrictMilitia[SECTOR(13,10)] = true; gDynamicRestrictMilitia[SECTOR(14,10)] = true; gDynamicRestrictMilitia[SECTOR(15,10)] = true; // ROAD SOUTH gDynamicRestrictMilitia[SECTOR(14,11)] = true; // HEADROCK HAM 3.3: Alma ITSELF (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(13,8)] = true; gDynamicRestrictMilitia[SECTOR(14,8)] = true; gDynamicRestrictMilitia[SECTOR(13,9)] = true; gDynamicRestrictMilitia[SECTOR(14,9)] = true; } if ( IsTownUnderCompleteControlByPlayer(CHITZENA) ) { // CHITZENA OUTSKIRTS gDynamicRestrictMilitia[SECTOR(1,1)] = true; gDynamicRestrictMilitia[SECTOR(3,1)] = true; gDynamicRestrictMilitia[SECTOR(1,2)] = true; gDynamicRestrictMilitia[SECTOR(3,2)] = true; gDynamicRestrictMilitia[SECTOR(1,3)] = true; gDynamicRestrictMilitia[SECTOR(2,3)] = true; gDynamicRestrictMilitia[SECTOR(3,3)] = true; // SAM SITE DEFENSE / ROAD SOUTH gDynamicRestrictMilitia[SECTOR(3,4)] = true; gDynamicRestrictMilitia[SECTOR(3,5)] = true; // HEADROCK HAM 3.3: Chitzena SAM-Site (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(2,4)] = true; // HEADROCK HAM 3.3: Chitzena ITSELF (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(2,1)] = true; gDynamicRestrictMilitia[SECTOR(2,2)] = true; } if ( IsTownUnderCompleteControlByPlayer(CAMBRIA) ) { // CAMBRIA OUTSKIRTS gDynamicRestrictMilitia[SECTOR(7,5)] = true; gDynamicRestrictMilitia[SECTOR(8,5)] = true; gDynamicRestrictMilitia[SECTOR(9,5)] = true; gDynamicRestrictMilitia[SECTOR(10,5)] = true; gDynamicRestrictMilitia[SECTOR(7,6)] = true; gDynamicRestrictMilitia[SECTOR(10,6)] = true; gDynamicRestrictMilitia[SECTOR(7,7)] = true; gDynamicRestrictMilitia[SECTOR(10,7)] = true; gDynamicRestrictMilitia[SECTOR(7,8)] = true; gDynamicRestrictMilitia[SECTOR(9,8)] = true; gDynamicRestrictMilitia[SECTOR(10,8)] = true; // SAM SITE DEFENSE gDynamicRestrictMilitia[SECTOR(7,9)] = true; gDynamicRestrictMilitia[SECTOR(9,9)] = true; // ROAD TO OMERTA gDynamicRestrictMilitia[SECTOR(9,2)] = true; gDynamicRestrictMilitia[SECTOR(9,3)] = true; gDynamicRestrictMilitia[SECTOR(9,4)] = true; // CROSSROADS WEST gDynamicRestrictMilitia[SECTOR(6,7)] = true; // ROAD TO ALMA gDynamicRestrictMilitia[SECTOR(11,7)] = true; // HEADROCK HAM 3.3: Cambria SAM-Site (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(8,9)] = true; // HEADROCK HAM 3.3: Cambria ITSELF (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(8,6)] = true; gDynamicRestrictMilitia[SECTOR(9,6)] = true; gDynamicRestrictMilitia[SECTOR(8,7)] = true; gDynamicRestrictMilitia[SECTOR(9,7)] = true; gDynamicRestrictMilitia[SECTOR(8,8)] = true; // HEADROCK HAM 3.3: Forgot Omerta! gDynamicRestrictMilitia[SECTOR(9,1)] = true; gDynamicRestrictMilitia[SECTOR(10,1)] = true; } if ( IsTownUnderCompleteControlByPlayer(BALIME) ) { // BALIME OUTSKIRTS gDynamicRestrictMilitia[SECTOR(10,11)] = true; gDynamicRestrictMilitia[SECTOR(11,11)] = true; gDynamicRestrictMilitia[SECTOR(12,11)] = true; gDynamicRestrictMilitia[SECTOR(13,11)] = true; gDynamicRestrictMilitia[SECTOR(10,12)] = true; gDynamicRestrictMilitia[SECTOR(13,12)] = true; gDynamicRestrictMilitia[SECTOR(10,13)] = true; gDynamicRestrictMilitia[SECTOR(13,13)] = true; // WEST ROAD gDynamicRestrictMilitia[SECTOR(6,11)] = true; gDynamicRestrictMilitia[SECTOR(7,11)] = true; gDynamicRestrictMilitia[SECTOR(8,11)] = true; gDynamicRestrictMilitia[SECTOR(9,11)] = true; // SOUTH ROAD gDynamicRestrictMilitia[SECTOR(8,14)] = true; gDynamicRestrictMilitia[SECTOR(9,14)] = true; gDynamicRestrictMilitia[SECTOR(10,14)] = true; // HEADROCK HAM 3.3: Balime ITSELF (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(11,12)] = true; gDynamicRestrictMilitia[SECTOR(12,12)] = true; } if ( IsTownUnderCompleteControlByPlayer(GRUMM) ) { // GRUMM OUTSKIRTS gDynamicRestrictMilitia[SECTOR(1,6)] = true; gDynamicRestrictMilitia[SECTOR(2,6)] = true; gDynamicRestrictMilitia[SECTOR(3,6)] = true; gDynamicRestrictMilitia[SECTOR(3,7)] = true; gDynamicRestrictMilitia[SECTOR(4,7)] = true; gDynamicRestrictMilitia[SECTOR(4,8)] = true; gDynamicRestrictMilitia[SECTOR(3,9)] = true; gDynamicRestrictMilitia[SECTOR(4,9)] = true; // ROAD NORTH gDynamicRestrictMilitia[SECTOR(3,5)] = true; gDynamicRestrictMilitia[SECTOR(3,4)] = true; // ROAD EAST gDynamicRestrictMilitia[SECTOR(5,7)] = true; gDynamicRestrictMilitia[SECTOR(6,7)] = true; // ROAD SOUTH gDynamicRestrictMilitia[SECTOR(2,10)] = true; gDynamicRestrictMilitia[SECTOR(3,10)] = true; gDynamicRestrictMilitia[SECTOR(2,11)] = true; // HEADROCK HAM 3.3: Grumm ITSELF (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(1,7)] = true; gDynamicRestrictMilitia[SECTOR(2,7)] = true; gDynamicRestrictMilitia[SECTOR(1,8)] = true; gDynamicRestrictMilitia[SECTOR(2,8)] = true; gDynamicRestrictMilitia[SECTOR(3,8)] = true; } if ( IsTownUnderCompleteControlByPlayer(MEDUNA) ) { // HEADROCK HAM 3.3: Meduna ITSELF (for reinforcement purposes) gDynamicRestrictMilitia[SECTOR(3,14)] = true; gDynamicRestrictMilitia[SECTOR(4,14)] = true; gDynamicRestrictMilitia[SECTOR(5,14)] = true; gDynamicRestrictMilitia[SECTOR(3,15)] = true; gDynamicRestrictMilitia[SECTOR(4,15)] = true; gDynamicRestrictMilitia[SECTOR(3,16)] = true; } if ( IsTownUnderCompleteControlByPlayer(DRASSEN) && IsTownUnderCompleteControlByPlayer(CHITZENA) ) { // ROAD EAST OF SAN-MONA gDynamicRestrictMilitia[SECTOR(7,3)] = true; gDynamicRestrictMilitia[SECTOR(8,3)] = true; gDynamicRestrictMilitia[SECTOR(9,3)] = true; // SAN MONA gDynamicRestrictMilitia[SECTOR(5,3)] = true; gDynamicRestrictMilitia[SECTOR(6,3)] = true; gDynamicRestrictMilitia[SECTOR(4,4)] = true; gDynamicRestrictMilitia[SECTOR(5,4)] = true; } if ( IsTownUnderCompleteControlByPlayer(CAMBRIA) && ( IsTownUnderCompleteControlByPlayer(CHITZENA) || IsTownUnderCompleteControlByPlayer(DRASSEN) ) ) { // ROAD TO SAN MONA gDynamicRestrictMilitia[SECTOR(7,3)] = true; gDynamicRestrictMilitia[SECTOR(8,3)] = true; gDynamicRestrictMilitia[SECTOR(9,3)] = true; } if ( IsTownUnderCompleteControlByPlayer(CAMBRIA) && ( IsTownUnderCompleteControlByPlayer(DRASSEN) || IsTownUnderCompleteControlByPlayer(CHITZENA) || IsTownUnderCompleteControlByPlayer(GRUMM) ) ) { // ROAD SOUTH-EAST OF SAN MONA gDynamicRestrictMilitia[SECTOR(6,4)] = true; gDynamicRestrictMilitia[SECTOR(7,4)] = true; // SAN MONA gDynamicRestrictMilitia[SECTOR(5,3)] = true; gDynamicRestrictMilitia[SECTOR(6,3)] = true; gDynamicRestrictMilitia[SECTOR(4,4)] = true; gDynamicRestrictMilitia[SECTOR(5,4)] = true; } if ( IsTownUnderCompleteControlByPlayer(ALMA) && ( IsTownUnderCompleteControlByPlayer(DRASSEN) || IsTownUnderCompleteControlByPlayer(CAMBRIA) ) ) { // ROAD WEST OF ALMA gDynamicRestrictMilitia[SECTOR(11,7)] = true; } if ( IsTownUnderCompleteControlByPlayer(CHITZENA) && ( IsTownUnderCompleteControlByPlayer(DRASSEN) || IsTownUnderCompleteControlByPlayer(CAMBRIA) || IsTownUnderCompleteControlByPlayer(GRUMM) ) ) { // SAN MONA gDynamicRestrictMilitia[SECTOR(5,3)] = true; gDynamicRestrictMilitia[SECTOR(6,3)] = true; gDynamicRestrictMilitia[SECTOR(4,4)] = true; gDynamicRestrictMilitia[SECTOR(5,4)] = true; // ROAD TO SAN MONA gDynamicRestrictMilitia[SECTOR(7,3)] = true; gDynamicRestrictMilitia[SECTOR(8,3)] = true; gDynamicRestrictMilitia[SECTOR(9,3)] = true; } if ( IsTownUnderCompleteControlByPlayer(CAMBRIA) && IsTownUnderCompleteControlByPlayer(ALMA) ) { // ROAD EAST OF CAMBRIA gDynamicRestrictMilitia[SECTOR(11,7)] = true; } if ( IsTownUnderCompleteControlByPlayer(CAMBRIA) && ( IsTownUnderCompleteControlByPlayer(BALIME) || IsTownUnderCompleteControlByPlayer(GRUMM) || IsTownUnderCompleteControlByPlayer(ALMA) ) ) { // UPGRADED SAM-SITE DEFENSE FOR CAMBRIA gDynamicRestrictMilitia[SECTOR(7,10)] = true; gDynamicRestrictMilitia[SECTOR(8,10)] = true; gDynamicRestrictMilitia[SECTOR(9,10)] = true; // Tixa. Won't be accessible unless Minor Cities are allowed. } if ( IsTownUnderCompleteControlByPlayer(CAMBRIA) && IsTownUnderCompleteControlByPlayer(GRUMM) ) { // ESTONI gDynamicRestrictMilitia[SECTOR(6,8)] = true; // ESTONI MOUNTAIN GATE gDynamicRestrictMilitia[SECTOR(6,9)] = true; // SOUTHEAST ROAD TO SAN MONA gDynamicRestrictMilitia[SECTOR(6,4)] = true; gDynamicRestrictMilitia[SECTOR(7,4)] = true; } if ( IsTownUnderCompleteControlByPlayer(BALIME) && IsTownUnderCompleteControlByPlayer(GRUMM) && IsTownUnderCompleteControlByPlayer(CAMBRIA) ) { // GRUMM-CAMBRIA-BALIME ROAD gDynamicRestrictMilitia[SECTOR(6,10)] = true; } // CONTROL ALL TOWNS? if ( IsTownUnderCompleteControlByPlayer(BALIME) && IsTownUnderCompleteControlByPlayer(CAMBRIA) && IsTownUnderCompleteControlByPlayer(CHITZENA) && IsTownUnderCompleteControlByPlayer(DRASSEN) && IsTownUnderCompleteControlByPlayer(ALMA) && IsTownUnderCompleteControlByPlayer(GRUMM) ) { // ROADS TO MEDUNA gDynamicRestrictMilitia[SECTOR(6,12)] = true; gDynamicRestrictMilitia[SECTOR(7,14)] = true; gDynamicRestrictMilitia[SECTOR(2,12)] = true; // MEDUNA OUTSKIRTS gDynamicRestrictMilitia[SECTOR(2,13)] = true; gDynamicRestrictMilitia[SECTOR(3,13)] = true; gDynamicRestrictMilitia[SECTOR(4,13)] = true; gDynamicRestrictMilitia[SECTOR(5,13)] = true; gDynamicRestrictMilitia[SECTOR(6,13)] = true; gDynamicRestrictMilitia[SECTOR(6,14)] = true; } }*/ void AdjustRoamingRestrictions() { UINT32 uiCapturedTownsFlag = 0; UINT16 cnt = 0; if (!gGameExternalOptions.fDynamicRestrictRoaming) return; // to do: Add something to clean up the entire array before setting the flags... for (cnt=1; cnt < NUM_TOWNS; cnt++) { if ( IsTownUnderCompleteControlByPlayer((UINT8)cnt) ) { // Flag town as captured. uiCapturedTownsFlag |= (1 << (cnt-1)); } } for (cnt=0; cnt < 256; cnt++) { gDynamicRestrictMilitia[cnt] = false; } for (cnt=0; (cnt < 5001 && gDynamicRestrictions[cnt].sSectorID >= 0); cnt++) { if (!(~uiCapturedTownsFlag & gDynamicRestrictions[cnt].uiReqTownFlags)) { gDynamicRestrictMilitia[gDynamicRestrictions[cnt].sSectorID] = true; // HEADROCK HAM 4: Also adjust Player-based restrictions. if (gubManualRestrictMilitia[ gDynamicRestrictions[cnt].sSectorID ] == MANUAL_MOBILE_RESTRICTED) { gubManualRestrictMilitia[ gDynamicRestrictions[cnt].sSectorID ] = MANUAL_MOBILE_NO_RESTRICTION; } } } } // HEADROCK HAM B2.7: This is a copy of an existing function that generates possible movement directions for militia. // Due to the widespread use of that function, I've decided to make a new one that calculates directions // when generating militia. This function checks for available space at the target location. The stored results // indicate the amount of space left in the target sector. // HEADROCK HAM 3.6: Revamped to fix bugs. void GenerateDirectionInfosForTraining( INT16 sMapX, INT16 sMapY, UINT8* uiDirNumber, UINT16 pMoveDir[4][3] ) { SECTORINFO *pSectorInfo; UINT8 ubMaxMilitia = gGameExternalOptions.iMaxMilitiaPerSector; *uiDirNumber = 0; UINT16 usMilitiaCapacity = 0; UINT16 usUpgradeCapacity = 0; // Clean up pMoveDir for (UINT8 x = 0; x < 4; x++) { pMoveDir[x][0]=0; pMoveDir[x][1]=0; pMoveDir[x][2]=0; } #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%ld,%ld-------------------", sMapX, sMapY ); #endif // Check north if( sMapY > MINIMUM_VALID_Y_COORDINATE ) { pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY - 1 ) ] ); // Are militia allowed to travel in that direction from the current sector, for training purposes? if ( CheckStandardConditionsForDirection( sMapX, sMapY, sMapX, sMapY - 1, TRUE, FALSE ) ) { // Count free slots. Each slot is worth 3 points. usMilitiaCapacity = 3 * (ubMaxMilitia - CountMilitia( pSectorInfo )); // Count militia that can be upgraded. Regular = 1 point, Green = 2 points. usUpgradeCapacity = MilitiaUpgradeSlotsCheck( pSectorInfo ); // Found at least one slot here? if (usUpgradeCapacity || usMilitiaCapacity) { // Store data in return variable. pMoveDir[ *uiDirNumber ][0] = SECTOR( sMapX, sMapY - 1 ); pMoveDir[ *uiDirNumber ][1] = usMilitiaCapacity + usUpgradeCapacity; pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_NORTH; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); #endif ++(*uiDirNumber); } } } // Check west if( sMapX > MINIMUM_VALID_X_COORDINATE ) { pSectorInfo = &( SectorInfo[ SECTOR( sMapX - 1, sMapY ) ] ); // Are militia allowed to travel in that direction from the current sector, for training purposes? if ( CheckStandardConditionsForDirection( sMapX, sMapY, sMapX - 1, sMapY, TRUE, FALSE ) ) { // Count free slots. Each slot is worth 3 points. usMilitiaCapacity = 3 * (ubMaxMilitia - CountMilitia( pSectorInfo )); // Count militia that can be upgraded. Regular = 1 point, Green = 2 points. usUpgradeCapacity = MilitiaUpgradeSlotsCheck( pSectorInfo ); // Found at least one slot here? if (usUpgradeCapacity || usMilitiaCapacity) { // Store data in return variable. pMoveDir[ *uiDirNumber ][0] = SECTOR( sMapX - 1, sMapY ); pMoveDir[ *uiDirNumber ][1] = usMilitiaCapacity + usUpgradeCapacity; pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_WEST; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); #endif ++(*uiDirNumber); } } } // Check south if( sMapY < MAXIMUM_VALID_Y_COORDINATE ) { pSectorInfo = &( SectorInfo[ SECTOR( sMapX , sMapY + 1 ) ] ); // Are militia allowed to travel in that direction from the current sector, for training purposes? if ( CheckStandardConditionsForDirection( sMapX, sMapY, sMapX, sMapY + 1, TRUE, FALSE ) ) { // Count free slots. Each slot is worth 3 points. usMilitiaCapacity = 3 * (ubMaxMilitia - CountMilitia( pSectorInfo )); // Count militia that can be upgraded. Regular = 1 point, Green = 2 points. usUpgradeCapacity = MilitiaUpgradeSlotsCheck( pSectorInfo ); // Found at least one slot here? if (usUpgradeCapacity || usMilitiaCapacity) { // Store data in return variable. pMoveDir[ *uiDirNumber ][0] = SECTOR( sMapX, sMapY + 1 ); pMoveDir[ *uiDirNumber ][1] = usMilitiaCapacity + usUpgradeCapacity; pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_SOUTH; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); #endif ++(*uiDirNumber); } } } // Check east if( sMapX < MAXIMUM_VALID_X_COORDINATE ) { pSectorInfo = &( SectorInfo[ SECTOR( sMapX + 1 , sMapY ) ] ); // Are militia allowed to travel in that direction from the current sector, for training purposes? if ( CheckStandardConditionsForDirection( sMapX, sMapY, sMapX +1 , sMapY, TRUE, FALSE ) ) { // Count free slots. Each slot is worth 3 points. usMilitiaCapacity = 3 * (ubMaxMilitia - CountMilitia( pSectorInfo )); // Count militia that can be upgraded. Regular = 1 point, Green = 2 points. usUpgradeCapacity = MilitiaUpgradeSlotsCheck( pSectorInfo ); // Found at least one slot here? if (usUpgradeCapacity || usMilitiaCapacity) { // Store data in return variable. pMoveDir[ *uiDirNumber ][0] = SECTOR( sMapX + 1, sMapY ); pMoveDir[ *uiDirNumber ][1] = usMilitiaCapacity + usUpgradeCapacity; pMoveDir[ *uiDirNumber ][2] = INSERTION_CODE_EAST; #ifdef DEBUG_SHOW_RATINGS if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Priority of north direction - %ld", pMoveDir[ *uiDirNumber ][1]); #endif ++(*uiDirNumber); } } } } // HEADROCK HAM 3.4: Something that should've been done a long time ago - moving the check of roaming restrictions // to a separate function. Returns TRUE if the sector can be traversed by militia. BOOLEAN IsSectorRoamingAllowed( UINT32 uiSector ) { // EXPLORATION-BASED RESTRICTION // Is militia allowed to move through visited sectors? if (gGameExternalOptions.fUnrestrictVisited) { // Has the sector ever been visited? if ( SectorInfo[ uiSector ].fSurfaceWasEverPlayerControlled ) { // HEADROCK HAM 4: Take player-based restrictions into account... if ( gubManualRestrictMilitia[ uiSector ] == MANUAL_MOBILE_NO_RESTRICTION || gubManualRestrictMilitia[ uiSector ] == MANUAL_MOBILE_NO_LEAVE ) { // Always return TRUE. return TRUE; } } } // Please note that with exploration restrictions, an UNVISITED sector is not neccesarily RESTRICTED. // XML-BASED RESTRICTION // Is destination allowed by the Dynamic Restrictions Array? if( !gDynamicRestrictMilitia[ uiSector ] ) { // Don't restrict movement if there are player mercs in the target sector! Otherwise, the player // cannot have militia follow him to attack the enemy. if (!PlayerMercsInSector_MSE( SECTORX(uiSector), SECTORY(uiSector), FALSE )) { return FALSE; } } else { // HEADROCK HAM 4: Take player restrictions into account if ( gubManualRestrictMilitia[ uiSector ] == MANUAL_MOBILE_NO_ENTER ) { if ( !NumEnemiesInSector( SECTORX(uiSector), SECTORY(uiSector) ) ) { // The player has disallowed movement into this sector, manually. As the condition above // dictates, this only takes effect if there are no enemies in the target sector. Otherwise, // militia can move to intercept them anyway. return FALSE; } } } return TRUE; } // HEADROCK HAM 3.6: Function returns the number of "upgrade slots" available at target sector. Basically, every GREEN // Militia at the target sector counts as 2 upgrade slots, while every regular counts as 1. UINT16 MilitiaUpgradeSlotsCheck( SECTORINFO * pSectorInfo ) { // Result variables UINT16 usNumUpgradeSlots = 0; // Target sector full of elites? if (pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] >= gGameExternalOptions.iMaxMilitiaPerSector) { // Militia cannot be placed or moved here at all. return 0; } // If not allowed to train elites ( or not yet ) if (!gGameExternalOptions.gfTrainVeteranMilitia || // Not allowed at all (GetWorldDay( ) < gGameExternalOptions.guiTrainVeteranMilitiaDelay)) // Not yet allowed. { // If sector full of Regulars and Elites? if (pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]+pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA] >= gGameExternalOptions.iMaxMilitiaPerSector) // Target sector full of regular+elite militia { // Can't upgrade Regulars to Elites, so the sector is already full. return 0; } // Each Green we can convert to Regular counts as 2 slots. usNumUpgradeSlots = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA] * 2; return (usNumUpgradeSlots); } // Allowed to train Green->Regular, and Regular->Elite usNumUpgradeSlots = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA] * 2; usNumUpgradeSlots += pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA]; return (usNumUpgradeSlots); } // HEADROCK HAM 4: Returns whether sector is allowed for militia roaming, taking into account player-set restrictions. UINT8 ManualMobileMovementAllowed( UINT8 ubSector ) { BOOLEAN fRestricted = TRUE; if (gGameExternalOptions.gflimitedRoaming) { if (gGameExternalOptions.fUnrestrictVisited) { // Has the sector ever been visited? if ( SectorInfo[ ubSector ].fSurfaceWasEverPlayerControlled ) { // Always return TRUE. fRestricted = FALSE; } } if(gGameExternalOptions.fDynamicRestrictRoaming) { if (gDynamicRestrictMilitia[ ubSector ] ) { fRestricted = FALSE; } } } else { fRestricted = FALSE; } if (fRestricted) { // Restricted roaming! Militia can't go here ever. return (0); } else { if (gubManualRestrictMilitia[ ubSector ] == MANUAL_MOBILE_NO_ENTER ) { // Player-restricted. Militia can potentially go here, if the player removed the restriction. return (1); } else if (gubManualRestrictMilitia[ ubSector ] == MANUAL_MOBILE_NO_LEAVE ) { // Militia allowed to enter, but not leave. return (2); } else { // Roaming allowed. return (3); } } } void InitManualMobileRestrictions() { UINT16 x; for ( x = 0; x < 256; x++ ) { UINT8 ubTownID = GetTownIdForSector( SECTORX(x), SECTORY(x) ); if (ubTownID != BLANK_SECTOR ) { if (gfMilitiaAllowedInTown[ubTownID]) { // Major town. Set to NO-ENTER by default. gubManualRestrictMilitia[x] = MANUAL_MOBILE_NO_ENTER; continue; } else { // Minor town. Set to NO_RESTRICTIONS by default. gubManualRestrictMilitia[x] = MANUAL_MOBILE_NO_RESTRICTION; continue; } } // By default, all other sectors are "go". gubManualRestrictMilitia[x] = MANUAL_MOBILE_NO_RESTRICTION; } }