From 923327ca14770fe8bd90fcd98cfd8322d307ef9a Mon Sep 17 00:00:00 2001 From: Wanne Date: Wed, 30 Oct 2013 19:48:45 +0000 Subject: [PATCH] Mobile Miltia improvements (by Moa) - ja2_options.ini: MOBILE_MILITIA_MAX_ACTIVE_MODE, MOBILE_MILITIA_MAX_ACTIVE_MODIFIER - see: http://www.bears-pit.com/board/ubbthreads.php/topics/325103/2.html git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@6544 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 16 +- GameSettings.h | 8 +- Strategic/Assignments.cpp | 8 + Strategic/MilitiaSquads.cpp | 396 ++++++++++++++++++++++++++++++++++-- Strategic/MilitiaSquads.h | 4 + Strategic/Town Militia.cpp | 10 + Utils/_ChineseText.cpp | 8 + Utils/_DutchText.cpp | 8 + Utils/_EnglishText.cpp | 8 + Utils/_FrenchText.cpp | 8 + Utils/_GermanText.cpp | 8 + Utils/_ItalianText.cpp | 8 + Utils/_PolishText.cpp | 8 + Utils/_RussianText.cpp | 8 + Utils/_TaiwaneseText.cpp | 8 + 15 files changed, 495 insertions(+), 19 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index c7d58330..4a82e7ec 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1881,6 +1881,12 @@ void LoadGameExternalOptions() gGameExternalOptions.gfmusttrainroaming = iniReader.ReadBoolean("Mobile Militia Training Settings","MUST_TRAIN_MOBILE_MILITIA",FALSE); + //Moa: Mode to determine maximum allowed mobile militia for training / deserting. + gGameExternalOptions.gbMobileMilitiaMaxActiveMode = (UINT8) iniReader.ReadInteger("Mobile Militia Training Settings","MOBILE_MILITIA_MAX_ACTIVE_MODE",0,0,255); + + //Moa: modifier for the maximum (make sure this is read after iMaxMilitiaPerSector) + gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier = iniReader.ReadFloat("Mobile Militia Training Settings","MOBILE_MILITIA_MAX_ACTIVE_MODIFIER", 0.5, 0.01, 10.0) * gGameExternalOptions.iMaxMilitiaPerSector; + gGameExternalOptions.guiCreateEachNHours = iniReader.ReadInteger("Mobile Militia Training Settings","CREATE_MOBILE_MILITIA_SQUAD_EACH_N_HOURS",24, 1, 96); // HEADROCK HAM 3.3: New militia feature, Minimum Leadership required to train Roaming Militia @@ -1921,7 +1927,15 @@ void LoadGameExternalOptions() // HEADROCK HAM 3.3: If enabled, Roaming Militia can automatically reinforce city garrisons. gGameExternalOptions.fAllowMobileReinforceSAM = iniReader.ReadBoolean("Mobile Militia Movement Settings","ALLOW_MOBILE_MILITIA_REINFORCE_SAM_GARRISONS", FALSE); - + // Moa: If enabled militia will follow player (default HAM behavier), set to FALSE if they should do what they want. + gGameExternalOptions.gfAllowMilitiaFollowPlayer = iniReader.ReadBoolean("Mobile Militia Movement Settings","ALLOW_MILITIA_FOLLOW_PLAYER", TRUE); + + // Moa: If enabled militia will spread (default HAM behavier) or set to FALSE to fill up the destination group to maximum. + gGameExternalOptions.gfAllowMilitiaSpread = iniReader.ReadBoolean("Mobile Militia Movement Settings","ALLOW_MILITIA_SPREAD", TRUE); + + // Moa: If enabled militia will spread when following or set to FALSE to fill up the destination group to maximum. + gGameExternalOptions.gfAllowMilitiaSpreadWhenFollowing = iniReader.ReadBoolean("Mobile Militia Movement Settings","ALLOW_MILITIA_SPREAD_WHEN_FOLLOWING", FALSE); + // SANDRO - added several bonuses for militia //################# Militia Strength Settings ################## diff --git a/GameSettings.h b/GameSettings.h index fa11053a..68546fbc 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -453,6 +453,12 @@ typedef struct BOOLEAN gfAllowMilitiaGroups; BOOLEAN gfmusttrainroaming; BOOLEAN gflimitedRoaming; + BOOLEAN gfAllowMilitiaFollowPlayer; //Moa: TRUE try to follow, FALSE do what they want + BOOLEAN gfAllowMilitiaSpreadWhenFollowing; //Moa: TRUE spread normal, FALSE dont spread when following + BOOLEAN gfAllowMilitiaSpread; //Moa: TRUE spread normal, FALSE fill up to max + UINT8 gbMobileMilitiaMaxActiveMode; //Moa: Used to check if the maximum of mobile militia is reached (for training/deserting). THIS IS A MODE + FLOAT gfpMobileMilitiaMaxActiveModifier; //Moa: const multiplier for the mode used (precalculated with MOBILE_MILITIA_MAX_ACTIVE_MODIFIER * MAX_MILITIA_PER_SECTOR). + BOOLEAN gfAllowReinforcements; BOOLEAN gfAllowReinforcementsOnlyInCity; UINT32 guiBaseQueenPoolIncrement; @@ -832,7 +838,7 @@ typedef struct // HEADROCK HAM B1: Allows reducing the chance of scoring a hit fractionally, if CTH = Minimum CTH UINT16 usMinimumCTHDivisor; - // HEADROCK HAM B1: Allows Restricted Roaming Militia to move through previously visited sectors, even if restricted. + // HEADROCK HAM B1: Allows Restricted Roaming Militia to move through previously visited sectors. BOOLEAN fUnrestrictVisited; // HEADROCK HAM B1: Allows the capture of towns to dynamically alter roaming restrictions diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index 2e02f4e2..5a767361 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -15198,6 +15198,10 @@ BOOLEAN CanCharacterTrainMobileMilitia( SOLDIERTYPE *pSoldier ) // return( FALSE ); //} + + if ( 100 <= GetMobileMilitiaQuota( FALSE ) ) + return ( FALSE ); + // If we've reached this, then all is well. return( TRUE ); @@ -15217,6 +15221,10 @@ BOOLEAN CanCharacterTrainMilitiaWithErrorReport( SOLDIERTYPE *pSoldier ) return( FALSE ); } + if ( 100 <= GetMobileMilitiaQuota( TRUE ) ) + return ( FALSE ); + + /////////////////////////////// // Test for required Leadership diff --git a/Strategic/MilitiaSquads.cpp b/Strategic/MilitiaSquads.cpp index c82afa4a..e5aee8d4 100644 --- a/Strategic/MilitiaSquads.cpp +++ b/Strategic/MilitiaSquads.cpp @@ -25,6 +25,8 @@ #include "Auto Resolve.h" #include "Vehicles.h" #include "Tactical Save.h" + #include "Campaign.h" + #include "message.h" #endif #include "connect.h" @@ -514,14 +516,15 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL ubNumEnemiesNearTarget > 0 ) { // Both sectors threatened. - - if (gGameExternalOptions.gfAllowReinforcements && !gGameExternalOptions.gfAllowReinforcementsOnlyInCity) + //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 + 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 @@ -531,20 +534,7 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL ubChanceToSpreadOut = __min( 100, (ubNumEnemiesNearOrigin * 100) / usTotalNumEnemies ); } } - else if (NumEnemiesInFiveSectors( sMapX, sMapY ) > 0 && - NumEnemiesInFiveSectors( sTMapX, sTMapY ) > 0 ) - { - // Both sectors threatened. Militia will spread out between them, to maximize defensive capability. - ubChanceToSpreadOut = 100; - } - else if (NumEnemiesInFiveSectors( sMapX, sMapY ) > 0 && - NumEnemiesInFiveSectors( sTMapX, sTMapY ) == 0 ) - { - // Source sector is threatened, while destination is not. The group does not - // move at all, as it is already in a good position to defend. - return; - } else { /////////////////////////////////////////////////////// @@ -575,6 +565,19 @@ void MoveMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, BOOL { ubChanceToSpreadOut = 0; } + + + if (!gGameExternalOptions.gfAllowMilitiaSpread ) + { + // Spreading is not allowed by user. Let them fill up the target sector. + ubChanceToSpreadOut = 0; + } + else if ( !gGameExternalOptions.gfAllowMilitiaSpreadWhenFollowing && PlayerMercsInSector_MSE( (UINT8) sTMapX, (UINT8) sTMapY, FALSE ) ) + { + // There is a Player in targetsector or about to arrive, but spreading is not allowed by user. + // Let them fill up the target sector + ubChanceToSpreadOut = 0; + } } } @@ -1488,7 +1491,7 @@ void UpdateMilitiaSquads(INT16 sMapX, INT16 sMapY ) // moving squad, if it is not a SAM site if( ( fSourceCityAllowsRoaming ) && (!IsThisSectorASAMSector( sMapX, sMapY, 0 )) ) { - if( !PlayerMercsInSector_MSE( (UINT8)sMapX, (UINT8)sMapY, FALSE ) ) // and there's no player's mercs in the sector + if( !gGameExternalOptions.gfAllowMilitiaFollowPlayer || !PlayerMercsInSector_MSE( (UINT8)sMapX, (UINT8)sMapY, FALSE ) ) // and there's no player's mercs in the sector, or they are not forced to follow { if( GetWorldHour() % 2 )return; @@ -1569,6 +1572,9 @@ void UpdateMilitiaSquads(INT16 sMapX, INT16 sMapY ) pEnemyGroup->ubNextX = targetX; pEnemyGroup->ubNextY = targetY; */ + //Moa: handle deserters before moving in hostile territory + MobileMilitiaDeserters( targetX, targetY, TRUE, TRUE ); + gfMSBattle = TRUE; // GroupArrivedAtSector( pEnemyGroup->ubGroupID , TRUE, FALSE ); @@ -2251,3 +2257,359 @@ void InitManualMobileRestrictions() gubManualRestrictMilitia[x] = MANUAL_MOBILE_NO_RESTRICTION; } } + + +extern BOOLEAN IsMercOnTeam(UINT8 ubMercID); +// @brief Calculates the contingent for mobile militia. +// Calculates current active mobile militia and compares to variable maximum limit which can be altered by external data. +// @param printMessage Set to TRUE to get a screenmessage telling the player which maximum is reached. No message is generated when the maximum is not yet reached (<=100). +// @return The current percentage (0% - 255%). If the feature is deactivated allways 0 is returned. +UINT8 GetMobileMilitiaQuota( BOOLEAN printMessage ) +{ + /////////////////////// + // Check for maximum mobiles allowed + // + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode > 1 ) + { + //what we need to know + UINT16 iActiveMobiles = 0, iCurrentMax = 0; + UINT16 iTownSectorsUnderPlayerControl = 0, iTownSectorsLiberatedAtLeastOnce = 0; + UINT8 iTownsUnderPlayerControl = 0, iCurrentProgress = 0, iMaxProgress = 0, iNumRebelsInPlayerTeam = 0; + + //temp values for sectors we gonna check + INT8 iCurrT_ID, test; + UINT16 iStrategicMapID, iSectorInfoID; + SECTORINFO *pSectorInfo; + + // start gathering data + if (gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 8U) // check for TownsLiberated is active + for ( iCurrT_ID = 0; iCurrT_ID < NUM_TOWNS; iCurrT_ID++) // for each town + { + if ( GetTownSectorsUnderControl( iCurrT_ID ) == GetTownSectorSize( iCurrT_ID ) ) + iTownsUnderPlayerControl++; // remember player has that town under control + } + + for ( iSectorInfoID = 0; iSectorInfoID < 255; iSectorInfoID++) // for each sector ... + { + iStrategicMapID = SECTOR_INFO_TO_STRATEGIC_INDEX(iSectorInfoID); + pSectorInfo = &( SectorInfo[ iSectorInfoID ] ); + iCurrT_ID = StrategicMap[ iStrategicMapID ].bNameId; + + if ( iCurrT_ID != BLANK_SECTOR ) + { + // we have found a town (no sam site) + + if ( StrategicMap[ iStrategicMapID ].fEnemyControlled == FALSE ) + iTownSectorsUnderPlayerControl++; // remember player currently controls that town sector + + if ( pSectorInfo->fSurfaceWasEverPlayerControlled == TRUE ) + iTownSectorsLiberatedAtLeastOnce++; // remember player had allready liberated that town sector once + + if ( MilitiaTrainingAllowedInTown( iCurrT_ID ) == FALSE ) // considered as mobile only if in that town training is taboo (Tixa, Omerta..) + iActiveMobiles += CountMilitia( pSectorInfo ); + //for ( UINT8 militiaLevel = 0; militiaLevel < MAX_MILITIA_LEVELS; militiaLevel++ ) + // iActiveMobiles += pSectorInfo->ubNumberOfCivsAtLevel[ militiaLevel ]; // remember number of roaming militia in that town sector (green, regular, elite, ..) + } + + //else if ( StrategicMap[ iStrategicMapID ].bSAMCondition > 0 ) //faster then below, but not safe + else if ( MilitiaTrainingAllowedInSector( SECTORX(iSectorInfoID), SECTORY(iSectorInfoID), 0 ) == FALSE ) + { + // we are in wilderness + + //for ( UINT8 militiaLevel = 0; militiaLevel < MAX_MILITIA_LEVELS; militiaLevel++ ) + // iActiveMobiles += pSectorInfo->ubNumberOfCivsAtLevel[ militiaLevel ]; // remember number of roaming militia in that sector (green, regular, elite, ..) + iActiveMobiles += CountMilitia( pSectorInfo ); + } + } + + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 64U ) // check for rebels in player team is active + { + if ( gProfilesRPC != NULL ) + { + UINT16 rebelStructSize = sizeof ( gProfilesRPC ); + for (UINT16 rebelPC = 0; rebelPC < rebelStructSize; rebelPC++) + { + if ( IsMercOnTeam ( gProfilesRPC[ rebelPC ].ProfilId ) ) iNumRebelsInPlayerTeam++; + } + } + //if ( IsMercOnTeam ( IRA ) ) iNumRebelsInPlayerTeam++; + //if ( IsMercOnTeam ( DIMITRI ) ) iNumRebelsInPlayerTeam++; + //if ( IsMercOnTeam ( CARLOS ) ) iNumRebelsInPlayerTeam++; + //if ( IsMercOnTeam ( MIGUEL ) ) iNumRebelsInPlayerTeam++; + //if ( IsMercOnTeam ( DYNAMO ) ) iNumRebelsInPlayerTeam++; + //if ( IsMercOnTeam ( ENRICO ) ) iNumRebelsInPlayerTeam++; + } + + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 16U ) // check for current progress is active + iCurrentProgress = CurrentPlayerProgressPercentage(); + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 32U ) // check for max progress is active + iMaxProgress = HighestPlayerProgressPercentage(); + //end gathering data + ///////////////////// + // calc maximum (note: the modifier was initilized allready with MOBILE_MILITIA_MAX_ACTIVE_MODIFIER * MAX_MILITIA_PER_SECTOR) + UINT8 iModeMatch = 0; //takes the mode which matches the maximum used in order to build a message later on (0=no match 1..6) + UINT16 iCalc; + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 128U ) // we are using the highest value + { + iCurrentMax = 0; + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 2U ) // check for town sectors under player control is active + { + iCalc = (UINT16) ( iTownSectorsUnderPlayerControl * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier ); + if ( iCurrentMax <= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 1; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 4U ) // check for town sectors liberated at least once is active + { + iCalc = (UINT16) (iTownSectorsLiberatedAtLeastOnce * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier ); + if ( iCurrentMax <= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 2; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 8U ) + { + iCalc = (UINT16) (iTownsUnderPlayerControl * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier * 3 ); + if ( iCurrentMax <= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 3; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 16U ) + { + iCalc = (UINT16) (iCurrentProgress * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier * 20 / 100 ); + if ( iCurrentMax <= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 4; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 32U ) + { + iCalc = (UINT16) (iMaxProgress * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier * 20 / 100 ); + if ( iCurrentMax <= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 5; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 64U ) + { + iCalc = (UINT16) (iNumRebelsInPlayerTeam * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier * 3 ); + if ( iCurrentMax <= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 6; + } + } + } + else // we are using lowest value + { + iCurrentMax = (UINT16) -1; + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 2U ) + { + iCalc = (UINT16) ( iTownSectorsUnderPlayerControl * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier ); + if ( iCurrentMax >= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 1; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 4U ) + { + iCalc = (UINT16) (iTownSectorsLiberatedAtLeastOnce * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier ); + if ( iCurrentMax >= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 2; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 8U ) + { + iCalc = (UINT16) (iTownsUnderPlayerControl * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier * 3 ); + if ( iCurrentMax >= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 3; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 16U ) + { + iCalc = (UINT16) (iCurrentProgress * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier * 20 / 100 ); + if ( iCurrentMax >= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 4; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 32U ) + { + iCalc = (UINT16) (iMaxProgress * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier * 20 / 100 ); + if ( iCurrentMax >= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 5; + } + } + if ( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 64U ) + { + iCalc = (UINT16) (iNumRebelsInPlayerTeam * gGameExternalOptions.gfpMobileMilitiaMaxActiveModifier * 3 ); + if ( iCurrentMax >= iCalc ) + { + iCurrentMax = iCalc; + iModeMatch = 6; + } + } + } + ///////////////////// + //Notify player + if ( iActiveMobiles >= iCurrentMax ) + { + if ( printMessage && iModeMatch > 0 )//fail safe to avoid in-buffer == out-buffer + { + // providing feedback why player cant train more. + CHAR16 sString[200]; + swprintf( sString, pMilitiaConfirmStrings[15], iActiveMobiles, iCurrentMax, pMilitiaConfirmStrings[15 + iModeMatch] );//We reached maximum (..active../..max..)..make this..to do sth + //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, sString ); + DoScreenIndependantMessageBox(sString, MSG_BOX_FLAG_OK, NULL); + } + } + return ( iCurrentMax == 0 ? 255 : (iActiveMobiles * 100 / iCurrentMax) ); + //return ( iCurrentMax == 0 ? 255 : max( 255, (iActiveMobiles * 100 / iCurrentMax) ) ); + + }//end check for maximum mobile militia allowed + + return 0; +} + + +//@brief Checks for militia quota and removes randomly some of militia if there are too many. +// Deserting works only if the feature is enabled and the sector is threatened by an adjacent enemy presence. +// The chance to generate deserters raises with the quota. +//@param sMapX, sMapY Strategic sector coordinate which will be checked. +//@param fDeleteEquipment If the militia has any equipment it will be deleted when he is a deserter. +//@param fPrintMessage Notifies player that some militia have deserted. +//@auth Moa +void MobileMilitiaDeserters(INT16 sMapX, INT16 sMapY, BOOLEAN fDeleteEquip, BOOLEAN fPrintMessage) +{ + // if more then allowed mobiles are active some of them will desert (green and only some regulars, elites will never desert) + UINT8 desertersGreen = 0, desertersRegular = 0; + BOOLEAN enemiesNear = FALSE; + + // feature not activated, return + if (!( gGameExternalOptions.gbMobileMilitiaMaxActiveMode & 1U ) ) + return; + + // Training allowed here therefore not a sector for mobile militia, return + if ( gfMilitiaAllowedInTown [ GetTownIdForSector( sMapX, sMapY ) ] ) + return; + + // This is a SAM site therefore no mobile militia here, return + if (IsThisSectorASAMSector( sMapX, sMapY, 0 )) + return; + + /////////////////////// + // check enemy presence to decide if mobiles are threatened and some will desert. + // Note: cant use NumEnemiesInFiveSectors() as it does not work for omerta or when reinforcements are deactivated, also it uses generateDirectionInfo(), which is used for militia movement. + UINT8 eAdmins = 0, eTroops = 0, eElites = 0; + + //enemies in current sector + GetNumberOfEnemiesInSector( sMapX, sMapY, &eAdmins, &eTroops, &eElites ); + if ( (eAdmins > 0) || (eTroops > 0) ||(eElites > 0) ) + enemiesNear = TRUE; + + if ( ( sMapX > MINIMUM_VALID_X_COORDINATE ) && !enemiesNear ) + { + //left side + GetNumberOfEnemiesInSector( sMapX - 1, sMapY, &eAdmins, &eTroops, &eElites ); + if ( (eAdmins > 0) || (eTroops > 0) ||(eElites > 0) ) + enemiesNear = TRUE; + } + if ( ( sMapX < MAXIMUM_VALID_X_COORDINATE ) && !enemiesNear) + { + //right side + GetNumberOfEnemiesInSector( sMapX + 1, sMapY, &eAdmins, &eTroops, &eElites ); + if ( (eAdmins > 0) || (eTroops > 0) ||(eElites > 0) ) + enemiesNear = TRUE; + } + if ( ( sMapY > MINIMUM_VALID_Y_COORDINATE ) && !enemiesNear ) + { + //top side + GetNumberOfEnemiesInSector( sMapX, sMapY - 1, &eAdmins, &eTroops, &eElites ); + if ( (eAdmins > 0) || (eTroops > 0) ||(eElites > 0) ) + enemiesNear = TRUE; + } + if ( ( sMapY < MAXIMUM_VALID_Y_COORDINATE ) && !enemiesNear) + { + //bottom side + GetNumberOfEnemiesInSector( sMapX, sMapY + 1, &eAdmins, &eTroops, &eElites ); + if ( (eAdmins > 0) || (eTroops > 0) ||(eElites > 0) ) + enemiesNear = TRUE; + } + + ///////////////////////// + //calc number of deserters + if ( enemiesNear ) //if deserting feature is active, this is a sector where mobile militia is allowed and there is a threat at target + { + UINT8 quota = GetMobileMilitiaQuota( FALSE ); + + if (quota > 100) //more active then allowed + { + UINT8 militiaGreen = SectorInfo[ SECTOR( sMapX, sMapY) ].ubNumberOfCivsAtLevel[ GREEN_MILITIA ]; + UINT8 militiaRegular = SectorInfo[ SECTOR( sMapX, sMapY) ].ubNumberOfCivsAtLevel[ REGULAR_MILITIA ]; + + desertersGreen = (quota - 100) * militiaGreen / 100; + desertersGreen = Random( min( militiaGreen, desertersGreen ) );//cant remove more then actually exist in that group + + desertersRegular = (quota - 100) * militiaRegular / 200; + desertersRegular = Random( min ( militiaRegular, desertersRegular ) ); + } + } + + //////////////////////// + //remove militia + if ( desertersRegular + desertersGreen > 0 ) + { + StrategicRemoveMilitiaFromSector( sMapX, sMapY, GREEN_MILITIA, desertersGreen ); + StrategicRemoveMilitiaFromSector( sMapX, sMapY, REGULAR_MILITIA, desertersRegular ); + + /////////////////////////// + //remove equipment + if ( fDeleteEquip && gGameExternalOptions.fMilitiaUseSectorInventory) + { + SOLDIERCREATE_STRUCT trashIt; + UINT8 cnt = desertersGreen; + while ( cnt > 0 ) + { + TakeMilitiaEquipmentfromSector( sMapX, sMapY, 0, &trashIt, SOLDIER_CLASS_GREEN_MILITIA); + cnt--; + } + cnt = desertersRegular; + while ( cnt > 0 ) + { + TakeMilitiaEquipmentfromSector( sMapX, sMapY, 0, &trashIt, SOLDIER_CLASS_REG_MILITIA); + cnt--; + } + } + + /////////////////////////// + //notify player + if ( fPrintMessage ) + { + CHAR16 sSector[16]; + GetShortSectorString( sMapX, sMapY, sSector ); + + //using screen message to have it logged. Cant use msgBox as it prevents showing the autoresolve screen. + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, pMilitiaConfirmStrings[ 22 ], ( desertersRegular + desertersGreen ), sSector ); + } + } + //clean up + if (gfStrategicMilitiaChangesMade) + { + ResetMilitia(); + } +} diff --git a/Strategic/MilitiaSquads.h b/Strategic/MilitiaSquads.h index d7af55ae..c7c69459 100644 --- a/Strategic/MilitiaSquads.h +++ b/Strategic/MilitiaSquads.h @@ -65,4 +65,8 @@ UINT8 ManualMobileMovementAllowed( UINT8 ubSector ); // HEADROCK HAM 4: Initialize all manual restrictions; void InitManualMobileRestrictions(); +//Moa: for mobile militia: militia deserters and maximum allowed +extern void MobileMilitiaDeserters( INT16 sMapX, INT16 sMapY, BOOLEAN fDeleteEquip, BOOLEAN fPrintMessage ); +extern UINT8 GetMobileMilitiaQuota( BOOLEAN printMessage ); + #endif \ No newline at end of file diff --git a/Strategic/Town Militia.cpp b/Strategic/Town Militia.cpp index e6bf9b30..c9910bf1 100644 --- a/Strategic/Town Militia.cpp +++ b/Strategic/Town Militia.cpp @@ -844,6 +844,16 @@ DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Militia3"); gfYesNoPromptIsForContinue = TRUE; + //Moa: prevent continue training when at/above maximum + if ( ubMilitiaType == MOBILE_MILITIA ) + { + if ( 100 <= GetMobileMilitiaQuota( TRUE ) ) + { + MilitiaTrainingRejected( MOBILE_MILITIA ); + return; + } + } + // is there enough loyalty to continue training if( DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( pSoldier ) == FALSE ) { diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index dfb151ea..e80a64f6 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -5099,6 +5099,14 @@ STR16 pMilitiaConfirmStrings[] = L"继续在%s (%s %d)训练巡逻民兵吗?", // L"Continue training mobile militia in %s (%s %d)?", // HEADROCK HAM 3.6: Mobile Militia L"在%d地区训练巡逻民兵将花费$%d。%s", // L"Training mobile militia in %d sectors will cost $ %d. %s", // HEADROCK HAM 3.6: Mobile Militia L"训练一队民兵要花费$", // L"Training a squad of mobile militia will cost $", // HEADROCK HAM 3.6: Mobile Militia + L"You cannot train more mobile militia because the maximum is reached (%d/%d). You have to %s first in order to train more.", // TODO.Translate + L"liberate more town sectors", // TODO.Translate + L"liberate new town sectors", // TODO.Translate + L"liberate more towns", // TODO.Translate + L"regain your lost progress", // TODO.Translate + L"progress further", // TODO.Translate + L"recruit more rebels", // TODO.Translate + L"The militia team leader has to inform you that %d fellow have deserted facing enemy in sector %s.", // TODO.Translate }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index e1c1bbe7..aa1a4e1c 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -5101,6 +5101,14 @@ STR16 pMilitiaConfirmStrings[] = L"Continue training mobile militia in %s (%s %d)?", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate L"Training mobile militia in %d sectors will cost $ %d. %s", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate L"Training a squad of mobile militia will cost $", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate + L"You cannot train more mobile militia because the maximum is reached (%d/%d). You have to %s first in order to train more.", // TODO.Translate + L"liberate more town sectors", // TODO.Translate + L"liberate new town sectors", // TODO.Translate + L"liberate more towns", // TODO.Translate + L"regain your lost progress", // TODO.Translate + L"progress further", // TODO.Translate + L"recruit more rebels", // TODO.Translate + L"The militia team leader has to inform you that %d fellow have deserted facing enemy in sector %s.", // TODO.Translate }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index e1591163..0e70370e 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -5099,6 +5099,14 @@ STR16 pMilitiaConfirmStrings[] = L"Continue training mobile militia in %s (%s %d)?", // HEADROCK HAM 3.6: Mobile Militia L"Training mobile militia in %d sectors will cost $ %d. %s", // HEADROCK HAM 3.6: Mobile Militia L"Training a squad of mobile militia will cost $", // HEADROCK HAM 3.6: Mobile Militia + L"You cannot train more mobile militia because the maximum is reached (%d/%d). You have to %s first in order to train more.", //Moa: maximum number mobile militia (15) + L"liberate more town sectors", + L"liberate new town sectors", + L"liberate more towns", + L"regain your lost progress", + L"progress further", + L"recruit more rebels", + L"The militia team leader has to inform you that %d fellow have deserted facing enemy in sector %s.", //Moa: mobile deserted (22) }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 2d887430..fa1d6dbb 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -5106,6 +5106,14 @@ STR16 pMilitiaConfirmStrings[] = L"Voulez-vous poursuivre l'entraînement de la milice mobile à %s (%s %d)?", // HEADROCK HAM 3.6: Mobile Militia L"L'entraînement des milices mobiles dans %d secteurs vous coûtera %d $. %s", // HEADROCK HAM 3.6: Mobile Militia L"L'entraînement de la milice mobile vous coûtera $", // HEADROCK HAM 3.6: Mobile Militia + L"You cannot train more mobile militia because the maximum is reached (%d/%d). You have to %s first in order to train more.", // TODO.Translate + L"liberate more town sectors", // TODO.Translate + L"liberate new town sectors", // TODO.Translate + L"liberate more towns", // TODO.Translate + L"regain your lost progress", // TODO.Translate + L"progress further", // TODO.Translate + L"recruit more rebels", // TODO.Translate + L"The militia team leader has to inform you that %d fellow have deserted facing enemy in sector %s.", // TODO.Translate }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 039b1460..6809ed2a 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -4946,6 +4946,14 @@ STR16 pMilitiaConfirmStrings[] = L"Mobile Milizen in %s (%s %d) weitertrainieren?", // HEADROCK HAM 3.6: Mobile Militia L"Mobile Milizen in %d Sektoren zu trainieren kostet $ %d. %s", // HEADROCK HAM 3.6: Mobile Militia L"Eine mobile Milizeinheit für diese Stadt zu trainieren kostet $", // HEADROCK HAM 3.6: Mobile Militia + L"Sie können keine weiteren mobilen Milizen ausbilden, da das Maximum erreicht wurde (%d/%d). Sie müssen zunächst %s um weitere zu trainieren.", + L"weitere Stadtteile befreien", + L"neue Stadtteile befreien", + L"mehr Städte erobern", + L"den verlorenen Fortschritt wieder aufholen", + L"weiter fortschreiten", + L"mehr Rebellen rekrutieren", + L"Der Miliztruppführer muss Sie leider darüber informieren, dass %d Mitstreiter im Angesicht des Feindes desertierten im Sektor %s.", }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 09f65658..530d9683 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -5084,6 +5084,14 @@ STR16 pMilitiaConfirmStrings[] = L"Continue training mobile militia in %s (%s %d)?", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate L"Training mobile militia in %d sectors will cost $ %d. %s", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate L"Training a squad of mobile militia will cost $", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate + L"You cannot train more mobile militia because the maximum is reached (%d/%d). You have to %s first in order to train more.", // TODO.Translate + L"liberate more town sectors", // TODO.Translate + L"liberate new town sectors", // TODO.Translate + L"liberate more towns", // TODO.Translate + L"regain your lost progress", // TODO.Translate + L"progress further", // TODO.Translate + L"recruit more rebels", // TODO.Translate + L"The militia team leader has to inform you that %d fellow have deserted facing enemy in sector %s.", // TODO.Translate }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 60a3cf5b..ad7f5af8 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -5108,6 +5108,14 @@ STR16 pMilitiaConfirmStrings[] = L"Kontynuuj trening ruchomej samoobrony w %s (%s %d)?", // HEADROCK HAM 3.6: Mobile Militia L"Trenowanie ruchomej samoobrony w %d sektorach będzie kosztować %d$. %s", // HEADROCK HAM 3.6: Mobile Militia L"Trenowanie ruchomej samoobrony będzie kosztować $", // HEADROCK HAM 3.6: Mobile Militia + L"You cannot train more mobile militia because the maximum is reached (%d/%d). You have to %s first in order to train more.", // TODO.Translate + L"liberate more town sectors", // TODO.Translate + L"liberate new town sectors", // TODO.Translate + L"liberate more towns", // TODO.Translate + L"regain your lost progress", // TODO.Translate + L"progress further", // TODO.Translate + L"recruit more rebels", // TODO.Translate + L"The militia team leader has to inform you that %d fellow have deserted facing enemy in sector %s.", // TODO.Translate }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 75135d3f..364d2f37 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -5098,6 +5098,14 @@ STR16 pMilitiaConfirmStrings[] = L"Продолжить тренировку мобильного подразделения в %s (%s %d)?", L"Тренировка мобильного подразделения в секторе %d обойдётся в $ %d. %s", L"Тренировка мобильного подразделения ополченцев обойдётся в $", + L"You cannot train more mobile militia because the maximum is reached (%d/%d). You have to %s first in order to train more.", // TODO.Translate + L"liberate more town sectors", // TODO.Translate + L"liberate new town sectors", // TODO.Translate + L"liberate more towns", // TODO.Translate + L"regain your lost progress", // TODO.Translate + L"progress further", // TODO.Translate + L"recruit more rebels", // TODO.Translate + L"The militia team leader has to inform you that %d fellow have deserted facing enemy in sector %s.", // TODO.Translate }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel diff --git a/Utils/_TaiwaneseText.cpp b/Utils/_TaiwaneseText.cpp index a18de86f..17f0234a 100644 --- a/Utils/_TaiwaneseText.cpp +++ b/Utils/_TaiwaneseText.cpp @@ -5102,6 +5102,14 @@ STR16 pMilitiaConfirmStrings[] = L"Continue training mobile militia in %s (%s %d)?", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate L"Training mobile militia in %d sectors will cost $ %d. %s", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate L"Training a squad of mobile militia will cost $", // HEADROCK HAM 3.6: Mobile Militia // TODO.Translate + L"You cannot train more mobile militia because the maximum is reached (%d/%d). You have to %s first in order to train more.", // TODO.Translate + L"liberate more town sectors", // TODO.Translate + L"liberate new town sectors", // TODO.Translate + L"liberate more towns", // TODO.Translate + L"regain your lost progress", // TODO.Translate + L"progress further", // TODO.Translate + L"recruit more rebels", // TODO.Translate + L"The militia team leader has to inform you that %d fellow have deserted facing enemy in sector %s.", // TODO.Translate }; //Strings used in the popup box when withdrawing, or depositing money from the $ sign at the bottom of the single merc panel