Files
source/Strategic/MilitiaSquads.cpp
T

1793 lines
63 KiB
C++

#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 "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"
#include "Campaign.h"
#include "message.h"
#include "mapscreen.h"
#include "Strategic Pathing.h"
#include "GameVersion.h"
#include "connect.h"
#include "MilitiaSquads.h"
#include "Reinforcement.h"
#include "Inventory Choosing.h" // added by Flugente for MoveOneMilitiaEquipmentSet() and MoveMilitiaEquipment()
#include "MilitiaIndividual.h" // added by Flugente
// Debug defines
//#define DEBUG_SHOW_RATINGS
#ifdef DEBUG_SHOW_RATINGS
#define DEBUG_RATINGS_CONDITION TRUE
//#define DEBUG_RATINGS_CONDITION !fForBattle
#include "message.h"
#endif
// 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
UINT16 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->usGroupTeam == OUR_TEAM )// && !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 usTeam )
{
UINT8 ubRes = 0;
GROUP *curr = gpGroupList;
while( curr )
{
if ( curr->ubSectorX == sMapX && curr->ubSectorY == sMapY && curr->usGroupTeam == usTeam )
{
if( !curr->ubGroupID )
return curr->ubGroupID;
else
ubRes = curr->ubGroupID;
}
curr = curr->next;
}
return ubRes;
}
// Creates militia at destination sector and removes it from starting sector
void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOLEAN fAlternativeMax, BOOLEAN fSkipSpreading )
{
SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] );
SECTORINFO *pTSectorInfo = &( SectorInfo[ SECTOR( sTMapX, sTMapY ) ] );
UINT16 bGreensSourceTeam = 0, bGreensDestTeam = 0;
UINT16 bRegularsSourceTeam = 0, bRegularsDestTeam = 0;
UINT16 bElitesSourceTeam = 0, bElitesDestTeam = 0;
UINT16 bTotalGreens = 0, bTotalRegulars = 0, bTotalElites = 0;
UINT8 bTotalGreensPercent = 0, bTotalRegularsPercent = 0, bTotalElitesPercent = 0;
INT16 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 (fSkipSpreading || 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.
UINT16 ubNumEnemiesNearOrigin = NumEnemiesInFiveSectors( sMapX, sMapY );
UINT16 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.
//Moa: removed below: reinforcement is handled elsewhere, the function below is even better then 50%/50% because it leaves
//a chance to make one sector fully defended instead of possibly two losses. Talking about unfull groups - full groups wont change anyway.
/*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
{
///////////////////////////////////////////////////////
// Both sectors not threatened.
// Alter spreadout chance based on number of troops in Source and destination sectors.
if ( NumNonPlayerTeamMembersInSector( sMapX, sMapY, MILITIA_TEAM ) >= gGameExternalOptions.iMaxMilitiaPerSector &&
NumNonPlayerTeamMembersInSector( sTMapX, sTMapY, MILITIA_TEAM ) == 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 ( NumNonPlayerTeamMembersInSector( sMapX, sMapY, MILITIA_TEAM ) && NumNonPlayerTeamMembersInSector( sTMapX, sTMapY, MILITIA_TEAM ) )
{
// 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;
}
ubChanceToSpreadOut = 0;
}
}
// Should we spread out into the target sector, or move en-masse?
if (PreRandom(100) < ubChanceToSpreadOut)
{
if ( NumNonPlayerTeamMembersInSector( sMapX, sMapY, MILITIA_TEAM ) && NumNonPlayerTeamMembersInSector( sTMapX, sTMapY, MILITIA_TEAM ) )
{
// 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;
// Flugente: mobiles take along their gear
// move only gear for those who come new into a sector
UINT16 elites = max(0, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]);
UINT16 regulars = max(0, bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]);
UINT16 greens = max(0, bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]);
MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, elites, regulars, greens);
// Flugente: update individual militia data
ApplyTacticalLifeRatioToMilitia( );
// 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 ] );
// Flugente: move along individual militia data
MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), greens, regulars, elites );
// 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;
// Flugente: mobiles take along their gear
// move only gear for those who come new into a sector
UINT16 elites = max(0, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]);
UINT16 regulars = max(0, bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]);
UINT16 greens = max(0, bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]);
MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, elites, regulars, greens);
// Flugente: update individual militia data
ApplyTacticalLifeRatioToMilitia( );
// Remove half team from source sector
StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, bGreensDestTeam );
StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, bRegularsDestTeam );
StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, bElitesDestTeam );
// Flugente: move along individual militia data
MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), greens, regulars, elites );
// Add half team to target sector
StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, bGreensDestTeam );
StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, bRegularsDestTeam );
StrategicAddMilitiaToSector( sTMapX, sTMapY, 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( (NumNonPlayerTeamMembersInSector( sTMapX, sTMapY, MILITIA_TEAM ) + NumNonPlayerTeamMembersInSector( sMapX, sMapY, MILITIA_TEAM )), (UINT16)gGameExternalOptions.iMaxMilitiaPerSector );
bNewSourceGroupSize = __max( 0, (NumNonPlayerTeamMembersInSector( sTMapX, sTMapY, MILITIA_TEAM ) + NumNonPlayerTeamMembersInSector( sMapX, sMapY, MILITIA_TEAM )) - 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;
}
}
// Flugente: mobiles take along their gear
// move only gear for those who come new into a sector
UINT16 elites = max(0, bElitesDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ]);
UINT16 regulars = max(0, bRegularsDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]);
UINT16 greens = max(0, bGreensDestTeam - pTSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]);
MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, elites, regulars, greens);
// Flugente: update individual militia data
ApplyTacticalLifeRatioToMilitia( );
// 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 ] );
// Flugente: move along individual militia data
MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), greens, regulars, elites );
// 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
{
// Flugente: mobiles take along their gear
MoveMilitiaEquipment(sMapX, sMapY, sTMapX, sTMapY, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ], pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ], pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ]);
// Flugente: update individual militia data
ApplyTacticalLifeRatioToMilitia( );
UINT16 greens = pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA];
UINT16 regulars = pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA];
UINT16 elites = pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA];
StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, greens );
StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, regulars );
StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, elites );
// Flugente: move along individual militia data
MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), greens, regulars, elites );
// Entire group moves from Source to Target, leaving no one behind.
StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, greens );
StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, regulars );
StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, elites );
// Block target group from moving. It has just assimilated another group.
AddToBlockMoveList( sTMapX, sTMapY );
}
}
while ( !fAlternativeMax && NumNonPlayerTeamMembersInSector( sTMapX, sTMapY, MILITIA_TEAM ) > 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)
{
if ( MilitiaInSectorOfRank( sMapX, sMapY, ELITE_MILITIA ) )
{
StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, 1 );
StrategicRemoveMilitiaFromSector( sMapX, sMapY, ELITE_MILITIA, 1 );
MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), 0, 0, 1 );
return TRUE;
}
if ( MilitiaInSectorOfRank( sMapX, sMapY, REGULAR_MILITIA ) )
{
StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, 1 );
StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, 1 );
MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), 0, 1, 0 );
return TRUE;
}
if ( MilitiaInSectorOfRank( sMapX, sMapY, GREEN_MILITIA ) )
{
StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, 1 );
StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, 1 );
MoveIndividualMilitiaProfiles( SECTOR( sMapX, sMapY ), SECTOR( sTMapX, sTMapY ), 1, 0, 0 );
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 ] );
UINT16 uiSumOfEnemyTroops = NumNonPlayerTeamMembersInSector( sLMX, sLMY, ENEMY_TEAM );
//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;
}
UINT16 ubMaxSquadSize = gGameExternalOptions.iMaxMilitiaPerSector;
UINT16 ubSourceGreens = SectorInfo[SECTOR(sMapX, sMapY)].ubNumberOfCivsAtLevel[ GREEN_MILITIA ];
UINT16 ubSourceRegulars = SectorInfo[SECTOR(sMapX, sMapY)].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ];
UINT16 ubSourceElites = SectorInfo[SECTOR(sMapX, sMapY)].ubNumberOfCivsAtLevel[ ELITE_MILITIA ];
UINT16 ubTargetGreens = SectorInfo[SECTOR(sDMapX, sDMapY)].ubNumberOfCivsAtLevel[ GREEN_MILITIA ];
UINT16 ubTargetRegulars = SectorInfo[SECTOR(sDMapX, sDMapY)].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ];
UINT16 ubTargetElites = SectorInfo[SECTOR(sDMapX, sDMapY)].ubNumberOfCivsAtLevel[ ELITE_MILITIA ];
UINT16 ubSourceMilitia = ubSourceGreens + ubSourceRegulars + ubSourceElites;
UINT16 ubTargetMilitia = ubTargetGreens + ubTargetRegulars + ubTargetElites;
UINT16 usSourceEnemiesInFiveSectors = NumEnemiesInFiveSectors( sMapX, sMapY );
UINT16 usTargetEnemiesInFiveSectors = NumEnemiesInFiveSectors( sDMapX, sDMapY );
UINT16 ubTargetEnemies = NumNonPlayerTeamMembersInSector( sDMapX, sDMapY, ENEMY_TEAM );
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;
}
// 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, NumNonPlayerTeamMembersInSector( sDMapX, sDMapY, ENEMY_TEAM ), ((INT32)CountAllMilitiaInSector( sMapX, sMapY )) - ((INT32)NumNonPlayerTeamMembersInSector( sDMapX, sDMapY, ENEMY_TEAM )) );
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 && NumNonPlayerTeamMembersInSector( sMapX, sMapY, ENEMY_TEAM ) )
{
fReadyForBattle = FALSE;
}
else
{
fReadyForBattle = TRUE;
}
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)
{
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 && !NumNonPlayerTeamMembersInSector( sMapX, sMapY, ENEMY_TEAM ) )
{
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)
{
// 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)
{
for( UINT16 i = 0; i < uiBlockMoveListPointer ; ++i)
{
if( sMapX == SECTORX(pBlockMoveList[i]) && sMapY == SECTORY(pBlockMoveList[i]) )
return TRUE;
}
return FALSE;
}
void MilitiaMovementOrder(UINT8 sector)
{
INT16 sX = SECTORX(sector);
INT16 sY = SECTORY(sector);
int strategicsector = CALCULATE_STRATEGIC_INDEX( sX, sY );
// if we are in gamescreen and a battle is going on, and this is the sector that militia moves from, don't move them. Suddenly disappearing would be... awkward
if ( guiCurrentScreen == GAME_SCREEN && gTacticalStatus.uiFlags & INCOMBAT && gWorldSectorX == sX && gWorldSectorY == sY && !gbWorldSectorZ )
{
// remove all movement flags
StrategicMap[strategicsector].usFlags &= ~MILITIA_MOVE_ALLDIRS;
return;
}
// to stop the player from just setting someone in HQ to give orders and then reassigning them, we simply demand that there'll also be someone when the militia peform the travelling
if ( !MercStaffsMilitaryHQ() )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gpStrategicString[STR_MILITIAMOVEMENT_NO_STAFF_ABORT] );
// remove all movement flags
StrategicMap[strategicsector].usFlags &= ~MILITIA_MOVE_ALLDIRS;
return;
}
INT16 targetX = sX;
INT16 targetY = sY;
if ( StrategicMap[strategicsector].usFlags & MILITIA_MOVE_NORTH )
--targetY;
else if ( StrategicMap[strategicsector].usFlags & MILITIA_MOVE_WEST )
--targetX;
else if ( StrategicMap[strategicsector].usFlags & MILITIA_MOVE_EAST )
++targetX;
else if ( StrategicMap[strategicsector].usFlags & MILITIA_MOVE_SOUTH )
++targetY;
// remove all movement flags
StrategicMap[strategicsector].usFlags &= ~MILITIA_MOVE_ALLDIRS;
UINT8 targetsector = SECTOR(targetX, targetY);
// if militia moves from the currently loaded sector, it has to drop all gear first
if ( gGameExternalOptions.fMilitiaUseSectorInventory && gWorldSectorX == sX && gWorldSectorY == sY && !gbWorldSectorZ )
{
TeamDropAll( MILITIA_TEAM );
}
MoveMilitiaSquad( sX, sY, targetX, targetY, FALSE, TRUE );
if ( gfStrategicMilitiaChangesMade)
ResetMilitia();
if ( NumNonPlayerTeamMembersInSector( targetX, targetY, ENEMY_TEAM ) )
{
gfMSBattle = TRUE;
EnterAutoResolveMode( targetX, targetY );
}
}
extern BOOLEAN gfMSResetMilitia;
void DoMilitiaHelpFromAdjacentSectors( INT16 sMapX, INT16 sMapY )
{
UINT16 pMoveDir[4][3];
UINT8 uiDirNumber = 0;
UINT8 x;
UINT16 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 ( NumNonPlayerTeamMembersInSector( sMapX, sMapY, MILITIA_TEAM ) < 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;
}
// Flugente: order sNumber reinforcements from src sector to target sector
BOOLEAN CallMilitiaReinforcements( INT16 sTargetMapX, INT16 sTargetMapY, INT16 sSrcMapX, INT16 sSrcMapY, UINT16 sNumber )
{
UINT16 uiNumGreen = 0, uiNumReg = 0, uiNumElite = 0;
SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sTargetMapX, sTargetMapY ) ] );
guiDirNumber = 0;
UINT8 insertioncode = INSERTION_CODE_CENTER;
UINT8 movetype = THROUGH_STRATEGIC_MOVE;
// determine from which direction militia should enter (and exit if this isn't possible)
if ( sTargetMapX == sSrcMapX + 1 && sTargetMapY == sSrcMapY )
{
insertioncode = INSERTION_CODE_WEST;
movetype = EAST_STRATEGIC_MOVE;
}
else if ( sTargetMapX == sSrcMapX - 1 && sTargetMapY == sSrcMapY )
{
insertioncode = INSERTION_CODE_EAST;
movetype = WEST_STRATEGIC_MOVE;
}
else if ( sTargetMapX == sSrcMapX && sTargetMapY == sSrcMapY + 1 )
{
insertioncode = INSERTION_CODE_NORTH;
movetype = SOUTH_STRATEGIC_MOVE;
}
else if ( sTargetMapX == sSrcMapX && sTargetMapY == sSrcMapY - 1 )
{
insertioncode = INSERTION_CODE_SOUTH;
movetype = NORTH_STRATEGIC_MOVE;
}
else
{
// no proper direction here... get out
return FALSE;
}
// test wether travel from src to target is possible ( we cannot open the src map information, we'll rely on the xml data instead
if( SectorInfo[ SECTOR(sSrcMapX,sSrcMapY) ].ubTraversability[ movetype ] == GROUNDBARRIER || SectorInfo[ SECTOR(sSrcMapX,sSrcMapY) ].ubTraversability[ movetype ] == EDGEOFWORLD )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia cannot traverse to this sector due to the terrain." );
return FALSE;
}
guiDirNumber = 0;
ZeroMemory( gpAttackDirs, sizeof( gpAttackDirs ) );
gpAttackDirs[guiDirNumber][0] = uiNumGreen = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, GREEN_MILITIA );
gpAttackDirs[guiDirNumber][1] = uiNumReg = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, REGULAR_MILITIA );
gpAttackDirs[guiDirNumber][2] = uiNumElite = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, ELITE_MILITIA );
gpAttackDirs[ guiDirNumber ][3] = INSERTION_CODE_CENTER;
guiDirNumber = insertioncode + 1;
// we can't move more than we are told, and can't add more than would fit into the sector
UINT16 possibletomove = min( sNumber, gGameExternalOptions.iMaxMilitiaPerSector - NumNonPlayerTeamMembersInSector( sTargetMapX, sTargetMapY, MILITIA_TEAM ) );
UINT16 sMilitiaMoved = 0;
while ( sMilitiaMoved < possibletomove && MoveOneBestMilitiaMan( sSrcMapX, sSrcMapY, sTargetMapX, sTargetMapY ) )
{
++sMilitiaMoved;
UINT16 militia_green = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, GREEN_MILITIA );
UINT16 militia_troop = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, REGULAR_MILITIA );
UINT16 militia_elite = MilitiaInSectorOfRank( sTargetMapX, sTargetMapY, ELITE_MILITIA );
gpAttackDirs[guiDirNumber][0] += militia_green - uiNumGreen;
gpAttackDirs[guiDirNumber][1] += militia_troop - uiNumReg;
gpAttackDirs[guiDirNumber][2] += militia_elite - uiNumElite;
gpAttackDirs[ guiDirNumber ][3] = insertioncode;
uiNumGreen = militia_green;
uiNumReg = militia_troop;
uiNumElite = militia_elite;
}
guiDirNumber = 5;
if ( !sMilitiaMoved )
return FALSE;
// we need to use this flag. If it wasn't set prior to this, we remove it again afterwards, otherwise all militia will join us if we are in combat
BOOLEAN wantreinforcements = (gTacticalStatus.uiFlags & WANT_MILITIA_REINFORCEMENTS);
gTacticalStatus.uiFlags |= WANT_MILITIA_REINFORCEMENTS;
if (is_networked)
{
if (gfStrategicMilitiaChangesMade)
{
RemoveMilitiaFromTactical();
if(is_server && gMilitiaEnabled == 1)
PrepareMilitiaForTactical(FALSE);
}
}
else
PrepareMilitiaForTactical(FALSE);
if ( !wantreinforcements )
gTacticalStatus.uiFlags &= ~WANT_MILITIA_REINFORCEMENTS;
gfStrategicMilitiaChangesMade = FALSE;
return TRUE;
}
BOOLEAN MilitiaGroupEntersCurrentSector( UINT8 usGroupId, INT16 sMapX, INT16 sMapY )
{
GROUP* pGroup = GetGroup( usGroupId );
if ( !pGroup || pGroup->usGroupTeam != MILITIA_TEAM )
return FALSE;
INT16 sSrcMapX = pGroup->ubPrevX;
INT16 sSrcMapY = pGroup->ubPrevY;
UINT8 insertioncode = INSERTION_CODE_CENTER;
UINT8 movetype = THROUGH_STRATEGIC_MOVE;
// determine from which direction militia should enter (and exit if this isn't possible)
if ( sMapX == sSrcMapX + 1 && sMapY == sSrcMapY )
{
insertioncode = INSERTION_CODE_WEST;
movetype = EAST_STRATEGIC_MOVE;
}
else if ( sMapX == sSrcMapX - 1 && sMapY == sSrcMapY )
{
insertioncode = INSERTION_CODE_EAST;
movetype = WEST_STRATEGIC_MOVE;
}
else if ( sMapX == sSrcMapX && sMapY == sSrcMapY + 1 )
{
insertioncode = INSERTION_CODE_NORTH;
movetype = SOUTH_STRATEGIC_MOVE;
}
else if ( sMapX == sSrcMapX && sMapY == sSrcMapY - 1 )
{
insertioncode = INSERTION_CODE_SOUTH;
movetype = NORTH_STRATEGIC_MOVE;
}
else
{
// no proper direction here... get out
return FALSE;
}
// test wether travel from src to target is possible ( we cannot open the src map information, we'll rely on the xml data instead
if ( SectorInfo[SECTOR( sSrcMapX, sSrcMapY )].ubTraversability[movetype] == GROUNDBARRIER || SectorInfo[SECTOR( sSrcMapX, sSrcMapY )].ubTraversability[movetype] == EDGEOFWORLD )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Militia cannot traverse to this sector due to the terrain." );
return FALSE;
}
ZeroMemory( gpAttackDirs, sizeof(gpAttackDirs) );
guiDirNumber = insertioncode + 1;
gpAttackDirs[guiDirNumber][0] = pGroup->pEnemyGroup->ubNumAdmins;
gpAttackDirs[guiDirNumber][1] = pGroup->pEnemyGroup->ubNumTroops;
gpAttackDirs[guiDirNumber][2] = pGroup->pEnemyGroup->ubNumElites;
gpAttackDirs[guiDirNumber][3] = insertioncode;
guiDirNumber = 5;
// we need to use this flag. If it wasn't set prior to this, we remove it again afterwards, otherwise all militia will join us if we are in combat
BOOLEAN wantreinforcements = (gTacticalStatus.uiFlags & WANT_MILITIA_REINFORCEMENTS);
gTacticalStatus.uiFlags |= WANT_MILITIA_REINFORCEMENTS;
if ( is_networked )
{
if ( gfStrategicMilitiaChangesMade )
{
RemoveMilitiaFromTactical( );
if ( is_server && gMilitiaEnabled == 1 )
PrepareMilitiaForTactical( FALSE );
}
}
else
PrepareMilitiaForTactical( FALSE );
if ( !wantreinforcements )
gTacticalStatus.uiFlags &= ~WANT_MILITIA_REINFORCEMENTS;
gfStrategicMilitiaChangesMade = FALSE;
return TRUE;
}
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;
GenerateDirectionInfos( sMapX, sMapY, &uiDirNumber, pMoveDir, FALSE, TRUE );
for ( UINT8 x = 0; x < uiDirNumber; ++x )
{
if ( NumNonPlayerTeamMembersInSector( SECTORX( pMoveDir[x][0] ), SECTORY( pMoveDir[x][0] ), MILITIA_TEAM ) )
return TRUE;
}
return FALSE;
}
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 );
}
}
// 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] )
{
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 )
{
// 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 - NumNonPlayerTeamMembersInSector( sMapX, sMapY - 1, MILITIA_TEAM ));
// Count militia that can be upgraded. Regular = 1 point, Green = 2 points.
usUpgradeCapacity = MilitiaUpgradeSlotsCheck( sMapX, sMapY - 1 );
// 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 )
{
// 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 - NumNonPlayerTeamMembersInSector( sMapX - 1, sMapY, MILITIA_TEAM ));
// Count militia that can be upgraded. Regular = 1 point, Green = 2 points.
usUpgradeCapacity = MilitiaUpgradeSlotsCheck( sMapX - 1, sMapY );
// 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 )
{
// 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 - NumNonPlayerTeamMembersInSector( sMapX, sMapY + 1, MILITIA_TEAM ));
// Count militia that can be upgraded. Regular = 1 point, Green = 2 points.
usUpgradeCapacity = MilitiaUpgradeSlotsCheck( sMapX, sMapY + 1 );
// 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 )
{
// 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 - NumNonPlayerTeamMembersInSector( sMapX + 1, sMapY, MILITIA_TEAM ) );
// Count militia that can be upgraded. Regular = 1 point, Green = 2 points.
usUpgradeCapacity = MilitiaUpgradeSlotsCheck( sMapX + 1, sMapY );
// 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.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( INT16 sMapX, INT16 sMapY )
{
// Result variables
UINT16 usNumUpgradeSlots = 0;
// Target sector full of elites?
if ( MilitiaInSectorOfRank( sMapX, sMapY, 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 ( MilitiaInSectorOfRank( sMapX, sMapY, REGULAR_MILITIA ) + MilitiaInSectorOfRank( sMapX, sMapY, 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 = MilitiaInSectorOfRank( sMapX, sMapY, GREEN_MILITIA ) * 2;
return (usNumUpgradeSlots);
}
// Allowed to train Green->Regular, and Regular->Elite
usNumUpgradeSlots = MilitiaInSectorOfRank( sMapX, sMapY, GREEN_MILITIA ) * 2;
usNumUpgradeSlots += MilitiaInSectorOfRank( sMapX, sMapY, REGULAR_MILITIA );
return (usNumUpgradeSlots);
}
// Flugente: militia movement
BOOLEAN SaveMilitiaMovementInformationToSaveGameFile( HWFILE hFile )
{
UINT32 uiNumBytesWritten = 0;
UINT8 number = MILITIA_PATROLS_MAX;
//Save the number of elements
FileWrite( hFile, &number, sizeof(UINT8), &uiNumBytesWritten );
if ( uiNumBytesWritten != sizeof(UINT8) )
{
return(FALSE);
}
for ( UINT8 cnt = 0; cnt < MILITIA_PATROLS_MAX; ++cnt )
{
UINT32 uiNumOfNodes = 0;
PathStPtr pTempPath = gMilitiaPath[cnt].path;
uiNumBytesWritten = 0;
//loop through to get all the nodes
while ( pTempPath )
{
++uiNumOfNodes;
pTempPath = pTempPath->pNext;
}
//Save the number of the nodes
FileWrite( hFile, &uiNumOfNodes, sizeof(UINT32), &uiNumBytesWritten );
if ( uiNumBytesWritten != sizeof(UINT32) )
{
return(FALSE);
}
//loop through all the nodes and add them
pTempPath = gMilitiaPath[cnt].path;
//loop through nodes and save all the nodes
while ( pTempPath )
{
//Save the number of the nodes
FileWrite( hFile, pTempPath, sizeof(PathSt), &uiNumBytesWritten );
if ( uiNumBytesWritten != sizeof(PathSt) )
{
return(FALSE);
}
pTempPath = pTempPath->pNext;
}
//Save the group id
FileWrite( hFile, &gMilitiaPath[cnt].sGroupid, sizeof(INT16), &uiNumBytesWritten );
if ( uiNumBytesWritten != sizeof(INT16) )
{
return(FALSE);
}
}
return(TRUE);
}
BOOLEAN LoadMilitiaMovementInformationFromSavedGameFile( HWFILE hFile, UINT32 uiSavedGameVersion )
{
UINT32 uiNumBytesRead;
UINT32 uiTotalNodeCount = 0;
UINT32 uiNodeCount = 0;
PathSt *pPath = NULL;
UINT8 ubPassengerCnt = 0;
UINT8 numpaths = 0;
DeleteAllMilitiaPaths( );
// if this is an older savegame, nothing to read
if ( uiSavedGameVersion < MILITIA_PATH_PLOTTING )
return TRUE;
//Load the number of elements
FileRead( hFile, &numpaths, sizeof(UINT8), &uiNumBytesRead );
if ( uiNumBytesRead != sizeof(UINT8) )
{
return(FALSE);
}
if ( numpaths != MILITIA_PATROLS_MAX )
return(FALSE);
for ( UINT32 cnt = 0; cnt < MILITIA_PATROLS_MAX; ++cnt )
{
UINT32 uiNumOfNodes = 0;
PathStPtr pTempPath = NULL;
PathStPtr pTemp = NULL;
UINT32 uiNumBytesRead = 0;
INT16 groupid = 0;
//Load the number of the nodes
FileRead( hFile, &uiNumOfNodes, sizeof(UINT32), &uiNumBytesRead );
if ( uiNumBytesRead != sizeof(UINT32) )
{
return(FALSE);
}
//load all the nodes
for ( UINT32 cnt2 = 0; cnt2 < uiNumOfNodes; ++cnt2 )
{
//Allocate memory for the new node
pTemp = (PathStPtr)MemAlloc( sizeof(PathSt) );
if ( pTemp == NULL )
{
pTempPath = MoveToBeginningOfPathList( pTempPath );
ClearStrategicPathList( pTempPath, -1 );
return(FALSE);
}
memset( pTemp, 0, sizeof(PathSt) );
//Load the node
FileRead( hFile, pTemp, sizeof(PathSt), &uiNumBytesRead );
if ( uiNumBytesRead != sizeof(PathSt) )
{
MemFree( pTemp );
pTempPath = MoveToBeginningOfPathList( pTempPath );
ClearStrategicPathList( pTempPath, -1 );
return(FALSE);
}
//Put the node into the list
if ( cnt2 == 0 )
{
pTempPath = pTemp;
pTemp->pPrev = NULL;
}
else
{
pTempPath->pNext = pTemp;
pTemp->pPrev = pTempPath;
pTempPath = pTempPath->pNext;
}
pTemp->pNext = NULL;
}
//move to beginning of list
pTempPath = MoveToBeginningOfPathList( pTempPath );
gMilitiaPath[cnt].path = pTempPath;
if ( gMilitiaPath[cnt].path )
gMilitiaPath[cnt].path->pPrev = NULL;
//Load the number of the nodes
FileRead( hFile, &groupid, sizeof(INT16), &uiNumBytesRead );
if ( uiNumBytesRead != sizeof(INT16) )
{
return(FALSE);
}
gMilitiaPath[cnt].sGroupid = groupid;
}
return(TRUE);
}
// delete a militia group and transfer its militia to the sector they are in
void DissolveMilitiaGroup( UINT8 uGroupId )
{
GROUP* pGroup = GetGroup( uGroupId );
if ( pGroup && pGroup->usGroupTeam == MILITIA_TEAM )
{
ClearMercPathsAndWaypointsForAllInGroup( pGroup );
StrategicAddMilitiaToSector( pGroup->ubSectorX, pGroup->ubSectorY, GREEN_MILITIA, pGroup->pEnemyGroup->ubNumAdmins );
StrategicAddMilitiaToSector( pGroup->ubSectorX, pGroup->ubSectorY, REGULAR_MILITIA, pGroup->pEnemyGroup->ubNumTroops );
StrategicAddMilitiaToSector( pGroup->ubSectorX, pGroup->ubSectorY, ELITE_MILITIA, pGroup->pEnemyGroup->ubNumElites );
RemovePGroup( pGroup );
}
}
void DissolveAllMilitiaGroupsInSector( INT16 sMapX, INT16 sMapY )
{
GROUP* pGroup = gpGroupList;
while ( pGroup )
{
if ( pGroup->usGroupTeam == MILITIA_TEAM && pGroup->ubSectorX == sMapX && pGroup->ubSectorY == sMapY )
{
GROUP* pDeleteGroup = pGroup;
pGroup = pGroup->next;
DissolveMilitiaGroup( pDeleteGroup->ubGroupID );
}
else
pGroup = pGroup->next;
}
}