diff --git a/Strategic/MilitiaSquads.cpp b/Strategic/MilitiaSquads.cpp new file mode 100644 index 000000000..675e61d39 --- /dev/null +++ b/Strategic/MilitiaSquads.cpp @@ -0,0 +1,681 @@ +#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" +#endif + +#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 + +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->bLife ) && !( pPlayer->pSoldier->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 +void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY ) +{ + SECTORINFO *pSectorInfo = &( SectorInfo[ SECTOR( sMapX, sMapY ) ] ); + + StrategicAddMilitiaToSector( sTMapX, sTMapY, GREEN_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] / DIV_OF_ORIGINAL_MILITIA); + StrategicAddMilitiaToSector( sTMapX, sTMapY, REGULAR_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] / DIV_OF_ORIGINAL_MILITIA); + StrategicAddMilitiaToSector( sTMapX, sTMapY, ELITE_MILITIA, pSectorInfo->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] / DIV_OF_ORIGINAL_MILITIA); + + pSectorInfo = &( SectorInfo[ SECTOR( sTMapX, sTMapY ) ] ); + + while( CountMilitia(pSectorInfo) > MAXIMUM_MILITIA_SQUAD_SIZE ) + if(pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA])--pSectorInfo->ubNumberOfCivsAtLevel[GREEN_MILITIA];else + if(pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA])--pSectorInfo->ubNumberOfCivsAtLevel[REGULAR_MILITIA];else + if(pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA])--pSectorInfo->ubNumberOfCivsAtLevel[ELITE_MILITIA]; + +} + +// 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 ) ] ); + + 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 ] ); + + while( !fAlternativeMax && CountMilitia(pTSectorInfo) > MAXIMUM_MILITIA_SQUAD_SIZE || + fAlternativeMax && CountMilitia(pTSectorInfo) > MAXIMUM_MILITIA_SQUAD_SIZE_BATTLE ) + 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];} +} + +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 = 0; sLMX < 16 ; ++sLMX ) + for( sLMY = 0; sLMY < 16 ; ++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( sLMY - sMapY, 2 ) + pow( 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; + INT32 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; + } + + if( CountAllMilitiaInSector( sDMapX, sDMapY ) && + CountAllMilitiaInSector( sDMapX, sDMapY ) + CountAllMilitiaInSector( sMapX, sMapY ) <= MAXIMUM_MILITIA_SQUAD_SIZE ) + iRes += DIR_WITH_UNFULL_SQUAD_RATING_BONUS; + + if( NumEnemiesInSector( sDMapX, sDMapY ) ) + { + // if( GetTownIdForSector( sMapX, sMapY ) == BLANK_SECTOR ) + iDiff = iRes * ( (FLOAT)CountAllMilitiaInFiveSectors( sDMapX, sDMapY ) / (FLOAT)NumEnemiesInFiveSectors( sDMapX, sDMapY ) ); + // else + // iDiff = iRes * ( (FLOAT)CountAllMilitiaInFiveSectors( sMapX, sMapY ) / DIV_OF_ORIGINAL_MILITIA / (FLOAT)NumEnemiesInFiveSectors( sDMapX, sDMapY ) ); + + if( iDiff > (FLOAT)DIR_MIN_DIF * iRes ) + iRes = iDiff; + else + iRes = 0; + } + + +// There's player's mercs! Go there + if( PlayerMercsInSector_MSE( sDMapX, sDMapY, FALSE ) ) + iRes += 15000;// should be enough + +// 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 +BOOLEAN CheckStandardConditionsForDirection( INT16 sSMapX, INT16 sSMapY, INT16 sMapX, INT16 sMapY, BOOLEAN fWithCities, BOOLEAN fForBattle, BOOLEAN fOnlyCitySectors ) +{ + UINT8 uiTravab = GetTraversability( SECTOR( sSMapX , sSMapY ) , SECTOR( sMapX, sMapY ) ); + + if( uiTravab == GROUNDBARRIER || uiTravab == EDGEOFWORLD ) return FALSE; + + if( !fForBattle && gfMSBattle && NumEnemiesInSector( sMapX, sMapY ) ) return FALSE; + + return (GetTownIdForSector( sMapX, sMapY ) == BLANK_SECTOR || fWithCities) && !IsThisSectorASAMSector( sMapX, sMapY , 0 ) && + (GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR || !fOnlyCitySectors);// && +// ( !NumEnemiesInSector( sMapX, sMapY ) || GetEnemyGroupIdInSector(sMapX, sMapY ) || fForBattle ) && +// ( fForBattle || CountAllMilitiaInSector( sMapX, sMapY ) < MAXIMUM_MILITIA_SQUAD_SIZE || PlayerMercsInSector( sMapX, sMapY, 0 ) ) +} + +void GenerateDirectionInfos( INT16 sMapX, INT16 sMapY, UINT8* uiDirNumber, UINT16 pMoveDir[4][3], BOOLEAN fWithCities, BOOLEAN fForBattle, BOOLEAN fOnlyCitySectors ) +{ + *uiDirNumber = 0; + +#ifdef DEBUG_SHOW_RATINGS + if( DEBUG_RATINGS_CONDITION )ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%ld,%ld-------------------", sMapX, sMapY ); +#endif + + if(CheckStandardConditionsForDirection( sMapX, sMapY, sMapX, sMapY - 1, fWithCities, fForBattle, fOnlyCitySectors ) ) + { + 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(CheckStandardConditionsForDirection( sMapX, sMapY, sMapX - 1, sMapY, fWithCities, fForBattle, fOnlyCitySectors )) + { + 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(CheckStandardConditionsForDirection( sMapX, sMapY, sMapX, sMapY + 1, fWithCities, fForBattle, fOnlyCitySectors )) + { + 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(CheckStandardConditionsForDirection( sMapX, sMapY, sMapX + 1, sMapY, fWithCities, fForBattle, fOnlyCitySectors )) + { + 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; + + if( !gGameExternalOptions.gfAllowMilitiaGroups ) + return; + + if( sMapX == 1 && sMapY == 1 ) + { + ClearBlockMoveList(); + gfMSBattle = FALSE; + } + + if( CheckInBlockMoveList( sMapX, sMapY ) )return; + + uiMilitiaCount = CountMilitia(pSectorInfo); + + if( !uiMilitiaCount )return; + + // new squad + if( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ) + { + if( GetWorldHour() % CREATE_EACH_N_HOURS )return; + + GenerateDirectionInfos( sMapX, sMapY, &uiDirNumber, pMoveDir, FALSE, FALSE, FALSE ); + + if(uiDirNumber)// && Random(100) < CHANCE_TO_GENERATE_A_SQUAD) + { + for( x = 1; x < uiDirNumber ; ++x )pMoveDir[x][1] += pMoveDir[x-1][1]; + + iRandomRes = Random( pMoveDir[ uiDirNumber - 1 ][1] ); + + for( x = 0; x < uiDirNumber; ++x) + if( iRandomRes < pMoveDir[x][1] ) + { + iRandomRes = x; + break; + } + + // shouldn't be! + if(iRandomRes >= uiDirNumber)iRandomRes = 0; + + //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%ld,%ld:Dir count %ld, Rand %ld. Go to %ld,%ld", sMapX, sMapY, uiDirNumber, iRandomRes, SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] )); + + GenerateMilitiaSquad( sMapX, sMapY, SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ); + AddToBlockMoveList( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ); + + if( NumEnemiesInSector( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ) && CountAllMilitiaInSector( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ) ) + { +/* GROUP* pEnemyGroup = GetGroup( GetEnemyGroupIdInSector( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ) ); + + 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, SECTORX( pMoveDir[ iRandomRes ][0] ), 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 = SECTORX( pMoveDir[ iRandomRes ][0] ); + pEnemyGroup->ubNextY = SECTORY( pMoveDir[ iRandomRes ][0] ); +*/ + gfMSBattle = TRUE; +// GroupArrivedAtSector( pEnemyGroup->ubGroupID , TRUE, FALSE ); + + EnterAutoResolveMode( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ); +// } + } + + } + else return; + + } + // moving squad, if it is not a SAM site + else if(!IsThisSectorASAMSector( sMapX, sMapY, 0 )) + if( !PlayerMercsInSector_MSE( sMapX, sMapY, FALSE ) ) // and there's no player's mercs in the sector + { + if( GetWorldHour() % 2 )return; + + GenerateDirectionInfos( sMapX, sMapY, &uiDirNumber, pMoveDir, FALSE, 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]); + + iRandom = Random( pMoveDir[ uiDirNumber - 1 ][1] + + ( uiMilitiaCount >= MINIMUM_MILITIA_SQUAD_SIZE ? CHANCE_TO_MOVE_A_SQUAD : CHANCE_TO_MOVE_AN_UNFULL_SQUAD ) ); + + //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); + + MoveMilitiaSquad( sMapX, sMapY, SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ), FALSE ); + AddToBlockMoveList( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ); + + if( NumEnemiesInSector( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ) ) + { +/* GROUP* pEnemyGroup = GetGroup( GetEnemyGroupIdInSector( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ) ); + + 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, SECTORX( pMoveDir[ iRandomRes ][0] ), 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 = SECTORX( pMoveDir[ iRandomRes ][0] ); + pEnemyGroup->ubNextY = SECTORY( pMoveDir[ iRandomRes ][0] ); +*/ + gfMSBattle = TRUE; + + // GroupArrivedAtSector( pEnemyGroup->ubGroupID , TRUE, FALSE ); + EnterAutoResolveMode( SECTORX( pMoveDir[ iRandomRes ][0] ), SECTORY( pMoveDir[ iRandomRes ][0] ) ); +// } + } + } + else 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 fMilitiaAlreadyBeen = CountMilitia(pSectorInfo )>0 ? TRUE : FALSE; + + guiDirNumber = 0; + + GenerateDirectionInfos( sMapX, sMapY, &uiDirNumber, pMoveDir, + ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, FALSE ); + + 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 ) < MAXIMUM_MILITIA_SQUAD_SIZE_BATTLE && + ( fMoreTroopsLeft[0] || fMoreTroopsLeft[1] || fMoreTroopsLeft[2] || fMoreTroopsLeft[3] || fFirstLoop ) ) + { + fMoreTroopsLeft[ x ] = MoveOneBestMilitiaMan( SECTORX( pMoveDir[ x ][0] ), SECTORY( pMoveDir[ x ][0] ), sMapX, sMapY ); + + if( fMilitiaAlreadyBeen )gfMSResetMilitia = TRUE; + + if( fMoreTroopsLeft[ x ] ) + { + 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] = 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; + } + } + +} + +void MSCallBack( UINT8 ubResult ) +{ + if( ubResult == MSG_BOX_RETURN_YES ) + 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, + ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, FALSE ); + + 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; + + if( !gGameExternalOptions.gfAllowMilitiaGroups ) + return; + + if( CountAllMilitiaInSector( sMapX, sMapY ) ) MSCallBack( MSG_BOX_RETURN_YES ); + + if( IsThereMilitiaInAdjacentSector( sMapX, sMapY ) && CountAllMilitiaInSector( sMapX, sMapY ) < MAXIMUM_MILITIA_SQUAD_SIZE_BATTLE ) + DoScreenIndependantMessageBox( gzCWStrings[0], 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( sMapX, sMapY, TRUE ) )return; + + if( GetTownIdForSector( sDMapX, sDMapY ) != BLANK_SECTOR || + IsThisSectorASAMSector( sDMapX, sDMapY, 0 ) )return; + + + MoveMilitiaSquad( sMapX, sMapY, sDMapX, sDMapY, FALSE ); +} diff --git a/Strategic/MilitiaSquads.h b/Strategic/MilitiaSquads.h new file mode 100644 index 000000000..e539b6305 --- /dev/null +++ b/Strategic/MilitiaSquads.h @@ -0,0 +1,21 @@ +#ifndef _MILITIA_SQUADS_H +#define _MILITIA_SQUADS_H + +// header for town militia strategic control module + +#include "Types.h" +#include "Soldier Control.h" + +#define MAXIMUM_MILITIA_SQUAD_SIZE 30 + +extern UINT8 gpAttackDirs[5][4]; +extern UINT8 guiDirNumber; + +void UpdateMilitiaSquads(INT16 sMapX, INT16 sMapY ); +void MilitiaHelpFromAdjacentSectors( INT16 sMapX, INT16 sMapY ); + +void GenerateDirectionInfos( INT16 sMapX, INT16 sMapY, UINT8* uiDirNumber, UINT16 pMoveDir[4][3], BOOLEAN fWithCities, BOOLEAN fForBattle, BOOLEAN fOnlyCitySectors ); +BOOLEAN MoveOneBestMilitiaMan(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY); +void MilitiaFollowPlayer( INT16 sMapX, INT16 sMapY, INT16 sDMapX, INT16 sDMapY ); + +#endif \ No newline at end of file diff --git a/Strategic/Reinforcement.cpp b/Strategic/Reinforcement.cpp new file mode 100644 index 000000000..12091d092 --- /dev/null +++ b/Strategic/Reinforcement.cpp @@ -0,0 +1,423 @@ +#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" +#endif + +#include "Reinforcement.h" +#include "MilitiaSquads.h" + +#define IS_ONLY_IN_CITIES ( gGameExternalOptions.gfAllowReinforcmentsOnlyInCity ? TRUE: FALSE ) + +UINT8 gubReinforcementMinEnemyStaticGroupSize = 12; + +void GetNumberOfEnemiesInFiveSectors( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites ) +{ + UINT8 ubNumAdmins, ubNumTroops, ubNumElites; + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber, ubIndex; + + GetNumberOfStationaryEnemiesInSector( sSectorX, sSectorY, pubNumAdmins, pubNumTroops, pubNumElites ); + + GetNumberOfMobileEnemiesInSector( sSectorX, sSectorY, &ubNumAdmins, &ubNumTroops, &ubNumElites ); + + *pubNumAdmins += ubNumAdmins; + *pubNumTroops += ubNumTroops; + *pubNumElites += ubNumElites; + + if( !gGameExternalOptions.gfAllowReinforcments ) + return; + + GenerateDirectionInfos( sSectorX, sSectorY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sSectorX, sSectorY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + for( ubIndex = 0; ubIndex < ubDirNumber; ubIndex++ ) + { + GetNumberOfStationaryEnemiesInSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ), &ubNumAdmins, &ubNumTroops, &ubNumElites ); + + while( ubNumElites + ubNumTroops + ubNumAdmins > gubReinforcementMinEnemyStaticGroupSize) + { + if( ubNumElites ) + { + *pubNumElites += 1; + ubNumElites--; + }else if( ubNumTroops ) + { + *pubNumTroops += 1; + ubNumTroops--; + }else if( ubNumAdmins ) + { + *pubNumAdmins += 1; + ubNumAdmins--; + } + } + + + GetNumberOfMobileEnemiesInSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ), &ubNumAdmins, &ubNumTroops, &ubNumElites ); + + *pubNumAdmins += ubNumAdmins; + *pubNumTroops += ubNumTroops; + *pubNumElites += ubNumElites; + } +} + +UINT8 NumEnemiesInFiveSectors( INT16 sMapX, INT16 sMapY ) +{ + UINT8 ubNumAdmins, ubNumTroops, ubNumElites; + + GetNumberOfEnemiesInFiveSectors( sMapX, sMapY, &ubNumAdmins, &ubNumTroops, &ubNumElites ); + + return ubNumAdmins + ubNumTroops + ubNumElites; +} + +BOOLEAN IsGroupInARightSectorToReinforce( GROUP *pGroup, INT16 sSectorX, INT16 sSectorY ) +{ + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber = 0, ubIndex; + + if( pGroup->ubSectorX == sSectorX && pGroup->ubSectorY == sSectorY ) + return TRUE; //Well, it's in the same sector, so allow to reinforce + + if( !gGameExternalOptions.gfAllowReinforcments ) + return FALSE; + + GenerateDirectionInfos( sSectorX, sSectorY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sSectorX, sSectorY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + for( ubIndex = 0; ubIndex < ubDirNumber; ubIndex++ ) + if( pGroup->ubSectorX == SECTORX( pusMoveDir[ ubIndex ][ 0 ] ) && pGroup->ubSectorY == SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ) + return TRUE; + + return FALSE; +} + +UINT8 GetAdjacentSectors( UINT8 pSectors[4], INT16 sSectorX, INT16 sSectorY ) +{ + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber = 0, ubIndex; + UINT8 ubCounter = 0; + + GenerateDirectionInfos( sSectorX, sSectorY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sSectorX, sSectorY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + for( ubIndex = 0; ubIndex < ubDirNumber; ubIndex++ ) + pSectors[ ubCounter++ ] = pusMoveDir[ ubIndex ][ 0 ]; + + return ubCounter; +} + +UINT8 CountAllMilitiaInFiveSectors(INT16 sMapX, INT16 sMapY) +{ + UINT8 ubResult = 0; + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber, ubIndex; + + ubResult = CountAllMilitiaInSector( sMapX, sMapY ); + + if( !gGameExternalOptions.gfAllowReinforcments ) + return ubResult; + + GenerateDirectionInfos( sMapX, sMapY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + for( ubIndex = 0; ubIndex < ubDirNumber; ubIndex++ ) + ubResult += CountAllMilitiaInSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ); + + + return ubResult; +} + +UINT8 MilitiaInFiveSectorsOfRank( INT16 sMapX, INT16 sMapY, UINT8 ubRank ) +{ + UINT8 ubResult = 0; + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber, ubIndex; + + ubResult = MilitiaInSectorOfRank( sMapX, sMapY, ubRank ); + + if( !gGameExternalOptions.gfAllowReinforcments ) + return ubResult; + + GenerateDirectionInfos( sMapX, sMapY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + for( ubIndex = 0; ubIndex < ubDirNumber; ubIndex++ ) + ubResult += MilitiaInSectorOfRank( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ), ubRank ); + + + return ubResult; +} + +BOOLEAN ARMoveBestMilitiaManFromAdjacentSector(INT16 sMapX, INT16 sMapY) +{ + UINT8 ubResult = 0; + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber; + UINT8 ubRandom; + + if( !gGameExternalOptions.gfAllowReinforcments ) + return FALSE; + + if( CountAllMilitiaInSector( sMapX, sMapY ) >= MAXIMUM_MILITIA_SQUAD_SIZE || + CountAllMilitiaInFiveSectors( sMapX, sMapY ) - CountAllMilitiaInSector( sMapX, sMapY ) == 0 ) + return FALSE; + + GenerateDirectionInfos( sMapX, sMapY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + ubRandom = Random( ubDirNumber ); + + while( !MoveOneBestMilitiaMan( SECTORX( pusMoveDir[ ubRandom ][ 0 ] ), SECTORY( pusMoveDir[ ubRandom ][ 0 ] ), sMapX, sMapY ) ) + ubRandom = Random( ubDirNumber ); + + return TRUE; +} + +BOOLEAN ARRemoveMilitiaMan( INT16 sMapX, INT16 sMapY, UINT8 ubRank ) +{ + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber, ubRandom; + + if( MilitiaInSectorOfRank( sMapX, sMapY, ubRank ) ) + { + StrategicRemoveMilitiaFromSector( sMapX, sMapY, ubRank, 1 ); + ARMoveBestMilitiaManFromAdjacentSector( sMapX, sMapY ); + return TRUE; + } + + if( !gGameExternalOptions.gfAllowReinforcments ) + return FALSE; + + GenerateDirectionInfos( sMapX, sMapY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + for( ; ; ) + { + ubRandom = Random( ubDirNumber ); + if( MilitiaInSectorOfRank( SECTORX( pusMoveDir[ ubRandom ][ 0 ] ), SECTORY( pusMoveDir[ ubRandom ][ 0 ] ), ubRank ) ) + { + StrategicRemoveMilitiaFromSector( SECTORX( pusMoveDir[ ubRandom ][ 0 ] ), SECTORY( pusMoveDir[ ubRandom ][ 0 ] ), ubRank, 1 ); + return TRUE; + } + } + + + return FALSE; +} + +GROUP* GetEnemyGroupInSector( INT16 sMapX, INT16 sMapY ) +{ + GROUP *curr; + curr = gpGroupList; + while( curr ) + { + if( curr->ubSectorX == sMapX && curr->ubSectorY == sMapY && !curr->fPlayer && curr->ubGroupID ) + return curr; + curr = curr->next; + } + return NULL; +} + + +UINT8 DoReinforcementAsPendingEnemy( INT16 sMapX, INT16 sMapY ) +{ + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber = 0, ubIndex; + GROUP *pGroup; + SECTORINFO *pThisSector, *pSector; + + if( !gGameExternalOptions.gfAllowReinforcments ) + return 255; + + pThisSector = &SectorInfo[ SECTOR( sMapX, sMapY ) ]; + + GenerateDirectionInfos( sMapX, sMapY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + for( ubIndex = 0; ubIndex < ubDirNumber; ubIndex++ ) + if( NumMobileEnemiesInSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ) && GetEnemyGroupInSector( SECTORX( pusMoveDir[ ubIndex][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ) ) + { + pGroup = GetEnemyGroupInSector( SECTORX( pusMoveDir[ ubIndex][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ); + + pGroup->ubPrevX = pGroup->ubSectorX; + pGroup->ubPrevY = pGroup->ubSectorY; + + pGroup->ubSectorX = pGroup->ubNextX = sMapX; + pGroup->ubSectorY = pGroup->ubNextY = sMapY; + + return pusMoveDir[ ubIndex ][ 2 ]; + } + + if( NumEnemiesInFiveSectors( sMapX, sMapY ) - NumEnemiesInSector( sMapX, sMapY ) == 0 ) + { + return 255; + }else + for(;;) + { + ubIndex = Random(ubDirNumber); + if( NumEnemiesInSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ) > gubReinforcementMinEnemyStaticGroupSize ) + { + pSector = &SectorInfo[ pusMoveDir[ ubIndex ][ 0 ] ]; + + if( pSector->ubNumElites ) + { + (pThisSector->ubNumElites)++; + (pSector->ubNumElites)--; + }else if( pSector->ubNumTroops ) + { + (pThisSector->ubNumTroops)++; + (pSector->ubNumTroops)--; + }else if( pSector->ubNumAdmins ) + { + (pThisSector->ubNumAdmins)++; + (pSector->ubNumAdmins)--; + } + + return pusMoveDir[ ubIndex ][ 2 ]; + } + } +} + + +UINT8 NumFreeMilitiaSlots() +{ + UINT8 ubNumFreeSlots = 0; + INT32 i; + SOLDIERTYPE *pSoldier; + //Count the number of free militia slots. It is possible to have multiple groups exceed the maximum. + for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM].bLastID; i++ ) + { + pSoldier = &Menptr[ i ]; + if( !pSoldier->bActive ) + ubNumFreeSlots++; + } + return max( 0 , ubNumFreeSlots - ( 32 - MAXIMUM_MILITIA_SQUAD_SIZE ) ); +} + +UINT8 DoReinforcementAsPendingMilitia( INT16 sMapX, INT16 sMapY, UINT8 *pubRank ) +{ + UINT16 pusMoveDir[4][3]; + UINT8 ubDirNumber = 0, ubIndex; + SECTORINFO *pSector;//*pThisSector, + + if( !gGameExternalOptions.gfAllowReinforcments ) + return 255; + +// pThisSector = &SectorInfo[ SECTOR( sMapX, sMapY ) ]; + + GenerateDirectionInfos( sMapX, sMapY, &ubDirNumber, pusMoveDir, + ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ? TRUE : FALSE ), TRUE, IS_ONLY_IN_CITIES ); + + if( CountAllMilitiaInFiveSectors( sMapX, sMapY ) - CountAllMilitiaInSector( sMapX, sMapY ) == 0 ) + { + *pubRank = 255; + return 255; + }else + for(;;) + { + ubIndex = Random(ubDirNumber); + if( CountAllMilitiaInSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ) ) ) + { + pSector = &SectorInfo[ pusMoveDir[ ubIndex ][ 0 ] ]; + + if( pSector->ubNumberOfCivsAtLevel[ ELITE_MILITIA ] ) + { + StrategicAddMilitiaToSector( sMapX, sMapY,ELITE_MILITIA, 1 ); + StrategicRemoveMilitiaFromSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ),ELITE_MILITIA, 1 ); + *pubRank = ELITE_MILITIA; + }else if( pSector->ubNumberOfCivsAtLevel[ REGULAR_MILITIA ] ) + { + StrategicAddMilitiaToSector( sMapX, sMapY,REGULAR_MILITIA, 1 ); + StrategicRemoveMilitiaFromSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ),REGULAR_MILITIA, 1 ); + *pubRank = REGULAR_MILITIA; + }else if( pSector->ubNumberOfCivsAtLevel[ GREEN_MILITIA ] ) + { + StrategicAddMilitiaToSector( sMapX, sMapY,GREEN_MILITIA, 1 ); + StrategicRemoveMilitiaFromSector( SECTORX( pusMoveDir[ ubIndex ][ 0 ] ), SECTORY( pusMoveDir[ ubIndex ][ 0 ] ),GREEN_MILITIA, 1 ); + *pubRank = GREEN_MILITIA; + } + + return pusMoveDir[ ubIndex ][ 2 ]; + } + } + +} + + +void AddPossiblePendingMilitiaToBattle() +{ + + UINT8 ubSlots; + UINT8 ubNumElites, ubNumRegulars, ubNumGreens; + SECTORINFO *pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; + static UINT8 ubPredefinedInsertionCode = 255; + static UINT8 ubPredefinedRank = 255; + + if( !PlayerMercsInSector( gWorldSectorX, gWorldSectorY, 0 ) || !CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY ) + || !NumEnemiesInSector( gWorldSectorX, gWorldSectorY ) ) return; +//MAXIMUM_MILITIA_SQUAD_SIZE - CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY ); + ubSlots = NumFreeMilitiaSlots(); + if( !ubSlots ) + { //no available slots to add militia to. Try again later... + return; + } + + if( ubPredefinedInsertionCode != 255 && ubPredefinedRank != 255 && + CountAllMilitiaInSector( gWorldSectorX, gWorldSectorY ) ) + { + ubNumElites = ubNumRegulars = ubNumGreens = 0; + + // while( ubSlots ) + switch( ubPredefinedRank ) + { + case ELITE_MILITIA: + ubSlots--; + ubNumElites++; + break; + case REGULAR_MILITIA: + ubSlots--; + ubNumRegulars++; + break; + case GREEN_MILITIA: + ubSlots--; + ubNumGreens++; + break; + default: + ubSlots--; + } + + if( ubNumGreens || ubNumRegulars || ubNumElites ) + AddSoldierInitListMilitiaOnEdge( ubPredefinedInsertionCode, ubNumGreens, ubNumRegulars, ubNumElites ); + } + + ubPredefinedInsertionCode = 255; + ubPredefinedRank = 255; + + if( ubSlots ) + { //After going through the process, we have finished with some free slots and no more enemies to add. + //So, we can turn off the flag, as this check is no longer needed. + ubPredefinedInsertionCode = DoReinforcementAsPendingMilitia( gWorldSectorX, gWorldSectorY, &ubPredefinedRank ); + + if( ubPredefinedInsertionCode != 255 ) + AddPossiblePendingMilitiaToBattle(); + } + +} \ No newline at end of file diff --git a/Strategic/Reinforcement.h b/Strategic/Reinforcement.h new file mode 100644 index 000000000..cf63fe6f3 --- /dev/null +++ b/Strategic/Reinforcement.h @@ -0,0 +1,17 @@ +#ifndef __REINFORCEMENT_H__ +#define __REINFORCEMENT_H__ + +//For Autoresolve (mostly) +void GetNumberOfEnemiesInFiveSectors( INT16 sSectorX, INT16 sSectorY, UINT8 *pubNumAdmins, UINT8 *pubNumTroops, UINT8 *pubNumElites ); +BOOLEAN IsGroupInARightSectorToReinforce( GROUP *pGroup, INT16 sSectorX, INT16 sSectorY ); +UINT8 GetAdjacentSectors( UINT8 pSectors[4], INT16 sSectorX, INT16 sSectorY ); +UINT8 CountAllMilitiaInFiveSectors(INT16 sMapX, INT16 sMapY); +UINT8 MilitiaInFiveSectorsOfRank(INT16 sMapX, INT16 sMapY, UINT8 ubRank); +BOOLEAN ARRemoveMilitiaMan( INT16 sMapX, INT16 sMapY, UINT8 ubRank ); +UINT8 NumEnemiesInFiveSectors( INT16 sMapX, INT16 sMapY ); + +//For Tactical +UINT8 DoReinforcementAsPendingEnemy( INT16 sMapX, INT16 sMapY ); +void AddPossiblePendingMilitiaToBattle(); + +#endif \ No newline at end of file