From c7c085b04f964676ed248ab333c6253cf97ad18d Mon Sep 17 00:00:00 2001 From: Flugente Date: Sun, 22 Feb 2015 18:52:41 +0000 Subject: [PATCH] New Feature: Militia Volunteer Pool limits the number of militia we can train. Turned off by default. GameDir >= r2210 is recommended. git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@7746 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 5 ++ GameSettings.h | 6 +++ Laptop/LaptopSave.h | 5 +- Strategic/Assignments.cpp | 14 +++++ Strategic/Map Screen Interface Map.cpp | 23 ++++++++- Strategic/MilitiaSquads.cpp | 8 ++- Strategic/Player Command.cpp | 11 +++- Strategic/Town Militia.cpp | 71 +++++++++++++++++++++++++- Strategic/Town Militia.h | 12 +++++ Utils/_ChineseText.cpp | 2 + Utils/_DutchText.cpp | 2 + Utils/_EnglishText.cpp | 2 + Utils/_FrenchText.cpp | 2 + Utils/_GermanText.cpp | 2 + Utils/_ItalianText.cpp | 2 + Utils/_PolishText.cpp | 2 + Utils/_RussianText.cpp | 2 + 17 files changed, 165 insertions(+), 6 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 2c5f1b3ed..84ab60190 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1994,6 +1994,11 @@ void LoadGameExternalOptions() // HEADROCK HAM 3: Define effect of "TEACHER" trait in increasing effective leadership, for purposes of eligibility for training militia. This is a percentage value. HAM Default would be 200 = double effective leadership for each TEACHING level. gGameExternalOptions.usTeacherTraitEffectOnLeadership = iniReader.ReadInteger("Militia Training Settings","TEACHER_TRAIT_EFFECT_ON_LEADERSHIP", 100, 1, 10000); + //################# Militia Volunteer Pool Settings ################### + gGameExternalOptions.fMilitiaVolunteerPool = iniReader.ReadBoolean( "Militia Volunteer Pool Settings", "MILITIA_VOLUNTEER_POOL", FALSE ); + gGameExternalOptions.dMilitiaVolunteerGainFactorHourly = iniReader.ReadFloat( "Militia Volunteer Pool Settings", "MILITIA_VOLUNTEER_POOL_GAINFACTOR_HOURLY", 0.001f, 0.0f, 1.0f ); + gGameExternalOptions.dMilitiaVolunteerGainFactorLiberation = iniReader.ReadFloat( "Militia Volunteer Pool Settings", "MILITIA_VOLUNTEER_POOL_GAINFACTOR_LIBERATION", 0.2f, 0.0f, 1.0f ); + gGameExternalOptions.dMilitiaVolunteerMultiplierFarm = iniReader.ReadFloat( "Militia Volunteer Pool Settings", "MILITIA_VOLUNTEER_POOL_MULTIPLIER_FARM", 0.05f, 0.0f, 1.0f ); //################# Mobile Militia Training Settings ################## diff --git a/GameSettings.h b/GameSettings.h index e848d87be..2bc052957 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -287,6 +287,12 @@ typedef struct INT32 iVeteranCostModifier; INT32 iMinLoyaltyToTrain; + // Flugente: militia volunteer pool + BOOLEAN fMilitiaVolunteerPool; + FLOAT dMilitiaVolunteerGainFactorHourly; + FLOAT dMilitiaVolunteerGainFactorLiberation; + FLOAT dMilitiaVolunteerMultiplierFarm; + INT32 iMaxEnemyGroupSize; BOOLEAN fMercDayOne; diff --git a/Laptop/LaptopSave.h b/Laptop/LaptopSave.h index fa4ec109e..8c3526e47 100644 --- a/Laptop/LaptopSave.h +++ b/Laptop/LaptopSave.h @@ -166,7 +166,10 @@ typedef struct BOOLEAN bJohnEscorted;// and if they were at all UINT8 ubJohnPossibleMissedFlights;// and how many flights he already missed - UINT8 bPadding[ 77 ]; // Flugente: 86->77 + // Flugente: militia volunteer pool. No relation to the laptop whatsoever, but I'm not creating a new struct for one variable + FLOAT dMilitiaVolunteerPool; + + UINT8 bPadding[ 72 ]; } LaptopSaveInfoStruct; diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index 8ba358f0f..5f1a7d67a 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -1412,6 +1412,10 @@ BOOLEAN CanCharacterTrainMilitia( SOLDIERTYPE *pSoldier ) { AssertNotNIL(pSoldier); + // Flugente: militia volunteer pool + if ( !GetVolunteerPool() ) + return FALSE; + // Make sure the basic sector/merc variables are still applicable. This is simply a fail-safe. if( !BasicCanCharacterTrainMilitia( pSoldier ) ) { @@ -2480,6 +2484,9 @@ void UpdateAssignments() // reset scan flags in all sectors ClearSectorScanResults(); + // update militia volunteer count + UpdateVolunteers(); + // run through sectors and handle each type in sector for(sX = 0 ; sX < MAP_WORLD_X; ++sX ) { @@ -18375,6 +18382,13 @@ BOOLEAN CanCharacterTrainMilitiaWithErrorReport( SOLDIERTYPE *pSoldier ) return( FALSE ); } + if ( !GetVolunteerPool() ) + { + swprintf( sString, L"There are no volunteers for militia left!" ); + DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); + return (FALSE); + } + if ( 100 <= GetMobileMilitiaQuota( TRUE ) ) return ( FALSE ); diff --git a/Strategic/Map Screen Interface Map.cpp b/Strategic/Map Screen Interface Map.cpp index 1525ac0c4..5b34aa785 100644 --- a/Strategic/Map Screen Interface Map.cpp +++ b/Strategic/Map Screen Interface Map.cpp @@ -7667,6 +7667,25 @@ void MilitiaGroupBoxButtonCallback( GUI_BUTTON *btn, INT32 reason ) void DisplayMilitiaGroupBox() { + // if we play with a limited militia pool, show us how many we have + if ( fShowMilitia && gGameExternalOptions.fMilitiaVolunteerPool ) + { + CHAR16 sVolunteerString[200]; + + SetFont( FONT12ARIAL ); + + if ( GetVolunteerPool() >= 2 * gGameExternalOptions.iTrainingSquadSize ) + SetFontForeground( FONT_FCOLOR_GREEN ); + else if ( GetVolunteerPool( ) >= gGameExternalOptions.iTrainingSquadSize ) + SetFontForeground( FONT_FCOLOR_YELLOW ); + else + SetFontForeground( FONT_FCOLOR_RED ); + + // header + swprintf( sVolunteerString, szMilitiaStrategicMovementText[8], GetVolunteerPool( ), CalcHourlyVolunteerGain( ) ); + mprintf( MapScreenRect.iLeft + 20, MapScreenRect.iBottom - 10, sVolunteerString ); + } + if ( !gMilitiaGroupBoxCreated ) return; @@ -7679,6 +7698,8 @@ void DisplayMilitiaGroupBox() return; } + SetFontForeground( FONT_FCOLOR_WHITE ); + CreateMilitiaGroupBoxVideoObjects(); // move to defines later @@ -7715,7 +7736,7 @@ void DisplayMilitiaGroupBox() SetFontForeground( FONT_FCOLOR_WHITE ); SetFontBackground( FONT_MCOLOR_BLACK ); - //Display the background for the drop down window + //Display the background ColorFillVideoSurfaceArea( FRAME_BUFFER, gMilitiaGroupBoxX, gMilitiaGroupBoxY, gMilitiaGroupBoxX + MILITIAGROUPBOX_WIDTH, gMilitiaGroupBoxY + MILITIAGROUPBOX_HEIGHT, Get16BPPColor( FROMRGB( 50, 50, 50 ) ) ); // mouse region - clicking on a group name selects that group diff --git a/Strategic/MilitiaSquads.cpp b/Strategic/MilitiaSquads.cpp index ffe36c2bd..99069a71b 100644 --- a/Strategic/MilitiaSquads.cpp +++ b/Strategic/MilitiaSquads.cpp @@ -191,6 +191,9 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, ubMilitiaToTrain = CalcNumMilitiaTrained(ubBestLeadership, TRUE); } + // Flugente: our pool of volunteers limits how many militia can be created + ubMilitiaToTrain = min( ubMilitiaToTrain, GetVolunteerPool( ) ); + // HEADROCK HAM 3.4: Composition of new Mobile Militia groups is now dictated by two INI settings controlling // the percentage of Elites and Regulars within the group. If the percentage for either is above 0, at least // one militia of that type will be created every time. Green militia are created based on whatever remains. @@ -302,6 +305,9 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, ubTargetGreen--; } + // Flugente: substract volunteers + AddVolunteers( -ubMilitiaToTrain ); + while (ubMilitiaToTrain > 0) { @@ -447,7 +453,7 @@ void GenerateMilitiaSquad(INT16 sMapX, INT16 sMapY, INT16 sTMapX, INT16 sTMapY, --pTargetSector->ubNumberOfCivsAtLevel[ELITE_MILITIA]; } } - + // Update the militia if the current sector is affected if (gfStrategicMilitiaChangesMade) { diff --git a/Strategic/Player Command.cpp b/Strategic/Player Command.cpp index f2fd07951..737ea0e2c 100644 --- a/Strategic/Player Command.cpp +++ b/Strategic/Player Command.cpp @@ -28,6 +28,7 @@ // HEADROCK HAM 3.6: Added for facility string printing... #include "PopUpBox.h" #include "CampaignStats.h" // added by Flugente + #include "Town Militia.h" // added by Flugente #endif #include "postalservice.h" @@ -391,10 +392,18 @@ BOOLEAN SetThisSectorAsPlayerControlled( INT16 sMapX, INT16 sMapY, INT8 bMapZ, B //if ( ubTraverseType == ) - if ( GetTownIdForSector( sMapX, sMapY ) != BLANK_SECTOR ) + UINT8 townid = GetTownIdForSector( sMapX, sMapY ); + if ( townid != BLANK_SECTOR ) { // first liberation of a town sector -> special texts gCurrentIncident.usIncidentFlags |= INCIDENT_FIRST_LIBERATION; + + // liberating a town sector for the first time grants us an initial wave of volunteers + UINT16 population = GetSectorPopulation( sMapX, sMapY ); + + FLOAT loyalpopulation = gTownLoyalty[townid].ubRating * population / 100; + + AddVolunteers( loyalpopulation * gGameExternalOptions.dMilitiaVolunteerGainFactorLiberation ); } } diff --git a/Strategic/Town Militia.cpp b/Strategic/Town Militia.cpp index c7697d1c4..4ed7bfcb8 100644 --- a/Strategic/Town Militia.cpp +++ b/Strategic/Town Militia.cpp @@ -127,6 +127,9 @@ void TownMilitiaTrainingCompleted( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMa UINT8 ubTrainerEffectiveLeadership = FindBestMilitiaTrainingLeadershipInSector ( sMapX, sMapY, pTrainer->bSectorZ, TOWN_MILITIA ); UINT8 iTrainingSquadSize = __min(iMaxMilitiaPerSector, CalcNumMilitiaTrained(ubTrainerEffectiveLeadership, FALSE)); + // Flugente: our pool of volunteers limits how many militia can be created + iTrainingSquadSize = min( iTrainingSquadSize, GetVolunteerPool( ) ); + DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Militia1"); // get town index @@ -310,6 +313,8 @@ void TownMilitiaTrainingCompleted( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMa ++ubMilitiaTrained; } + AddVolunteers( -ubMilitiaTrained ); + if (gfStrategicMilitiaChangesMade) { ResetMilitia(); @@ -2142,7 +2147,7 @@ UINT8 CalcNumMilitiaTrained(UINT8 ubBestLeadership, BOOLEAN fMobile) ubMilitiaToTrain = __max(3, ubMilitiaToTrain); } } - + return (ubMilitiaToTrain); } @@ -2428,4 +2433,66 @@ UINT32 CalcMilitiaUpkeep( void ) } return (uiTotalPayment); -} \ No newline at end of file +} + +// Flugente: our militia volunteer pool is limited +INT32 GetVolunteerPool() +{ + // if this feature is off, we simply assume we have reserves + if ( !gGameExternalOptions.fMilitiaVolunteerPool ) + return 999999; + + // the pool is actually a float number, only return full volunteers though + return (INT32)LaptopSaveInfo.dMilitiaVolunteerPool; +} + +// add/remove volunteers from pool +void AddVolunteers( FLOAT asNum ) +{ + LaptopSaveInfo.dMilitiaVolunteerPool = max( 0, LaptopSaveInfo.dMilitiaVolunteerPool + asNum ); +} + +FLOAT CalcHourlyVolunteerGain() +{ + FLOAT loyalpopulation = 0.0f; // the number of loyal citizens, from which we derive the hourly volunteer gain + FLOAT populationmodifier = 1.0f; // certain sectors/facilities under our control increase the volunteer gain + + for ( UINT8 sX = 1; sX < MAP_WORLD_X - 1; ++sX ) + { + for ( UINT8 sY = 1; sY < MAP_WORLD_X - 1; ++sY ) + { + // not if the enemy controls this sector + if ( StrategicMap[CALCULATE_STRATEGIC_INDEX( sX, sY )].fEnemyControlled ) + continue; + + UINT8 sector = SECTOR( sX, sY ); + + SECTORINFO *pSectorInfo = &(SectorInfo[sector]); + + if ( !pSectorInfo ) + continue; + + // TODO: modifier increase for every farm we control + if ( pSectorInfo->ubTraversability[THROUGH_STRATEGIC_MOVE] == FARMLAND || pSectorInfo->ubTraversability[THROUGH_STRATEGIC_MOVE] == FARMLAND_ROAD ) + populationmodifier += gGameExternalOptions.dMilitiaVolunteerMultiplierFarm; + + UINT8 ubTownID = StrategicMap[CALCULATE_STRATEGIC_INDEX( sX, sY )].bNameId; + if ( ubTownID != BLANK_SECTOR ) + { + UINT16 population = GetSectorPopulation( sX, sY ); + + loyalpopulation += gTownLoyalty[ubTownID].ubRating * population / 100; + } + } + } + + FLOAT hourlygain = loyalpopulation * populationmodifier * gGameExternalOptions.dMilitiaVolunteerGainFactorHourly; + + return hourlygain; +} + +// every hour, controlled sectors add to our volunteer pool +void UpdateVolunteers() +{ + AddVolunteers( CalcHourlyVolunteerGain() ); +} diff --git a/Strategic/Town Militia.h b/Strategic/Town Militia.h index 76fba4c27..44a3eb2e2 100644 --- a/Strategic/Town Militia.h +++ b/Strategic/Town Militia.h @@ -112,4 +112,16 @@ typedef struct MILITIA_LIST_TYPE // HEADROCK HAM 3.6: Calculate upkeep costs for militia UINT32 CalcMilitiaUpkeep( void ); +// Flugente: militia volunteer pool +// how many volunteers do we currently have? +INT32 GetVolunteerPool(); + +// add/remove volunteers from pool +void AddVolunteers( FLOAT asNum ); + +FLOAT CalcHourlyVolunteerGain(); + +// every hour, controlled sectors add to our volunteer pool +void UpdateVolunteers(); + #endif \ No newline at end of file diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 2e1c902f6..ced28076d 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -10042,6 +10042,8 @@ STR16 szMilitiaStrategicMovementText[] = L"Group %d (new)", L"Group %d", L"Final", + + L"Militia Volunteers: %d (+%5.3f)", }; // WANNE: Some Chinese specific strings that needs to be in unicode! diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 0605c019f..601df1886 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -10053,6 +10053,8 @@ STR16 szMilitiaStrategicMovementText[] = L"Group %d (new)", L"Group %d", L"Final", + + L"Militia Volunteers: %d (+%5.3f)", }; #endif //DUTCH diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 971983ce0..bd6a81d92 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -10091,6 +10091,8 @@ STR16 szMilitiaStrategicMovementText[] = L"Group %d (new)", L"Group %d", L"Final", + + L"Militia Volunteers: %d (+%5.3f)", }; #endif //ENGLISH diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 31a1eee11..befe4d68f 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -10039,6 +10039,8 @@ STR16 szMilitiaStrategicMovementText[] = L"Group %d (new)", L"Group %d", L"Final", + + L"Militia Volunteers: %d (+%5.3f)", }; #endif //FRENCH diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 3656bd1d8..4fde1ca4c 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -9870,6 +9870,8 @@ STR16 szMilitiaStrategicMovementText[] = L"Group %d (new)", L"Group %d", L"Final", + + L"Militia Volunteers: %d (+%5.3f)", }; #endif //GERMAN diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index abfc83eec..6da44332f 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -10049,6 +10049,8 @@ STR16 szMilitiaStrategicMovementText[] = L"Group %d (new)", L"Group %d", L"Final", + + L"Militia Volunteers: %d (+%5.3f)", }; #endif //ITALIAN diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index 08c513784..0f6c49100 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -10064,6 +10064,8 @@ STR16 szMilitiaStrategicMovementText[] = L"Group %d (new)", L"Group %d", L"Final", + + L"Militia Volunteers: %d (+%5.3f)", }; #endif //POLISH diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index fd3ba96b2..7c29cdb8b 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -10037,6 +10037,8 @@ STR16 szMilitiaStrategicMovementText[] = L"Group %d (new)", L"Group %d", L"Final", + + L"Militia Volunteers: %d (+%5.3f)", };