From 7b0830c06042acfab2ce5b4650a2a8aa7ddf68b9 Mon Sep 17 00:00:00 2001 From: rftrdev <102184004+rftrdev@users.noreply.github.com> Date: Tue, 11 Oct 2022 00:17:12 -0700 Subject: [PATCH] Initial commit - SVN checkpoint to git branch --- GameSettings.cpp | 78 + GameSettings.h | 79 + Strategic/Assignments.cpp | 20 +- Strategic/Assignments.h | 1 + Strategic/Game Event Hook.cpp | 5 + Strategic/Game Event Hook.h | 2 + Strategic/Game Events.cpp | 1 + Strategic/Map Screen Interface Map.cpp | 38 + Strategic/MiniEvents.cpp | 1 + Strategic/Rebel Command.cpp | 1920 ++++++++++++++++++++++-- Strategic/Rebel Command.h | 94 +- Strategic/Strategic AI.cpp | 5 + Strategic/Town Militia.cpp | 5 +- Tactical/ArmsDealerInvInit.cpp | 6 +- Tactical/DisplayCover.cpp | 10 +- Tactical/Inventory Choosing.cpp | 26 +- Tactical/Soldier Create.cpp | 8 + TileEngine/Map Edgepoints.cpp | 3 +- TileEngine/Tactical Placement GUI.cpp | 14 +- Utils/Text.h | 1 + Utils/_ChineseText.cpp | 70 +- Utils/_DutchText.cpp | 2 +- Utils/_EnglishText.cpp | 39 +- Utils/_FrenchText.cpp | 2 +- Utils/_GermanText.cpp | 2 +- Utils/_ItalianText.cpp | 2 +- Utils/_PolishText.cpp | 2 +- Utils/_RussianText.cpp | 2 +- 28 files changed, 2232 insertions(+), 206 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 49ec4290..3a762983 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -4157,6 +4157,84 @@ void LoadRebelCommandSettings() gRebelCommandSettings.iFortificationsBonus = iniReader.ReadInteger("Rebel Command Settings", "FORTIFICATIONS_BONUS", 10, 0, 100); + // agent missions + gRebelCommandSettings.iDeepDeploymentSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.iDeepDeploymentRangeNS = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_NS", 200, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeEW = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_EW", 350, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeNS_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_NS_BONUS_COVERT", 50, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_EW_BONUS_COVERT" , 15, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeNS_Bonus_Scouting = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_NS_BONUS_SCOUTING" , 25, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Scouting = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_EW_BONUS_SCOUTING" , 40, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeNS_Bonus_Stealthy = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_NS_BONUS_STEALTHY" , 15, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Stealthy = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_EW_BONUS_STEALTHY" , 30, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeNS_Bonus_Survival = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_NS_BONUS_SURVIVAL" , 15, 0, 1000); + gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Survival = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_RANGE_EW_BONUS_SURVIVAL" , 30, 0, 1000); + gRebelCommandSettings.iDeepDeploymentDuration = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_DURATION" , 72, 0, 255); + gRebelCommandSettings.iDeepDeploymentDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_DURATION_BONUS_COVERT" , 24, 0, 255); + gRebelCommandSettings.iDeepDeploymentDuration_Bonus_Scouting = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_DURATION_BONUS_SCOUTING" , 48, 0, 255); + gRebelCommandSettings.iDeepDeploymentDuration_Bonus_Stealthy = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_DURATION_BONUS_STEALTHY" , 36, 0, 255); + gRebelCommandSettings.iDeepDeploymentDuration_Bonus_Survival = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_DURATION_BONUS_SURVIVAL" , 36, 0, 255); + + gRebelCommandSettings.iGetEnemyMovementTargetsSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "STRATEGIC_INTEL_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.iGetEnemyMovementTargetsDuration = iniReader.ReadInteger("Rebel Command Settings", "STRATEGIC_INTEL_DURATION", 72, 0, 255); + gRebelCommandSettings.iGetEnemyMovementTargetsDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "STRATEGIC_INTEL_DURATION_BONUS_COVERT", 48, 0, 255); + gRebelCommandSettings.iGetEnemyMovementTargetsDuration_Bonus_Radio = iniReader.ReadInteger("Rebel Command Settings", "STRATEGIC_INTEL_DURATION_BONUS_RADIO", 48, 0, 255); + + gRebelCommandSettings.iImproveLocalShopsSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "IMPROVE_LOCAL_SHOPS_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.iImproveLocalShopsDuration = iniReader.ReadInteger("Rebel Command Settings", "IMPROVE_LOCAL_SHOPS_DURATION", 72, 0, 255); + + gRebelCommandSettings.iReduceStrategicDecisionSpeedSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier = iniReader.ReadFloat("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_MODIFIER", 1.1f, 1.f, 10.f); + gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Covert = iniReader.ReadFloat("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_MODIFIER_BONUS_COVERT", 1.25f, 1.f, 10.f); + gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Deputy = iniReader.ReadFloat("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_MODIFIER_BONUS_DEPUTY", 1.25f, 1.f, 10.f); + gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Snitch = iniReader.ReadFloat("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_MODIFIER_BONUS_SNITCH", 1.25f, 1.f, 10.f); + gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration = iniReader.ReadInteger("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_DURATION", 72, 0, 255); + gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_DURATION_BONUS_COVERT", 24, 0, 255); + gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration_Bonus_Deputy = iniReader.ReadInteger("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_DURATION_BONUS_DEPUTY", 24, 0, 255); + gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration_Bonus_Snitch = iniReader.ReadInteger("Rebel Command Settings", "SLOWER_STRATEGIC_DECISIONS_DURATION_BONUS_SNITCH", 24, 0, 255); + + gRebelCommandSettings.iReduceUnalertedEnemyVisionSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "LOWER_READINESS_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier = iniReader.ReadFloat("Rebel Command Settings", "LOWER_READINESS_MODIFIER", 0.15f, 0.f, 1.f); + gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Covert = iniReader.ReadFloat("Rebel Command Settings", "LOWER_READINESS_MODIFIER_COVERT", 0.15f, 0.f, 1.f); + gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Radio = iniReader.ReadFloat("Rebel Command Settings", "LOWER_READINESS_MODIFIER_RADIO", 0.15f, 0.f, 1.f); + gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Stealthy = iniReader.ReadFloat("Rebel Command Settings", "LOWER_READINESS_MODIFIER_STEALTHY", 0.15f, 0.f, 1.f); + gRebelCommandSettings.iReduceUnalertedEnemyVisionDuration = iniReader.ReadInteger("Rebel Command Settings", "LOWER_READINESS_DURATION", 72, 0, 255); + gRebelCommandSettings.iReduceUnalertedEnemyVisionDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "LOWER_READINESS_DURATION_BONUS_COVERT", 72, 0, 255); + gRebelCommandSettings.iReduceUnalertedEnemyVisionDuration_Bonus_Radio = iniReader.ReadInteger("Rebel Command Settings", "LOWER_READINESS_DURATION_BONUS_RADIO", 72, 0, 255); + + gRebelCommandSettings.iSabotageInfantryEquipmentSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.iSabotageInfantryEquipmentModifier = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_MODIFIER", 10, 0, 100); + gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Auto_Weapons = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_MODIFIER_AUTO_WEAPONS", 10, 0, 100); + gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Covert = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_MODIFIER_COVERT", 10, 0, 100); + gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Demolitions = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_MODIFIER_DEMOLITIONS", 10, 0, 100); + gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Gunslinger = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_MODIFIER_GUNSLINGER", 10, 0, 100); + gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Ranger = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_MODIFIER_RANGER", 10, 0, 100); + gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Sniper = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_MODIFIER_SNIPER", 10, 0, 100); + gRebelCommandSettings.iSabotageInfantryEquipmentDuration = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_DURATION", 72, 0, 255); + gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Auto_Weapons = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_DURATION_BONUS_AUTO_WEAPONS", 72, 0, 255); + gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_DURATION_BONUS_COVERT", 72, 0, 255); + gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Demolitions = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_DURATION_BONUS_DEMOLITIONS", 72, 0, 255); + gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Gunslinger = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_DURATION_BONUS_GUNSLINGER", 72, 0, 255); + gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Ranger = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_DURATION_BONUS_RANGER", 72, 0, 255); + gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Sniper = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_EQUIPMENT_DURATION_BONUS_SNIPER", 72, 0, 255); + + gRebelCommandSettings.iSabotageMechanicalUnitsSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_STAT_LOSS", 20, 0, 100); + gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Covert = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_STAT_LOSS_COVERT", 20, 0, 100); + gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Demolitions = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_STAT_LOSS_DEMOLITIONS", 20, 0, 100); + gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Heavy_Weapons = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_STAT_LOSS_HEAVY_WEAPONS", 20, 0, 100); + gRebelCommandSettings.iSabotageMechanicalUnitsDuration = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_DURATION", 72, 0, 255); + gRebelCommandSettings.iSabotageMechanicalUnitsDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_DURATION_BONUS_COVERT", 72, 0, 255); + gRebelCommandSettings.iSabotageMechanicalUnitsDuration_Bonus_Demolitions = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_DURATION_BONUS_DEMOLITIONS", 72, 0, 255); + gRebelCommandSettings.iSabotageMechanicalUnitsDuration_Bonus_Heavy_Weapons = iniReader.ReadInteger("Rebel Command Settings", "SABOTAGE_VEHICLES_DURATION_BONUS_HEAVY_WEAPONS", 72, 0, 255); + + gRebelCommandSettings.iTrainMilitiaAnywhereSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "TRAIN_MILITIA_ANYWHERE_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.iTrainMilitiaAnywhereMaxTrainers = iniReader.ReadInteger("Rebel Command Settings", "TRAIN_MILITIA_ANYWHERE_MAX_TRAINERS", 1, 1, 4); + gRebelCommandSettings.iTrainMilitiaAnywhereMaxTrainers_Teaching = iniReader.ReadInteger("Rebel Command Settings", "TRAIN_MILITIA_ANYWHERE_MAX_TRAINERS_TEACHING", 1, 1, 4); + gRebelCommandSettings.iTrainMilitiaAnywhereDuration = iniReader.ReadInteger("Rebel Command Settings", "TRAIN_MILITIA_ANYWHERE_DURATION", 72, 0, 255);; + gRebelCommandSettings.iTrainMilitiaAnywhereDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "TRAIN_MILITIA_ANYWHERE_DURATION_BONUS_COVERT", 72, 0, 255);; + gRebelCommandSettings.iTrainMilitiaAnywhereDuration_Bonus_Survival = iniReader.ReadInteger("Rebel Command Settings", "TRAIN_MILITIA_ANYWHERE_DURATION_BONUS_SURVIVAL", 72, 0, 255);; + gRebelCommandSettings.iTrainMilitiaAnywhereDuration_Bonus_Teaching = iniReader.ReadInteger("Rebel Command Settings", "TRAIN_MILITIA_ANYWHERE_DURATION_BONUS_TEACHING", 72, 0, 255);; } void FreeGameExternalOptions() diff --git a/GameSettings.h b/GameSettings.h index 8ed96c48..117fcf06 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1842,6 +1842,85 @@ typedef struct INT16 iFortificationsBonus; + // agent missions + INT8 iDeepDeploymentSuccessChance; + INT16 iDeepDeploymentRangeNS; + INT16 iDeepDeploymentRangeEW; + INT16 iDeepDeploymentRangeNS_Bonus_Covert; + INT16 iDeepDeploymentRangeEW_Bonus_Covert; + INT16 iDeepDeploymentRangeNS_Bonus_Scouting; + INT16 iDeepDeploymentRangeEW_Bonus_Scouting; + INT16 iDeepDeploymentRangeNS_Bonus_Stealthy; + INT16 iDeepDeploymentRangeEW_Bonus_Stealthy; + INT16 iDeepDeploymentRangeNS_Bonus_Survival; + INT16 iDeepDeploymentRangeEW_Bonus_Survival; + UINT8 iDeepDeploymentDuration; + UINT8 iDeepDeploymentDuration_Bonus_Covert; + UINT8 iDeepDeploymentDuration_Bonus_Scouting; + UINT8 iDeepDeploymentDuration_Bonus_Stealthy; + UINT8 iDeepDeploymentDuration_Bonus_Survival; + + INT8 iGetEnemyMovementTargetsSuccessChance; + UINT8 iGetEnemyMovementTargetsDuration; + UINT8 iGetEnemyMovementTargetsDuration_Bonus_Covert; + UINT8 iGetEnemyMovementTargetsDuration_Bonus_Radio; + + INT8 iImproveLocalShopsSuccessChance; + UINT8 iImproveLocalShopsDuration; + + INT8 iReduceStrategicDecisionSpeedSuccessChance; + FLOAT fReduceStrategicDecisionSpeedModifier; + FLOAT fReduceStrategicDecisionSpeedModifier_Covert; + FLOAT fReduceStrategicDecisionSpeedModifier_Deputy; + FLOAT fReduceStrategicDecisionSpeedModifier_Snitch; + UINT8 iReduceStrategicDecisionSpeedDuration; + UINT8 iReduceStrategicDecisionSpeedDuration_Bonus_Covert; + UINT8 iReduceStrategicDecisionSpeedDuration_Bonus_Deputy; + UINT8 iReduceStrategicDecisionSpeedDuration_Bonus_Snitch; + + INT8 iReduceUnalertedEnemyVisionSuccessChance; + FLOAT fReduceUnlaertedEnemyVisionModifier; + FLOAT fReduceUnlaertedEnemyVisionModifier_Covert; + FLOAT fReduceUnlaertedEnemyVisionModifier_Radio; + FLOAT fReduceUnlaertedEnemyVisionModifier_Stealthy; + UINT8 iReduceUnalertedEnemyVisionDuration; + UINT8 iReduceUnalertedEnemyVisionDuration_Bonus_Covert; + UINT8 iReduceUnalertedEnemyVisionDuration_Bonus_Radio; + + INT8 iSabotageInfantryEquipmentSuccessChance; + INT8 iSabotageInfantryEquipmentModifier; + INT8 iSabotageInfantryEquipmentModifier_Auto_Weapons; + INT8 iSabotageInfantryEquipmentModifier_Covert; + INT8 iSabotageInfantryEquipmentModifier_Demolitions; + INT8 iSabotageInfantryEquipmentModifier_Gunslinger; + INT8 iSabotageInfantryEquipmentModifier_Ranger; + INT8 iSabotageInfantryEquipmentModifier_Sniper; + UINT8 iSabotageInfantryEquipmentDuration; + UINT8 iSabotageInfantryEquipmentDuration_Bonus_Auto_Weapons; + UINT8 iSabotageInfantryEquipmentDuration_Bonus_Covert; + UINT8 iSabotageInfantryEquipmentDuration_Bonus_Demolitions; + UINT8 iSabotageInfantryEquipmentDuration_Bonus_Gunslinger; + UINT8 iSabotageInfantryEquipmentDuration_Bonus_Ranger; + UINT8 iSabotageInfantryEquipmentDuration_Bonus_Sniper; + + INT8 iSabotageMechanicalUnitsSuccessChance; + INT8 iSabotageMechanicalUnitsStatLoss; + INT8 iSabotageMechanicalUnitsStatLoss_Covert; + INT8 iSabotageMechanicalUnitsStatLoss_Demolitions; + INT8 iSabotageMechanicalUnitsStatLoss_Heavy_Weapons; + UINT8 iSabotageMechanicalUnitsDuration; + UINT8 iSabotageMechanicalUnitsDuration_Bonus_Covert; + UINT8 iSabotageMechanicalUnitsDuration_Bonus_Demolitions; + UINT8 iSabotageMechanicalUnitsDuration_Bonus_Heavy_Weapons; + + INT8 iTrainMilitiaAnywhereSuccessChance; + INT8 iTrainMilitiaAnywhereMaxTrainers; + INT8 iTrainMilitiaAnywhereMaxTrainers_Teaching; + UINT8 iTrainMilitiaAnywhereDuration; + UINT8 iTrainMilitiaAnywhereDuration_Bonus_Covert; + UINT8 iTrainMilitiaAnywhereDuration_Bonus_Survival; + UINT8 iTrainMilitiaAnywhereDuration_Bonus_Teaching; + } REBELCOMMAND_SETTINGS; typedef struct diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index 0e6398d6..2b5facce 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -1,3 +1,4 @@ +#pragma optimize("",off) #ifdef PRECOMPILEDHEADERS #include "Strategic All.h" #include "GameSettings.h" @@ -1658,6 +1659,9 @@ BOOLEAN BasicCanCharacterTrainMilitia( SOLDIERTYPE *pSoldier ) // check if sam site if( fSamSitePresent == FALSE ) { + if (RebelCommand::CanTrainMilitiaAnywhere()) + return( TRUE ); + // nope return ( FALSE ); } @@ -1971,6 +1975,9 @@ BOOLEAN CanCharacterTrainMilitia( SOLDIERTYPE *pSoldier ) } } + if (RebelCommand::CanTrainMilitiaAnywhere()) + ubFacilityTrainersAllowed = RebelCommand::GetMaxTrainersForTrainMilitiaAnywhere(); + // Count number of trainers already operating here if ( CountMilitiaTrainersInSoldiersSector( pSoldier, TOWN_MILITIA ) >= ubFacilityTrainersAllowed ) { @@ -2033,6 +2040,9 @@ BOOLEAN DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( SOLDIERTYPE *pSol { return( TRUE ); } + + if (RebelCommand::CanTrainMilitiaAnywhere()) + return(TRUE); return( FALSE ); } @@ -2085,7 +2095,7 @@ BOOLEAN IsMilitiaTrainableFromSoldiersSectorMaxed( SOLDIERTYPE *pSoldier, INT8 i // is there a town really here if( bTownId == BLANK_SECTOR ) { - fSamSitePresent = IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); + fSamSitePresent = IsThisSectorASAMSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ) || RebelCommand::CanTrainMilitiaAnywhere(); // if there is a sam site here if( fSamSitePresent ) @@ -6136,7 +6146,8 @@ void HandleTrainingInSector( INT16 sMapX, INT16 sMapY, INT8 bZ ) } // check if we're doing a sector where militia can be trained - if( ( (StrategicMap[CALCULATE_STRATEGIC_INDEX(sMapX, sMapY) ].bNameId != BLANK_SECTOR ) || ( fSamSiteInSector == TRUE ) ) && (bZ == 0) ) + const BOOL canTrainMilitiaAnywhere = RebelCommand::CanTrainMilitiaAnywhere(); + if( (canTrainMilitiaAnywhere || (StrategicMap[CALCULATE_STRATEGIC_INDEX(sMapX, sMapY) ].bNameId != BLANK_SECTOR ) || ( fSamSiteInSector == TRUE ) ) && (bZ == 0) ) { // init town trainer list memset( TownTrainer, 0, sizeof( TownTrainer ) ); @@ -7898,7 +7909,7 @@ BOOLEAN TrainTownInSector( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMapY, INT1 // get town index ubTownId = StrategicMap[CALCULATE_STRATEGIC_INDEX(pTrainer->sSectorX, pTrainer->sSectorY ) ].bNameId; - if( fSamSiteInSector == FALSE ) + if( fSamSiteInSector == FALSE && !RebelCommand::CanTrainMilitiaAnywhere()) { AssertNE(ubTownId, BLANK_SECTOR); } @@ -20165,6 +20176,9 @@ BOOLEAN CanCharacterTrainMilitiaWithErrorReport( SOLDIERTYPE *pSoldier ) } } + if (RebelCommand::CanTrainMilitiaAnywhere()) + ubFacilityTrainersAllowed = RebelCommand::GetMaxTrainersForTrainMilitiaAnywhere(); + // If we are here, then TrainersAllowed > 0. // Otherwise we'd have failed the BasicCanTrain check if ( CountMilitiaTrainersInSoldiersSector( pSoldier, TOWN_MILITIA ) >= ubFacilityTrainersAllowed ) diff --git a/Strategic/Assignments.h b/Strategic/Assignments.h index a3c731ae..03672ed7 100644 --- a/Strategic/Assignments.h +++ b/Strategic/Assignments.h @@ -100,6 +100,7 @@ enum ADMINISTRATION, // merc boosts the effectiveness of other mercs EXPLORATION, // merc searches the sector for undiscovered items ASSIGNMENT_MINIEVENT, + ASSIGNMENT_REBELCOMMAND, NUM_ASSIGNMENTS, }; diff --git a/Strategic/Game Event Hook.cpp b/Strategic/Game Event Hook.cpp index e951f765..261fbd80 100644 --- a/Strategic/Game Event Hook.cpp +++ b/Strategic/Game Event Hook.cpp @@ -51,6 +51,7 @@ #include "Player Command.h" // added by Flugente #include "LuaInitNPCs.h" // added by Flugente #include "MiniEvents.h" + #include "Rebel Command.h" #endif #include "connect.h" @@ -680,6 +681,10 @@ BOOLEAN ExecuteStrategicEvent( STRATEGICEVENT *pEvent ) CheckMiniEvents(pEvent->uiParam); } break; + + case EVENT_REBELCOMMAND: + RebelCommand::HandleStrategicEvent(pEvent->uiParam); + break; } gfPreventDeletionOfAnyEvent = fOrigPreventFlag; return TRUE; diff --git a/Strategic/Game Event Hook.h b/Strategic/Game Event Hook.h index 20077c04..29e08bb5 100644 --- a/Strategic/Game Event Hook.h +++ b/Strategic/Game Event Hook.h @@ -152,6 +152,8 @@ enum EVENT_MINIEVENT, + EVENT_REBELCOMMAND, + NUMBER_OF_EVENT_TYPES_PLUS_ONE, NUMBER_OF_EVENT_TYPES = NUMBER_OF_EVENT_TYPES_PLUS_ONE - 1 }; diff --git a/Strategic/Game Events.cpp b/Strategic/Game Events.cpp index a698d903..ffcc77b8 100644 --- a/Strategic/Game Events.cpp +++ b/Strategic/Game Events.cpp @@ -140,6 +140,7 @@ CHAR16 gEventName[NUMBER_OF_EVENT_TYPES_PLUS_ONE][40]={ L"bandit attack", L"ArmyFinishTraining", L"MiniEvent", + L"ARC_Event", }; #endif diff --git a/Strategic/Map Screen Interface Map.cpp b/Strategic/Map Screen Interface Map.cpp index 0f0619fa..58041913 100644 --- a/Strategic/Map Screen Interface Map.cpp +++ b/Strategic/Map Screen Interface Map.cpp @@ -1,3 +1,4 @@ +#pragma optimize("",off) #ifdef PRECOMPILEDHEADERS #include "Strategic All.h" #include "GameSettings.h" @@ -49,6 +50,7 @@ #include "Map Screen Interface Map Inventory.h" // added by Flugente #include "LuaInitNPCs.h" // added by Flugente #include "Game Event Hook.h" // added by Flugente + #include "Rebel Command.h" #endif #include "Quests.h" @@ -844,6 +846,42 @@ void fillMapColoursForVisitedSectors(INT32(&colorMap)[ MAXIMUM_VALID_Y_COORDINAT } } } + + if (RebelCommand::ShowEnemyMovementTargets()) + { + const auto targetColor = MAP_SHADE_LT_RED; + GROUP* pGroup = gpGroupList; + + while (pGroup) + { + if (pGroup->usGroupTeam == ENEMY_TEAM) + { + const UINT8 intention = pGroup->pEnemyGroup->ubIntention; + if (intention == STAGING + || intention == REINFORCEMENTS + || intention == PURSUIT + || intention == ASSAULT) + { + WAYPOINT* wp = pGroup->pWaypoints; + + while (wp) + { + if (wp->next == nullptr) + break; + + wp = wp->next; + } + + if (GetSectorFlagStatus(wp->x-1, wp->y-1, (UINT8)iCurrentMapSectorZ, SF_ALREADY_VISITED)) + { + colorMap[wp->y-1][wp->x-1] = targetColor; + } + } + } + + pGroup = pGroup->next; + } + } } diff --git a/Strategic/MiniEvents.cpp b/Strategic/MiniEvents.cpp index 99a5585c..ec801403 100644 --- a/Strategic/MiniEvents.cpp +++ b/Strategic/MiniEvents.cpp @@ -1529,6 +1529,7 @@ void MiniEventsLua(UINT32 eventId) && pSoldier->stats.bLife > 0 && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_POW + && pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE)) { gAllMercs.push_back(pSoldier); diff --git a/Strategic/Rebel Command.cpp b/Strategic/Rebel Command.cpp index 2291b913..737a89e0 100644 --- a/Strategic/Rebel Command.cpp +++ b/Strategic/Rebel Command.cpp @@ -1,4 +1,4 @@ -//#pragma optimize("", off) +#pragma optimize("", off) /* Rebel Command by rftr @@ -15,6 +15,9 @@ Directives can be improved with money. At the start of the campaign, this feature is unavailable, but the player gains access to the ARC website as soon as they complete the food delivery quest for the rebels. +Missions were added later and provide powerful temporary bonuses. To enable these bonuses, Supplies must be spent as well as sending +either a generic rebel agent or one of their own mercenaries, the latter providing better mission bonuses. + How to add a new directive: - add to the RebelCommandDirectives enum in the header @@ -37,6 +40,16 @@ How to add a new admin action: - add admin-action-specific effect - if effect applies outside of towns, add help text range band as appropriate to SetupAdminActionBox +How to add a new mission: +- add to the RebelCommandAgentMissions enum in the header +- add strings to text files (szRebelCommandAgentMissionsText) +- add values to MissionHelpers::missionInfo table in SetupInfo() +- add to valid check in HandleStrategicEvent() (allows advance from first event/prepare to second event/active effect) +- add to SetupMissionAgentBox() (mission description and merc bonus text) +- add to StartMission() +- add mission-specific functions +- rftr todo: ??? + Points of interest: - Init() - set up rebel command for the first time - SetupInfo() - set constants @@ -60,19 +73,21 @@ Points of interest: #include "finances.h" #include "Font Control.h" #include "Game Clock.h" +#include "Game Event Hook.h" #include "GameSettings.h" #include "GameVersion.h" #include "input.h" #include "Line.h" -#include "insurance.h" #include "laptop.h" #include "message.h" #include "MessageBoxScreen.h" #include "MilitiaIndividual.h" #include "mousesystem.h" +#include "Overhead Types.h" #include "Queen Command.h" #include "random.h" #include "SaveLoadGame.h" +#include "strategic.h" #include "strategicmap.h" #include "Strategic Mines.h" #include "Strategic Movement.h" @@ -84,10 +99,13 @@ Points of interest: #include "WordWrap.h" #endif +#include +#include + #define DIRECTIVE_TEXT(id) RCDT_##id##, RCDT_##id##_EFFECT, RCDT_##id##_DESC, RCDT_##id##_IMPROVE, +#define MISSION_TEXT(id) RCAMT_##id##_TITLE, RCAMT_##id##_DESC, #define ADMIN_ACTION_CHANGE_COST 15000 -#define GRANT_SUPPLIES_LOYALTY_GAIN 1000 #define REBEL_COMMAND_DROPDOWN DropDownTemplate::getInstance() @@ -97,12 +115,96 @@ Points of interest: #define WEBSITE_HEIGHT 395 extern UINT32 gCoolnessBySector[256]; -extern UINT32 guiInsuranceBackGround; extern BOOLEAN gfTownUsesLoyalty[MAX_TOWNS]; extern GROUP *gpGroupList; namespace RebelCommand { + +namespace MissionHelpers +{ +constexpr UINT16 DEEP_DEPLOYMENT_RANGE_BONUS_COVERT = 1; +constexpr UINT16 DEEP_DEPLOYMENT_RANGE_BONUS_SCOUTING = 2; +constexpr UINT16 DEEP_DEPLOYMENT_RANGE_BONUS_STEALTHY = 3; +constexpr UINT16 DEEP_DEPLOYMENT_RANGE_BONUS_SURVIVAL = 4; +constexpr UINT16 REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_COVERT = 1; +constexpr UINT16 REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_DEPUTY = 2; +constexpr UINT16 REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_SNITCH = 3; +constexpr UINT16 REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_COVERT = 1; +constexpr UINT16 REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_RADIO = 2; +constexpr UINT16 REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_STEALTHY = 3; +constexpr UINT16 SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_AUTO_WEAPONS = 1; +constexpr UINT16 SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_COVERT = 2; +constexpr UINT16 SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_DEMOLITIONS = 3; +constexpr UINT16 SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_GUNSLINGER = 4; +constexpr UINT16 SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_RANGER = 5; +constexpr UINT16 SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_SNIPER = 6; +constexpr UINT16 SABOTAGE_MECHANICAL_UNITS_COVERT = 1; +constexpr UINT16 SABOTAGE_MECHANICAL_UNITS_DEMOLITIONS = 2; +constexpr UINT16 SABOTAGE_MECHANICAL_UNITS_HEAVY_WEAPONS = 3; +constexpr UINT16 TRAIN_MILITIA_ANYWHERE_TEACHING = 1; + + +typedef struct { + std::vector newSkills; + std::vector oldSkills; + std::vector durationBonuses; + std::vector floatModifiers; + std::vector intModifiers; + std::vector extraBits; +} MissionInfo; + +// this vector serves as a comparison table to determine which bonuses will be applied to a mission +// see SetupInfo() for initialisation +std::vector missionInfo; + +// cached param for firing the preparation/first event. used in the mission start confirmation popup callback +UINT32 missionParam; + +void GetMissionInfo(RebelCommandAgentMissions mission, const MERCPROFILESTRUCT* merc, UINT32& durationBonus, FLOAT& floatModifier, INT16& intModifier, int& durationBonusSkill, int& floatModifierSkill, int& intModifierSkill, UINT16& extraBits) +{ + durationBonus = 0; + durationBonusSkill = 0; + floatModifier = 0; + floatModifierSkill = 0; + intModifier = 0; + intModifierSkill = 0; + extraBits = 0; + + const std::vector* skills = gGameOptions.fNewTraitSystem ? &missionInfo[mission].newSkills : &missionInfo[mission].oldSkills; + + for (int i = 0; i < sizeof(merc->bSkillTraits) / sizeof(merc->bSkillTraits[0]); ++i) + { + for (size_t j = 0; j < skills->size(); ++j) + { + if (merc->bSkillTraits[i] == (*skills)[j]) + { + if (missionInfo[mission].durationBonuses[j] > durationBonus) + { + durationBonus = missionInfo[mission].durationBonuses[j]; + durationBonusSkill = (*skills)[j]; + } + + if (missionInfo[mission].floatModifiers[j] > floatModifier) + { + floatModifier = missionInfo[mission].floatModifiers[j]; + floatModifierSkill = (*skills)[j]; + extraBits = missionInfo[mission].extraBits[j]; + } + + if (missionInfo[mission].intModifiers[j] > intModifier) + { + intModifier = missionInfo[mission].intModifiers[j]; + intModifierSkill = (*skills)[j]; + extraBits = missionInfo[mission].extraBits[j]; + } + } + } + } +} + +} + void DEBUG_DAY(); void DEBUG_PRINT(); @@ -110,16 +212,21 @@ enum WebsiteState { RCS_NATIONAL_OVERVIEW, RCS_REGIONAL_OVERVIEW, + RCS_AGENT_OVERVIEW, }; enum RebelCommandText // keep this synced with szRebelCommandText in the text files { RCT_NATIONAL_OVERVIEW = 0, RCT_REGIONAL_OVERVIEW, + RCT_AGENT_OVERVIEW, + RCT_SELECT_VIEW, RCT_SWITCH_TO_REGIONAL, RCT_SWITCH_TO_NATIONAL, + RCT_SWITCH_TO_AGENT, RCT_SUPPLIES, RCT_INCOMING_SUPPLIES, + RCT_INTEL, RCT_PER_DAY, RCT_CURRENT_DIRECTIVE, RCT_IMPROVE_DIRECTIVE, @@ -187,7 +294,6 @@ enum RebelCommandHelpText // keep this synced with szRebelCommandHelpText in the RCHT_ADMIN_TEAM, RCHT_LOYALTY, RCHT_MAX_LOYALTY, - RCHT_GRANT_SUPPLIES, RCHT_AA_TOWN_ONLY, RCHT_AA_TOWN_PLUS_ONE, RCHT_AA_TOWN_PLUS_TWO, @@ -210,21 +316,123 @@ enum RebelCommandDirectivesText // keep this synced with szRebelCommandDirective DIRECTIVE_TEXT(DRAFT) }; +enum RebelCommandAgentMissionsText // keep this synced with szRebelCommandAgentMissionsText in the text files +{ + //RCAMT_NOTIFY_MISSIONS_AVAILABLE, + RCAMT_NEW_MISSIONS_AVAILABLE_TIME, + RCAMT_MISSION_ACTIVE, + MISSION_TEXT(DEEP_DEPLOYMENT) + MISSION_TEXT(GET_ENEMY_MOVEMENT_TARGETS) + MISSION_TEXT(IMPROVE_LOCAL_SHOPS) + MISSION_TEXT(REDUCE_STRATEGIC_DECISION_SPEED) + MISSION_TEXT(REDUCE_UNALERTED_ENEMY_VISION) + MISSION_TEXT(SABOTAGE_INFANTRY_EQUIPMENT) + MISSION_TEXT(SABOTAGE_MECHANICAL_UNITS) + MISSION_TEXT(TRAIN_MILITIA_ANYWHERE) +}; + enum ChangeAdminActionState { CAAS_INIT, CAAS_CHANGING, }; +enum MissionOverviewSubview +{ + MOS_MISSION_LIST, + MOS_ACTIVE_MISSION_EFFECTS, +}; + +struct MissionFirstEvent +{ + BOOLEAN isFirstEvent; + BOOLEAN sentGenericRebelAgent; + BOOLEAN isMissionSuccess; + UINT8 mercProfileId; + UINT8 missionId; + UINT8 missionDurationInHours; + UINT8 extraBits; +}; + +struct MissionSecondEvent +{ + BOOLEAN isSecondEvent; + BOOLEAN sentGenericRebelAgent; + UINT8 mercProfileId; + UINT8 missionId; + UINT16 extraBits; +}; + +// serialisation/deserialisation functions for passing information into a strategic event param +UINT32 SerialiseMissionFirstEvent(BOOLEAN sentGenericRebelAgent, UINT8 mercProfileId, RebelCommandAgentMissions mission, UINT8 missionDuration, UINT8 extraBits) +{ + UINT32 ret = 0x00000000; + + if (!sentGenericRebelAgent) + ret |= 0x01000000; + + ret |= (mercProfileId << 16); + ret |= (static_cast(mission) << 8); + ret |= missionDuration; + + // extraBits can only be 6 bits + extraBits &= 0x3F; + + ret |= (extraBits << 25); + + return ret; +} + +void DeserialiseMissionFirstEvent(UINT32 param, MissionFirstEvent& evt) +{ + evt.isFirstEvent = ((param >> 31) & 0x00000001) == 0; + evt.sentGenericRebelAgent = ((param >> 24) & 0x00000001) == 0; + evt.isMissionSuccess = (param & 0x000000FF) > 0; + evt.mercProfileId = ((param >> 16) & 0x000000FF); + evt.missionId = ((param >> 8) & 0x000000FF); + evt.missionDurationInHours = (param & 0x000000FF); + evt.extraBits = ((param >> 25) & 0x0000003F); +} + +UINT32 SerialiseMissionSecondEvent(BOOLEAN sentGenericRebelAgent, UINT8 mercProfileId, RebelCommandAgentMissions mission, UINT16 extraBits) +{ + UINT32 ret = 0x80000000; + + if (!sentGenericRebelAgent) + ret |= 0x00010000; + + ret |= (mercProfileId << 8); + ret |= static_cast(mission); + + // extraBits can only be 14 bits + extraBits &= 0x3FFF; + + ret |= (extraBits << 17); + + return ret; +} + +void DeserialiseMissionSecondEvent(UINT32 param, MissionSecondEvent& evt) +{ + evt.isSecondEvent = ((param >> 31) & 0x00000001) == 1; + evt.sentGenericRebelAgent = ((param >> 16) & 0x00000001) == 0; + evt.mercProfileId = ((param >> 8) & 0x000000FF); + evt.missionId = (param & 0x000000FF); + evt.extraBits = ((param >> 17) & 0x0003FFF); +} + // website functions template void ButtonHelper(GUI_BUTTON* btn, INT32 reason, voidFunc onClick); +INT32 CalcIncomingSuppliesPerDay(RebelCommandDirectives directive); void ClearAllButtons(); void ClearAllHelpTextRegions(); void DeployOrReactivateAdminTeam(INT16 regionId); void DropdownSetup(); void GetDirectiveEffect(const RebelCommandDirectives directive, STR16 text); INT32 GetDirectiveImprovementCost(const RebelCommandDirectives directive); +INT32 GetMissionCost(); +INT8 GetMissionSuccessChanceBonus(const MERCPROFILESTRUCT* merc); void ImproveDirective(const RebelCommandDirectives directiveId); void PurchaseAdminAction(INT32 regionId, INT32 actionIndex); void RegionNavNext(); @@ -232,12 +440,18 @@ void RegionNavPrev(); void RenderHeader(RebelCommandText titleText); void RenderNationalOverview(); void RenderRegionalOverview(); +void RenderMissionOverview(); void SetDirectiveDescriptionHelpText(INT32 reason, MOUSE_REGION& region, RebelCommandDirectives text); void SetRegionHelpText(INT32 reason, MOUSE_REGION& helpTextRegion, RebelCommandHelpText text); void SetupAdminActionBox(const UINT8 actionIndex, const UINT16 descriptionText, const UINT16 buttonText); +BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index); +void SetWebsiteView(WebsiteState newState); +void StartMission(INT8 index); void ToggleWebsiteView(); void UpdateAdminActionChangeList(INT16 regionId); +constexpr BOOLEAN CanAdminActionBeToggled(RebelCommandAdminActions action) { return action != RebelCommandAdminActions::RCAA_SUPPLY_LINE; } +BOOLEAN CanAdminActionBeUsed(INT32 regionIndex, INT32 actionIndex); INT32 GetAdminActionCostForRegion(INT16 regionId); INT16 GetAdminActionInRegion(INT16 regionId, RebelCommandAdminActions adminAction); UINT8 GetRegionLoyalty(INT16 regionId); @@ -250,8 +464,10 @@ std::vector btnIds; ChangeAdminActionState adminActionChangeState; // help text regions +MOUSE_REGION adminActionActiveTextRegion[5]; MOUSE_REGION adminActionHelpTextRegion[6]; MOUSE_REGION adminTeamHelpTextRegion; +//MOUSE_REGION agentNotifyToggleTextRegion; MOUSE_REGION directiveDescriptionHelpTextRegion; MOUSE_REGION loyaltyHelpTextRegion; MOUSE_REGION maxLoyaltyHelpTextRegion; @@ -266,6 +482,10 @@ INT16 iCurrentRegionId = 1; INT32 iIncomingSuppliesPerDay = 0; SaveInfo rebelCommandSaveInfo; WebsiteState websiteState; +//INT8 missionIndex[NUM_ARC_AGENT_SLOTS]; +INT8 agentIndex[NUM_ARC_AGENT_SLOTS]; +std::unordered_map missionMap; +MissionOverviewSubview missionOverviewSubview = MOS_MISSION_LIST; // website template @@ -285,6 +505,28 @@ void ButtonHelper(GUI_BUTTON* btn, INT32 reason, voidFunc onClick) } } +INT32 CalcIncomingSuppliesPerDay(RebelCommandDirectives directive) +{ + const INT32 base = static_cast(CurrentPlayerProgressPercentage() * gRebelCommandSettings.fIncomeModifier + (directive == RCD_GATHER_SUPPLIES ? rebelCommandSaveInfo.directives[RCD_GATHER_SUPPLIES].GetValue1() : 0)); + const INT32 supplyUpkeep = static_cast(gRebelCommandSettings.fIncomeModifier + 0.5f); + INT32 upkeepCount = 0; + + for (int a = FIRST_TOWN+1; a < NUM_TOWNS; ++a) + { + // ignore this region if there is no active admin team + if (rebelCommandSaveInfo.regions[a].adminStatus != RAS_ACTIVE) + continue; + + for (int b = 0; b < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++b) + { + if (rebelCommandSaveInfo.regions[a].IsActive(b) && rebelCommandSaveInfo.regions[a].GetLevel(b) > 0) + upkeepCount++; + } + } + + return base - upkeepCount * supplyUpkeep; +} + void ClearAllButtons() { for (const auto btnId : btnIds) @@ -296,9 +538,12 @@ void ClearAllButtons() void ClearAllHelpTextRegions() { + for (int a = 0; a < 5; a++) + MSYS_RemoveRegion(&adminActionActiveTextRegion[a]); for (int a = 0; a < 6; a++) MSYS_RemoveRegion(&adminActionHelpTextRegion[a]); MSYS_RemoveRegion(&adminTeamHelpTextRegion); + //MSYS_RemoveRegion(&agentNotifyToggleTextRegion); MSYS_RemoveRegion(&directiveDescriptionHelpTextRegion); MSYS_RemoveRegion(&loyaltyHelpTextRegion); MSYS_RemoveRegion(&maxLoyaltyHelpTextRegion); @@ -375,6 +620,19 @@ void DropdownSetup() REBEL_COMMAND_DROPDOWN.Create(WEBSITE_LEFT + 5, WEBSITE_TOP + 98); } +BOOLEAN CanAdminActionBeUsed(INT32 regionIndex, INT32 actionIndex) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) return FALSE; + + if (rebelCommandSaveInfo.regions[regionIndex].adminStatus != RAS_ACTIVE) return FALSE; + + if (rebelCommandSaveInfo.regions[regionIndex].GetLevel(actionIndex) == 0) return FALSE; + + if (CanAdminActionBeToggled(rebelCommandSaveInfo.regions[regionIndex].actions[actionIndex]) && !rebelCommandSaveInfo.regions[regionIndex].IsActive(actionIndex)) return FALSE; + + return TRUE; +} + INT32 GetAdminActionCostForRegion(INT16 regionId) { INT16 totalLocalActions = 0; @@ -385,10 +643,10 @@ INT32 GetAdminActionCostForRegion(INT16 regionId) { for (int b = 0; b < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++b) { - totalNationalActions += rebelCommandSaveInfo.regions[a].actionLevels[b]; + totalNationalActions += rebelCommandSaveInfo.regions[a].GetLevel(b); if (a == regionId) - totalLocalActions += rebelCommandSaveInfo.regions[a].actionLevels[b]; + totalLocalActions += rebelCommandSaveInfo.regions[a].GetLevel(b); } } @@ -401,7 +659,7 @@ INT16 GetAdminActionInRegion(INT16 regionId, RebelCommandAdminActions adminActio { for (int idx = 0; idx < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++idx) { - if (rebelCommandSaveInfo.regions[regionId].actions[idx] == adminAction) + if (rebelCommandSaveInfo.regions[regionId].actions[idx] == adminAction && rebelCommandSaveInfo.regions[regionId].IsActive(idx)) { return idx; } @@ -453,6 +711,17 @@ INT32 GetDirectiveImprovementCost(const RebelCommandDirectives directive) return rebelCommandSaveInfo.directives[directive].GetCostToImprove(); } +INT32 GetMissionCost() +{ + const INT32 additionalCost = missionMap.size() * 250; + return 500 + additionalCost; +} + +INT8 GetMissionSuccessChanceBonus(const MERCPROFILESTRUCT* merc) +{ + return merc ? merc->bExpLevel * 5 : 0; +} + void ImproveDirective(const RebelCommandDirectives directive) { const INT32 cost = rebelCommandSaveInfo.directives[directive].GetCostToImprove(); @@ -552,35 +821,111 @@ void SetupAdminActionBox(const UINT8 actionIndex, const UINT16 descriptionText, { // show label if maxed out if ((actionIndex == RCAA_SUPPLY_LINE && rebelCommandSaveInfo.regions[iCurrentRegionId].ubMaxLoyalty >= MAX_LOYALTY_VALUE) - || (actionIndex != RCAA_SUPPLY_LINE && rebelCommandSaveInfo.regions[iCurrentRegionId].actionLevels[actionIndex] >= 2)) + || (actionIndex != RCAA_SUPPLY_LINE && rebelCommandSaveInfo.regions[iCurrentRegionId].GetLevel(actionIndex) >= 2)) { DrawTextToScreen(szRebelCommandAdminActionsText[buttonText], x, y + 7, 0, FONT10ARIALBOLD, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); } else // show button { - const UINT8 level = rebelCommandSaveInfo.regions[iCurrentRegionId].actionLevels[actionIndex]; + const UINT8 level = rebelCommandSaveInfo.regions[iCurrentRegionId].GetLevel(actionIndex); swprintf(text, szRebelCommandText[level == 0 ? RCT_ADMIN_ACTION_ESTABLISH : RCT_ADMIN_ACTION_IMPROVE], szRebelCommandAdminActionsText[buttonText]); - const INT32 btnId = CreateTextButton(text, FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, x, y, 140, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + const INT32 btnId = CreateTextButton(text, FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, x, y, 140, 18, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) { - ButtonHelper(btn, reason, [btn]() { PurchaseAdminAction(btn->UserData[0], btn->UserData[1]); }); + ButtonHelper(btn, reason, [btn]() { PurchaseAdminAction(MSYS_GetBtnUserData(btn, 0), MSYS_GetBtnUserData(btn, 1)); }); }); - Assert(ButtonList[btnId]); - ButtonList[btnId]->UserData[0] = iCurrentRegionId; - ButtonList[btnId]->UserData[1] = actionIndex; + MSYS_SetBtnUserData(btnId, 0, iCurrentRegionId); + MSYS_SetBtnUserData(btnId, 1, actionIndex); btnIds.push_back(btnId); } y += 22; - swprintf(text, szRebelCommandText[RCT_ADMIN_ACTION_TIER], rebelCommandSaveInfo.regions[iCurrentRegionId].actionLevels[actionIndex]); + swprintf(text, szRebelCommandText[RCT_ADMIN_ACTION_TIER], rebelCommandSaveInfo.regions[iCurrentRegionId].GetLevel(actionIndex)); DrawTextToScreen(text, x, y, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + // show on/off switch for toggleable actions + if (CanAdminActionBeToggled(rebelCommandSaveInfo.regions[iCurrentRegionId].actions[actionIndex]) && rebelCommandSaveInfo.regions[iCurrentRegionId].GetLevel(actionIndex) > 0) + { + // draw checkbox text + DrawTextToScreen(szRebelCommandText[RCT_ACTIVE], x+125, y, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, RIGHT_JUSTIFIED); + + // draw checkbox + const INT32 btnId = CreateCheckBoxButton(x + 128, y-3, "INTERFACE\\OptionsCheckBoxes_12x12.sti", MSYS_PRIORITY_HIGH, [](GUI_BUTTON* btn, INT32 reason) { + const UINT8 regionIndex = (UINT8)MSYS_GetBtnUserData( btn, 0 ); + const UINT8 actionIndex = (UINT8)MSYS_GetBtnUserData( btn, 1 ); + + if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP) + { + if (btn->uiFlags & BUTTON_CLICKED_ON) + { + // don't toggle on if we have a bad supply balance + if (rebelCommandSaveInfo.iSupplies <= 0) + return; + + btn->uiFlags &= ~BUTTON_CLICKED_ON; + rebelCommandSaveInfo.regions[regionIndex].SetActive(actionIndex); + } + else + { + btn->uiFlags |= BUTTON_CLICKED_ON; + rebelCommandSaveInfo.regions[regionIndex].SetInactive(actionIndex); + } + } + RenderWebsite(); + }); + + MSYS_SetBtnUserData( btnId, 0, iCurrentRegionId ); + MSYS_SetBtnUserData( btnId, 1, actionIndex ); + + Assert(ButtonList[btnId]); + if (rebelCommandSaveInfo.regions[iCurrentRegionId].IsActive(actionIndex)) + ButtonList[btnId]->uiFlags |= BUTTON_CLICKED_ON; + + btnIds.push_back(btnId); + + // setup mouse target for "Active" text - setting this AFTER the checkbox so we can set the button id + MSYS_DefineRegion(&adminActionActiveTextRegion[actionIndex-1], x+75, y-2, x+125, y+14, MSYS_PRIORITY_HIGH, + CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, [](MOUSE_REGION* pRegion, INT32 iReason) { + if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP) + { + const UINT8 regionIndex = (UINT8)MSYS_GetRegionUserData( pRegion, 0 ); + const UINT8 actionIndex = (UINT8)MSYS_GetRegionUserData( pRegion, 1 ); + const INT32 buttonId = MSYS_GetRegionUserData( pRegion, 2 ); + GUI_BUTTON* btn = ButtonList[buttonId]; + + if (btn->uiFlags & BUTTON_CLICKED_ON) + { + // don't toggle on if we have a bad supply balance + if (rebelCommandSaveInfo.iSupplies <= 0) + return; + + btn->uiFlags &= ~BUTTON_CLICKED_ON; + rebelCommandSaveInfo.regions[regionIndex].SetInactive(actionIndex); + } + else + { + btn->uiFlags |= BUTTON_CLICKED_ON; + rebelCommandSaveInfo.regions[regionIndex].SetActive(actionIndex); + } + + RenderWebsite(); + } + }); + MSYS_AddRegion(&adminActionActiveTextRegion[actionIndex-1]); + MSYS_SetRegionUserData(&adminActionActiveTextRegion[actionIndex-1], 0, iCurrentRegionId); + MSYS_SetRegionUserData(&adminActionActiveTextRegion[actionIndex-1], 1, actionIndex); + MSYS_SetRegionUserData(&adminActionActiveTextRegion[actionIndex-1], 2, btnId); + + } + y += 13; - DisplayWrappedString(x, y, 140, 2, FONT10ARIAL, FONT_MCOLOR_BLACK, szRebelCommandAdminActionsText[descriptionText], FONT_MCOLOR_BLACK, FALSE, 0); + const UINT8 textColor = rebelCommandSaveInfo.regions[iCurrentRegionId].IsActive(actionIndex) ? FONT_MCOLOR_BLACK : FONT_MCOLOR_DKGRAY; + DisplayWrappedString(x, y, 140, 2, FONT10ARIAL, textColor, szRebelCommandAdminActionsText[descriptionText], FONT_MCOLOR_BLACK, FALSE, 0); helpTextY = y; + // special case for index 5: show state change button if (actionIndex == 5) { @@ -711,12 +1056,27 @@ void SetRegionHelpText(INT32 reason, MOUSE_REGION& helpTextRegion, RebelCommandH SetRegionFastHelpText(&helpTextRegion, L""); } +void SetWebsiteView(WebsiteState newState) +{ + websiteState = newState; +} + void ToggleWebsiteView() { - if (websiteState == RCS_REGIONAL_OVERVIEW) - websiteState = RCS_NATIONAL_OVERVIEW; - else + switch (websiteState) + { + case RCS_NATIONAL_OVERVIEW: websiteState = RCS_REGIONAL_OVERVIEW; + break; + + case RCS_REGIONAL_OVERVIEW: + websiteState = RCS_AGENT_OVERVIEW; + break; + + case RCS_AGENT_OVERVIEW: + websiteState = RCS_NATIONAL_OVERVIEW; + break; + } } void UpdateAdminActionChangeList(INT16 regionId) @@ -747,6 +1107,16 @@ void UpdateAdminActionChangeList(INT16 regionId) BOOLEAN EnterWebsite() { + // debugging. todo: randomise missions in DailyUpdate and save them in rebelcommandsaveinfo + for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) + { + //missionIndex[i] = Random(RCAM_NUM_MISSIONS-1) + 1; + rebelCommandSaveInfo.availableMissions[i] = Random(RCAM_NUM_MISSIONS); + agentIndex[i] = -1; + } + //missionIndex[0] = RCAM_TRAIN_MILITIA_ANYWHERE; + //missionIndex[1] = RCAM_IMPROVE_LOCAL_SHOPS; + UpdateAdminActionChangeList(iCurrentRegionId); // make sure we have a valid directive @@ -760,14 +1130,6 @@ BOOLEAN EnterWebsite() websiteState = RCS_NATIONAL_OVERVIEW; - VOBJECT_DESC VObjectDesc; - - // load the background (white tile) - VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; - FilenameForBPP("LAPTOP\\BackGroundTile.sti", VObjectDesc.ImageFile); - AddVideoObject(&VObjectDesc, &guiInsuranceBackGround); - - RenderWebsite(); return(TRUE); @@ -778,8 +1140,6 @@ void ExitWebsite() ClearAllButtons(); ClearAllHelpTextRegions(); REBEL_COMMAND_DROPDOWN.Destroy(); - - DeleteVideoObjectFromIndex(guiInsuranceBackGround); } void HandleWebsite() @@ -809,6 +1169,21 @@ void HandleWebsite() RenderWebsite(); break; + case '1': + SetWebsiteView(RCS_NATIONAL_OVERVIEW); + RenderWebsite(); + break; + + case '2': + SetWebsiteView(RCS_REGIONAL_OVERVIEW); + RenderWebsite(); + break; + + case '3': + SetWebsiteView(RCS_AGENT_OVERVIEW); + RenderWebsite(); + break; + default: HandleKeyBoardShortCutsForLapTop(input.usEvent, input.usParam, input.usKeyState); break; @@ -833,9 +1208,9 @@ void RenderWebsite() ClearAllHelpTextRegions(); // background - WebPageTileBackground(4, 4, 125, 100, guiInsuranceBackGround); + ColorFillVideoSurfaceArea(FRAME_BUFFER, WEBSITE_LEFT, WEBSITE_TOP, WEBSITE_LEFT + WEBSITE_WIDTH, WEBSITE_TOP + WEBSITE_HEIGHT, Get16BPPColor(FROMRGB(224, 224, 224))); - SetFontShadow(FONT_MCOLOR_WHITE); + SetFontShadow(FONT_GRAY1); // national/regional views switch (websiteState) @@ -844,6 +1219,10 @@ void RenderWebsite() RenderRegionalOverview(); break; + case RCS_AGENT_OVERVIEW: + RenderMissionOverview(); + break; + case RCS_NATIONAL_OVERVIEW: default: RenderNationalOverview(); @@ -861,10 +1240,11 @@ void RenderHeader(RebelCommandText titleText) { CHAR16 sText[500]; UINT16 usPosX, usPosY; + INT32 btnId; // title usPosX = WEBSITE_LEFT + 1; - usPosY = WEBSITE_TOP + 3; + usPosY = WEBSITE_TOP + 2; DrawTextToScreen(szRebelCommandText[titleText], usPosX, usPosY, 0, FONT16ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); // supplies @@ -873,21 +1253,70 @@ void RenderHeader(RebelCommandText titleText) DrawTextToScreen(szRebelCommandText[RCT_SUPPLIES], usPosX, usPosY, 0, FONT10ARIALBOLD, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); // supply count - usPosX = WEBSITE_LEFT + 50; + usPosX = WEBSITE_LEFT + 55; usPosY = WEBSITE_TOP + 20; swprintf(sText, L"%d", rebelCommandSaveInfo.iSupplies); DrawTextToScreen(sText, usPosX, usPosY, 0, FONT14ARIAL, rebelCommandSaveInfo.iSupplies > 0 ? FONT_GREEN : FONT_MCOLOR_LTRED, FONT_MCOLOR_BLACK, FALSE, 0); + // intel + usPosX = WEBSITE_LEFT + 150; + usPosY = WEBSITE_TOP + 23; + DrawTextToScreen(szRebelCommandText[RCT_INTEL], usPosX, usPosY, 0, FONT10ARIALBOLD, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // intel count + usPosX = WEBSITE_LEFT + 185; + usPosY = WEBSITE_TOP + 20; + const int intel = (int)GetIntel(); + swprintf(sText, L"%d", intel); + DrawTextToScreen(sText, usPosX, usPosY, 0, FONT14ARIAL, intel > 0 ? FONT_GREEN : FONT_MCOLOR_LTRED, FONT_MCOLOR_BLACK, FALSE, 0); + // supplies region MSYS_DefineRegion(&suppliesHelpTextRegion, WEBSITE_LEFT, WEBSITE_TOP + 20, WEBSITE_LEFT + 100, WEBSITE_TOP + 35, MSYS_PRIORITY_HIGH, CURSOR_LAPTOP_SCREEN, [](MOUSE_REGION* pRegion, INT32 iReason) { SetRegionHelpText(iReason, suppliesHelpTextRegion, RCHT_SUPPLIES); }, MSYS_NO_CALLBACK); MSYS_AddRegion(&suppliesHelpTextRegion); MSYS_SetRegionUserData(&suppliesHelpTextRegion, 0, 0); + // view select text + usPosX = WEBSITE_LEFT + 251; + usPosY = WEBSITE_TOP + 3; + DrawTextToScreen(szRebelCommandText[RCT_SELECT_VIEW], usPosX, usPosY, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // view swap buttons + usPosY = WEBSITE_TOP + 13; + btnId = CreateTextButton(szRebelCommandText[RCT_SWITCH_TO_NATIONAL], FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 82, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + { + ButtonHelper(btn, reason, []() { SetWebsiteView(RCS_NATIONAL_OVERVIEW); }); + }); + btnIds.push_back(btnId); + usPosX = WEBSITE_LEFT + 334; + btnId = CreateTextButton(szRebelCommandText[RCT_SWITCH_TO_REGIONAL], FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 82, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + { + ButtonHelper(btn, reason, []() { SetWebsiteView(RCS_REGIONAL_OVERVIEW); }); + }); + btnIds.push_back(btnId); + usPosX = WEBSITE_LEFT + 417; + btnId = CreateTextButton(szRebelCommandText[RCT_SWITCH_TO_AGENT], FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 82, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + { + ButtonHelper(btn, reason, []() { SetWebsiteView(RCS_AGENT_OVERVIEW); }); + }); + btnIds.push_back(btnId); + // line at the bottom of the header usPosX = WEBSITE_LEFT - 1; usPosY = WEBSITE_TOP + 35; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 500, usPosY, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // DEBUG if (CHEATER_CHEAT_LEVEL()) @@ -920,15 +1349,6 @@ void RenderNationalOverview() // title RenderHeader(RCT_NATIONAL_OVERVIEW); - // view swap button - usPosX = WEBSITE_LEFT + 350; - usPosY = WEBSITE_TOP + 1; - btnId = CreateTextButton(szRebelCommandText[RCT_SWITCH_TO_REGIONAL], FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 149, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) - { - ButtonHelper(btn, reason, []() { ToggleWebsiteView(); }); - }); - btnIds.push_back(btnId); - // incoming supplies usPosX = WEBSITE_LEFT + 1; usPosY = WEBSITE_TOP + 40; @@ -936,7 +1356,7 @@ void RenderNationalOverview() usPosX = WEBSITE_LEFT + 5; usPosY += 10; - iIncomingSuppliesPerDay = static_cast(CurrentPlayerProgressPercentage() * gRebelCommandSettings.fIncomeModifier + (rebelCommandSaveInfo.iSelectedDirective == RCD_GATHER_SUPPLIES ? rebelCommandSaveInfo.directives[RCD_GATHER_SUPPLIES].GetValue1() : 0)); + iIncomingSuppliesPerDay = CalcIncomingSuppliesPerDay(static_cast(rebelCommandSaveInfo.iSelectedDirective)); swprintf(sText, L"%d", iIncomingSuppliesPerDay); DrawTextToScreen(sText, usPosX, usPosY, 0, FONT14ARIAL, iIncomingSuppliesPerDay > 0 ? FONT_GREEN : FONT_MCOLOR_LTRED, FONT_MCOLOR_BLACK, FALSE, 0); @@ -954,14 +1374,62 @@ void RenderNationalOverview() usPosX = WEBSITE_LEFT + 1; usPosY -= 13; MSYS_DefineRegion(&suppliesIncomeHelpTextRegion, usPosX, usPosY, usPosX + 100, usPosY + 35, MSYS_PRIORITY_HIGH, - CURSOR_LAPTOP_SCREEN, [](MOUSE_REGION* pRegion, INT32 iReason) { SetRegionHelpText(iReason, suppliesIncomeHelpTextRegion, RCHT_SUPPLIES_INCOME); }, MSYS_NO_CALLBACK); + CURSOR_LAPTOP_SCREEN, [](MOUSE_REGION* pRegion, INT32 iReason) { + if (iReason == MSYS_CALLBACK_REASON_MOVE) + { + CHAR16 text[1000]; + + // base income + const INT32 base = static_cast(CurrentPlayerProgressPercentage() * gRebelCommandSettings.fIncomeModifier); + swprintf(text, szRebelCommandHelpText[RCHT_SUPPLIES_INCOME], base); + + // admin action upkeep + const INT32 supplyUpkeep = static_cast(gRebelCommandSettings.fIncomeModifier + 0.5f); + + for (int a = FIRST_TOWN+1; a < NUM_TOWNS; ++a) + { + // ignore this region if there is no active admin team + if (rebelCommandSaveInfo.regions[a].adminStatus != RAS_ACTIVE) + continue; + + INT32 upkeepCount = 0; + for (int b = 0; b < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++b) + { + if (rebelCommandSaveInfo.regions[a].IsActive(b) && rebelCommandSaveInfo.regions[a].GetLevel(b) > 0) + upkeepCount++; + } + + if (upkeepCount > 0) + { + const INT32 totalUpkeep = upkeepCount * supplyUpkeep; + swprintf(text, L"%s\n-%d (%s)", text, totalUpkeep, pTownNames[a]); + } + } + + SetRegionFastHelpText(&suppliesIncomeHelpTextRegion, text); + } + else if (iReason == MSYS_CALLBACK_REASON_LOST_MOUSE) + SetRegionFastHelpText(&suppliesIncomeHelpTextRegion, L""); + }, MSYS_NO_CALLBACK); MSYS_AddRegion(&suppliesIncomeHelpTextRegion); MSYS_SetRegionUserData(&suppliesIncomeHelpTextRegion, 0, 0); // line between incoming supplies and directive usPosX = WEBSITE_LEFT - 1; usPosY += 43; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 500, usPosY, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // current directive usPosX = WEBSITE_LEFT + 1; @@ -981,12 +1449,11 @@ void RenderNationalOverview() swprintf(sText, szRebelCommandText[RCT_IMPROVE_DIRECTIVE], GetDirectiveImprovementCost(static_cast(directive))); btnId = CreateTextButton(sText, FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 200, 24, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) { - ButtonHelper(btn, reason, [btn]() { ImproveDirective(static_cast(btn->UserData[0])); }); + ButtonHelper(btn, reason, [btn]() { ImproveDirective(static_cast(MSYS_GetBtnUserData(btn, 0))); }); }); btnIds.push_back(btnId); - Assert(ButtonList[btnId]); - ButtonList[btnId]->UserData[0] = REBEL_COMMAND_DROPDOWN.GetSelectedEntryKey(); + MSYS_SetBtnUserData( btnId, 0, REBEL_COMMAND_DROPDOWN.GetSelectedEntryKey() ); } // directive effect @@ -1016,7 +1483,19 @@ void RenderNationalOverview() // line between directive and militia usPosX = WEBSITE_LEFT - 1; usPosY += 10; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 500, usPosY, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // militia usPosX = WEBSITE_LEFT + 1; @@ -1063,7 +1542,19 @@ void RenderNationalOverview() // draw vertical line usPosX += 75; usPosY = militiaY - 3; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX, usPosY + 38, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // header usPosX += 20; @@ -1084,8 +1575,19 @@ void RenderNationalOverview() // draw vertical line usPosX += 75; usPosY = militiaY - 3; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX, usPosY + 38, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // headers usPosX += 20; DrawTextToScreen(szRebelCommandText[RCT_MILITIA_RESOURCES], usPosX, usPosY, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); @@ -1112,7 +1614,19 @@ void RenderNationalOverview() // line usPosX = WEBSITE_LEFT + 25; usPosY = militiaY + 50; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 450, usPosY, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 450, usPosY, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX + 450, usPosY, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // training cost usPosX = WEBSITE_LEFT + 10; @@ -1121,7 +1635,19 @@ void RenderNationalOverview() // draw vertical line usPosX += 120; - DisplaySmallColouredLineWithShadow(usPosX, usPosY - 2, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY - 2, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY - 2, usPosX, usPosY + 38, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // upkeep cost usPosX += 20; @@ -1164,7 +1690,19 @@ void RenderNationalOverview() // line usPosX = WEBSITE_LEFT + 25; usPosY += 30; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 450, usPosY, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 450, usPosY, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX + 450, usPosY, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // training speed bonus usPosX = WEBSITE_LEFT + 10; @@ -1187,7 +1725,19 @@ void RenderNationalOverview() // draw vertical line usPosX = WEBSITE_LEFT + 130; usPosY -= 12; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX, usPosY + 38, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // militia physical stat bonus usPosX += 20; @@ -1233,15 +1783,6 @@ void RenderRegionalOverview() // title RenderHeader(RCT_REGIONAL_OVERVIEW); - // view swap button - usPosX = WEBSITE_LEFT + 350; - usPosY = WEBSITE_TOP + 1; - btnId = CreateTextButton(szRebelCommandText[RCT_SWITCH_TO_NATIONAL], FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 149, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) - { - ButtonHelper(btn, reason, []() { ToggleWebsiteView(); }); - }); - btnIds.push_back(btnId); - // region usPosX = WEBSITE_LEFT + 1; usPosY = WEBSITE_TOP + 40; @@ -1277,8 +1818,20 @@ void RenderRegionalOverview() // line between region info and admin info usPosX = WEBSITE_LEFT - 1; - usPosY += 20; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 500, usPosY, FROMRGB(240, 240, 240)); + usPosY += 15; + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // admin team usPosX = WEBSITE_LEFT + 1; @@ -1316,7 +1869,19 @@ void RenderRegionalOverview() // vertical line between admin team and loyalty usPosX = WEBSITE_LEFT + 105; usPosY += 5; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 15, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 15, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX, usPosY + 15, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // loyalty usPosX += 15; @@ -1347,7 +1912,19 @@ void RenderRegionalOverview() // vertical line between loyalty and max loyalty usPosX = WEBSITE_LEFT + 195; usPosY += 5; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 15, FROMRGB(240, 240, 240)); + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 15, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX, usPosY + 15, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // max loyalty usPosX += 15; @@ -1375,37 +1952,21 @@ void RenderRegionalOverview() MSYS_AddRegion(&maxLoyaltyHelpTextRegion); MSYS_SetRegionUserData(&maxLoyaltyHelpTextRegion, 0, 0); - // vertical line between max loyalty and supply grant + // vertical line between max loyalty usPosX = WEBSITE_LEFT + 325; usPosY += 5; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 15, FROMRGB(240, 240, 240)); - - if (iCurrentRegionId != OMERTA && rebelCommandSaveInfo.regions[iCurrentRegionId].adminStatus == RAS_ACTIVE) + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX, usPosY + 15, FROMRGB(240, 240, 240)); { - // supply grant - usPosX = WEBSITE_LEFT + 334; - btnId = CreateTextButton(L"Grant 100 Supplies", FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 165, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) - { - ButtonHelper(btn, reason, []() - { - if (rebelCommandSaveInfo.iSupplies >= 100) - { - rebelCommandSaveInfo.iSupplies -= 100; - IncrementTownLoyalty(iCurrentRegionId, static_cast(GRANT_SUPPLIES_LOYALTY_GAIN)); + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; - if (rebelCommandSaveInfo.uSupplyDropCount < 255) - rebelCommandSaveInfo.uSupplyDropCount++; - } - else - { - DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, szRebelCommandText[RCT_INSUFFICIENT_FUNDS], LAPTOP_SCREEN, MSG_BOX_FLAG_OK, NULL); - } - }); - }); - btnIds.push_back(btnId); - - // supply grant region - SetButtonFastHelpText(btnId, szRebelCommandHelpText[RCHT_GRANT_SUPPLIES]); + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX, usPosY + 15, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); } // deploy/reactivate admin teams (if applicable) @@ -1447,12 +2008,11 @@ void RenderRegionalOverview() swprintf(sText, szRebelCommandText[RCT_DEPLOY_ADMIN_TEAM], adminDeployCost); btnId = CreateTextButton(sText, FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 300, 100, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) { - ButtonHelper(btn, reason, [btn]() { DeployOrReactivateAdminTeam(btn->UserData[0]); }); + ButtonHelper(btn, reason, [btn]() { DeployOrReactivateAdminTeam(MSYS_GetBtnUserData(btn, 0)); }); }); btnIds.push_back(btnId); - Assert(ButtonList[btnId]); - ButtonList[btnId]->UserData[0] = iCurrentRegionId; + MSYS_SetBtnUserData( btnId, 0, iCurrentRegionId ); return; } else if (rebelCommandSaveInfo.regions[iCurrentRegionId].adminStatus == RAS_INACTIVE) @@ -1469,20 +2029,31 @@ void RenderRegionalOverview() swprintf(sText, szRebelCommandText[RCT_REACTIVATE_ADMIN_TEAM], adminReactivateCost / 2); btnId = CreateTextButton(sText, FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 300, 100, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) { - ButtonHelper(btn, reason, [btn]() { DeployOrReactivateAdminTeam(btn->UserData[0]); }); + ButtonHelper(btn, reason, [btn]() { DeployOrReactivateAdminTeam(MSYS_GetBtnUserData(btn, 0)); }); }); btnIds.push_back(btnId); - Assert(ButtonList[btnId]); - ButtonList[btnId]->UserData[0] = iCurrentRegionId; + MSYS_SetBtnUserData( btnId, 0, iCurrentRegionId ); return; } // line between admin info and admin actions usPosX = WEBSITE_LEFT - 1; - usPosY += 30; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 500, usPosY, FROMRGB(240, 240, 240)); + usPosY += 25; + //DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + LineDraw(FALSE, usPosX, usPosY, usPosX + WEBSITE_WIDTH, usPosY, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + } // admin actions usPosX = WEBSITE_LEFT + 1; @@ -1506,6 +2077,587 @@ void RenderRegionalOverview() swprintf(sText, szRebelCommandText[RCT_ADMIN_ACTION_COST], GetAdminActionCostForRegion(iCurrentRegionId)); DrawTextToScreen(sText, usPosX, usPosY, 0, FONT10ARIALBOLD, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); } + +BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index) +{ + CHAR16 sText[800]; + INT32 btnId; + VOBJECT_DESC vObjDesc; + HVOBJECT hvObj; + char sTemp[100]; + UINT32 image; + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; + + // temp/fixme + std::vector mercs; + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) + { + SOLDIERTYPE* pSoldier = MercPtrs[i]; + + if (pSoldier && pSoldier->bActive + && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) + ) + { + mercs.push_back(pSoldier); + } + } + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + // top horizontal line + LineDraw(FALSE, x, y, x+230, y, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + // bottom horizontal line + LineDraw(FALSE, x, y+300, x+230, y+300, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + // left vertical line + LineDraw(FALSE, x, y, x, y+300, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + // right vertical line + LineDraw(FALSE, x+230, y, x+230, y+300, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + UnLockVideoSurface( FRAME_BUFFER ); + + // clamp indices + //if (missionIndex[index] < 0) missionIndex[index] = RCAM_NUM_MISSIONS - 1; + //else if (missionIndex[index] >= RCAM_NUM_MISSIONS) missionIndex[index] = 0; + // we're reserving an index for the generic rebel agent, so no need to subtract 1 from size here + if (agentIndex[index] < 0) agentIndex[index] = static_cast(mercs.size()); + else if (agentIndex[index] > static_cast(mercs.size())) agentIndex[index] = 0; + + // draw mission title + switch (rebelCommandSaveInfo.availableMissions[index]) + { + case RCAM_DEEP_DEPLOYMENT: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_DEEP_DEPLOYMENT_TITLE]); break; + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_GET_ENEMY_MOVEMENT_TARGETS_TITLE]); break; + case RCAM_IMPROVE_LOCAL_SHOPS: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_IMPROVE_LOCAL_SHOPS_TITLE]); break; + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_REDUCE_STRATEGIC_DECISION_SPEED_TITLE]); break; + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_REDUCE_UNALERTED_ENEMY_VISION_TITLE]); break; + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_SABOTAGE_INFANTRY_EQUIPMENT_TITLE]); break; + case RCAM_SABOTAGE_MECHANICAL_UNITS: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_SABOTAGE_MECHANICAL_UNITS_TITLE]); break; + case RCAM_TRAIN_MILITIA_ANYWHERE: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_TRAIN_MILITIA_ANYWHERE_TITLE]); break; + + default: swprintf(sText, L"Mission Index: %d", rebelCommandSaveInfo.availableMissions[index]); break; + } + DrawTextToScreen(sText, x+5, y+5, 0, FONT14ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw mission base duration, in days + UINT8 missionDurationBase = 0; + switch (rebelCommandSaveInfo.availableMissions[index]) + { + case RCAM_DEEP_DEPLOYMENT: missionDurationBase = gRebelCommandSettings.iDeepDeploymentDuration; break; + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: missionDurationBase = gRebelCommandSettings.iGetEnemyMovementTargetsDuration; break; + case RCAM_IMPROVE_LOCAL_SHOPS: missionDurationBase = gRebelCommandSettings.iImproveLocalShopsDuration; break; + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: missionDurationBase = gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration; break; + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: missionDurationBase = gRebelCommandSettings.iReduceUnalertedEnemyVisionDuration; break; + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: missionDurationBase = gRebelCommandSettings.iSabotageInfantryEquipmentDuration; break; + case RCAM_SABOTAGE_MECHANICAL_UNITS: missionDurationBase = gRebelCommandSettings.iSabotageMechanicalUnitsDuration; break; + case RCAM_TRAIN_MILITIA_ANYWHERE: missionDurationBase = gRebelCommandSettings.iTrainMilitiaAnywhereDuration; break; + + default: break; + } + // convert from hours + missionDurationBase /= 24; + swprintf(sText, L"Mission duration: %d days", missionDurationBase); + DrawTextToScreen(sText, x+5, y+21, 0, FONT10ARIALBOLD, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw mission base success chance + int missionSuccessChanceBase = 50; + switch (rebelCommandSaveInfo.availableMissions[index]) + { + case RCAM_DEEP_DEPLOYMENT: missionSuccessChanceBase = gRebelCommandSettings.iDeepDeploymentSuccessChance; break; + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: missionSuccessChanceBase = gRebelCommandSettings.iGetEnemyMovementTargetsSuccessChance; break; + case RCAM_IMPROVE_LOCAL_SHOPS: missionSuccessChanceBase = gRebelCommandSettings.iImproveLocalShopsSuccessChance; break; + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: missionSuccessChanceBase = gRebelCommandSettings.iReduceStrategicDecisionSpeedSuccessChance; break; + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: missionSuccessChanceBase = gRebelCommandSettings.iReduceUnalertedEnemyVisionSuccessChance; break; + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: missionSuccessChanceBase = gRebelCommandSettings.iSabotageInfantryEquipmentSuccessChance; break; + case RCAM_SABOTAGE_MECHANICAL_UNITS: missionSuccessChanceBase = gRebelCommandSettings.iSabotageMechanicalUnitsSuccessChance; break; + case RCAM_TRAIN_MILITIA_ANYWHERE: missionSuccessChanceBase = gRebelCommandSettings.iTrainMilitiaAnywhereSuccessChance; break; + + default: break; + } + swprintf(sText, L"Chance of success: %d%s", missionSuccessChanceBase, L"%%"); + DrawTextToScreen(sText, x+5, y+33, 0, FONT10ARIALBOLD, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw mission description + switch (rebelCommandSaveInfo.availableMissions[index]) + { + case RCAM_DEEP_DEPLOYMENT: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_DEEP_DEPLOYMENT_DESC]); break; + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_GET_ENEMY_MOVEMENT_TARGETS_DESC]); break; + case RCAM_IMPROVE_LOCAL_SHOPS: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_IMPROVE_LOCAL_SHOPS_DESC]); break; + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_REDUCE_STRATEGIC_DECISION_SPEED_DESC]); break; + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_REDUCE_UNALERTED_ENEMY_VISION_DESC]); break; + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_SABOTAGE_INFANTRY_EQUIPMENT_DESC]); break; + case RCAM_SABOTAGE_MECHANICAL_UNITS: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_SABOTAGE_MECHANICAL_UNITS_DESC]); break; + case RCAM_TRAIN_MILITIA_ANYWHERE: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_TRAIN_MILITIA_ANYWHERE_DESC]); break; + + default: swprintf(sText, L"Mission description goes here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut faucibus libero dui. Etiam facilisis posuere dictum. Etiam a velit viverra, interdum eros non, placerat lectus. Vivamus ut lorem id velit tempus auctor. Donec molestie, erat at molestie malesuada, diam purus tincidunt eros, vel hendrerit mi elit vitae leo. Suspendisse dui lectus, malesuada eu elementum at, viverra eu odio."); break; + } + DisplayWrappedString(x+5, y+45, 220, 2, FONT10ARIAL, FONT_MCOLOR_BLACK, sText, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw agent portrait + if (agentIndex[index] == mercs.size()) + { + // draw black box for face + ColorFillVideoSurfaceArea(FRAME_BUFFER, x+5, y+150+10, x+5+48, y+150+10+43, Get16BPPColor(FROMRGB(64, 64, 64))); + + // draw question mark + SetFontShadow(NO_SHADOW); + DrawTextToScreen(L"?", x+5+20, y+150+10+16, 0, FONT14HUMANIST, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, 0); + SetFontShadow(FONT_GRAY1); + + // draw name + DrawTextToScreen(L"Name: [REDACTED]", x+55, y+150+10, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw location + DrawTextToScreen(L"Location: [REDACTED]", x+55, y+150+22, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw assignment + DrawTextToScreen(L"Assignment: None", x+55, y+150+34, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw contract + DrawTextToScreen(L"Contract: ---", x+55, y+150+46, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + } + else + { + // draw face + vObjDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; + sprintf(sTemp, "FACES\\%02d.sti", gMercProfiles[mercs[agentIndex[index]]->ubProfile].ubFaceIndex); + FilenameForBPP(sTemp, vObjDesc.ImageFile); + CHECKF(AddVideoObject(&vObjDesc, &image)); + GetVideoObject(&hvObj, image); + BltVideoObject(FRAME_BUFFER, hvObj, 0, x+5, y+150+10, VO_BLT_SRCTRANSPARENCY, NULL); + + // draw name + swprintf(sText, L"Name: %s", gMercProfiles[mercs[agentIndex[index]]->ubProfile].zName); + DrawTextToScreen(sText, x+55, y+150+10, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw location + CHAR16 locationStr[128]; + GetSectorIDString(mercs[agentIndex[index]]->sSectorX, mercs[agentIndex[index]]->sSectorY, 0, locationStr, TRUE); + swprintf(sText, L"Location: %s", locationStr); + DrawTextToScreen(sText, x+55, y+150+22, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw assignment + swprintf( sText, L"Assignment: %s", pAssignmentStrings[mercs[agentIndex[index]]->bAssignment]); + DrawTextToScreen(sText, x+55, y+150+34, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw contract + const BOOLEAN fromAim = mercs[agentIndex[index]]->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC; + + if (fromAim) + { + const INT32 endTime = mercs[agentIndex[index]]->iEndofContractTime; + const INT32 worldMin = GetWorldTotalMin(); + const INT32 remaining = endTime - worldMin; + + if (remaining >= 24 * 60) + { + swprintf(sText, L"Contract: %d days", remaining / (24 * 60)); + } + else + { + swprintf(sText, L"Contract: %d hours", remaining / 60); + } + } + else + { + swprintf(sText, L"Contract: ---"); + } + DrawTextToScreen(sText, x+55, y+150+46, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + } + + // draw btns under face + btnId = CreateTextButton(L"<<", FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, x+5, y+150+54, 24, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + { + const INT8 index = MSYS_GetBtnUserData(btn, 0); + ButtonHelper(btn, reason, [btn, index]() { agentIndex[index]--; }); + }); + MSYS_SetBtnUserData(btnId, 0, index); + btnIds.push_back(btnId); + + btnId = CreateTextButton(L">>", FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, x+5+24, y+150+54, 24, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + { + const INT8 index = MSYS_GetBtnUserData(btn, 0); + ButtonHelper(btn, reason, [btn, index]() { agentIndex[index]++; }); + }); + MSYS_SetBtnUserData(btnId, 0, index); + btnIds.push_back(btnId); + + // draw agent bonus header text + swprintf(sText, L"Agent bonus:"); + DrawTextToScreen(sText, x+5, y+150+54+20+2, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw agent bonus text + UINT32 durationBonus = 0; + int durationBonusSkill = 0; + std::vector agentBonusText; + if (agentIndex[index] < static_cast(mercs.size())) + { + const MERCPROFILESTRUCT merc = gMercProfiles[mercs[agentIndex[index]]->ubProfile]; + const INT8 successBonus_expLevel = GetMissionSuccessChanceBonus(&merc); + CHAR16 successText[100]; + // stupid string hack to get the percent sign to display correctly + swprintf(successText, L"Chance of success +%d%s (%s)", successBonus_expLevel, L"%s", pShortAttributeStrings[5]); // "Lvl" + agentBonusText.push_back(successText); + + const STR16* locSkillText = gGameOptions.fNewTraitSystem ? gzMercSkillTextNew : gzMercSkillText; + INT16 intModifier; + int intModifierSkill; + FLOAT floatModifier; + int floatModifierSkill; + UINT16 extraBits; + MissionHelpers::GetMissionInfo(static_cast(rebelCommandSaveInfo.availableMissions[index]), &merc, durationBonus, floatModifier, intModifier, durationBonusSkill, floatModifierSkill, intModifierSkill, extraBits); + switch (rebelCommandSaveInfo.availableMissions[index]) + { + case RCAM_DEEP_DEPLOYMENT: + { + intModifier = max(intModifier, gRebelCommandSettings.iDeepDeploymentRangeEW); + CHAR16 rangeText[100]; + swprintf(rangeText, L"Deployment range +%d (%s)", intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(rangeText); + } + break; + + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: + { + // no special modifiers. included for completeness. + } + break; + + case RCAM_IMPROVE_LOCAL_SHOPS: + { + // no special modifiers. included for completeness. + } + break; + + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: + { + floatModifier = max(floatModifier, gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier); + floatModifier -= 1.f; + floatModifier *= 100.f; + CHAR16 rangeText[100]; + swprintf(rangeText, L"Time +%2.0f%s (%s)", floatModifier, L"%s", locSkillText[floatModifierSkill]); + agentBonusText.push_back(rangeText); + } + break; + + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: + { + floatModifier = max(floatModifier, gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier); + floatModifier *= 100.f; + CHAR16 text[100]; + swprintf(text, L"Vision -%2.0f%s (%s)", floatModifier, L"%s", locSkillText[floatModifierSkill]); + agentBonusText.push_back(text); + } + break; + + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: + { + intModifier = max(intModifier, gRebelCommandSettings.iSabotageInfantryEquipmentModifier); + CHAR16 text[100]; + swprintf(text, L"Gear quality -%d (%s)", intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(text); + } + break; + + case RCAM_SABOTAGE_MECHANICAL_UNITS: + { + intModifier = max(intModifier, gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss); + CHAR16 text[100]; + swprintf(text, L"Overall stats -%d (%s)", intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(text); + } + break; + + case RCAM_TRAIN_MILITIA_ANYWHERE: + { + intModifier = max(intModifier, 1); + CHAR16 text[100]; + swprintf(text, L"Max trainers: %d (%s)", intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(text); + } + break; + + default: break; + } + + if (durationBonus > 0) + { + CHAR16 durationText[100]; + swprintf(durationText, L"Duration +%d hours (%s)", durationBonus, locSkillText[durationBonusSkill]); + agentBonusText.push_back(durationText); + } + } + + if (agentBonusText.size() == 0) + { + DrawTextToScreen(L"None", x+10, y+150+54+20+2+11, 0, FONT10ARIAL, FONT_MCOLOR_RED, FONT_MCOLOR_BLACK, FALSE, 0); + } + else + { + for (UINT8 i = 0; i < agentBonusText.size(); ++i) + { + // the percent sign here is a hack to get it to display properly for string that need a percent sign + swprintf(sText, agentBonusText[i], L"%%"); + DrawTextToScreen(sText, x+10, y+150+54+20+2+11*(i+1), 0, FONT10ARIAL, FONT_GREEN, FONT_MCOLOR_BLACK, FALSE, 0); + } + } + + // draw "start mission" btn + swprintf(sText, L"Start Mission (%d supplies)", GetMissionCost()); + btnId = CreateTextButton(sText, FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, x, y+290, 231, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + { + const INT8 index = MSYS_GetBtnUserData(btn, 0); + ButtonHelper(btn, reason, [btn, index]() { + StartMission(index); + }); + }); + MSYS_SetBtnUserData(btnId, 0, index); + btnIds.push_back(btnId); + + return TRUE; +} + +void RenderMissionOverview() +{ + // randomised missions - preserved in bitarray (24h refresh if none selected) + // only 1 active mission? allow multiple? + // increase cost based on num active missions? + // need a ui toggle between active missions and mission select + // need to convey that different folks can go on different missions, skills provide different bonuses + // we can probably store a lot of info in the strategic event int64 since we don't need to query it that often - on load, on mission start (send event)/end (receive event) + // 24 hours to find out if success/fail, then hold agent for a few days? + // don't need to save active missions - on load game, check strategic events (I have confirmed that strategic events are loaded before rebelcommandsaveinfo) + CHAR16 sText[800]; + INT32 btnId; + + // title + RenderHeader(RCT_AGENT_OVERVIEW); + + // toggle between mission picker and active mission effects + switch (missionOverviewSubview) + { + case MOS_MISSION_LIST: + swprintf(sText, L"View active mission effects"); + break; + + case MOS_ACTIVE_MISSION_EFFECTS: + swprintf(sText, L"View available mission list"); + break; + } + btnId = CreateTextButton(sText, FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, WEBSITE_LEFT + 15, WEBSITE_TOP + 40, 470, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + { + ButtonHelper(btn, reason, []() + { + switch (missionOverviewSubview) + { + case MOS_MISSION_LIST: + missionOverviewSubview = MOS_ACTIVE_MISSION_EFFECTS; + break; + + case MOS_ACTIVE_MISSION_EFFECTS: + missionOverviewSubview = MOS_MISSION_LIST; + break; + } + }); + }); + + btnIds.push_back(btnId); + + // main body + switch (missionOverviewSubview) + { + case MOS_MISSION_LIST: + for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) + { + SetupMissionAgentBox(WEBSITE_LEFT + 15 + 240 * i, WEBSITE_TOP + 65, i); + } + break; + + case MOS_ACTIVE_MISSION_EFFECTS: + // rftr todo: run through active mission list (missionMap) and show a one-line description for each. + // need a scroll or pagination if several missions active + // otherwise, no active effects + DrawTextToScreen(L"No active missions.", WEBSITE_LEFT + 25, WEBSITE_TOP + 75, 0, FONT12ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + break; + } + + // "new missions every X hours" text + DrawTextToScreen(szRebelCommandAgentMissionsText[RCAMT_NEW_MISSIONS_AVAILABLE_TIME], WEBSITE_LEFT + 22, WEBSITE_TOP + WEBSITE_HEIGHT - 17, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); +} + +void StartMission(INT8 index) +{ + const INT32 cost = GetMissionCost(); + if (rebelCommandSaveInfo.iSupplies < cost) + { + DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, szRebelCommandText[RCT_INSUFFICIENT_FUNDS], LAPTOP_SCREEN, MSG_BOX_FLAG_OK, NULL); + return; + } + + // todo do something with agentIndex[index] and missionIndex[index] (rebelCommandSaveInfo.availableMissions[index]) + // confirmation popup + std::vector mercs; + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) + { + SOLDIERTYPE* pSoldier = MercPtrs[i]; + + if (pSoldier && pSoldier->bActive + && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) + ) + { + mercs.push_back(pSoldier); + } + } + + const MERCPROFILESTRUCT merc = gMercProfiles[mercs[agentIndex[index]]->ubProfile]; + CHAR16 text[400]; + RebelCommandAgentMissionsText missionTitle; + INT8 missionSuccessChance; + UINT8 missionDuration; + UINT32 durationBonus; + FLOAT floatModifier; + INT16 intModifier; + int durSkill; + int floatSkill; + int intSkill; + UINT16 extraBits; + + MissionHelpers::GetMissionInfo(static_cast(rebelCommandSaveInfo.availableMissions[index]), &merc, durationBonus, floatModifier, intModifier, durSkill, floatSkill, intSkill, extraBits); + switch (rebelCommandSaveInfo.availableMissions[index]) + { + case RCAM_DEEP_DEPLOYMENT: + { + missionTitle = RCAMT_DEEP_DEPLOYMENT_TITLE; + missionSuccessChance = gRebelCommandSettings.iDeepDeploymentSuccessChance; + missionDuration = gRebelCommandSettings.iDeepDeploymentDuration; + } + break; + + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: + { + missionTitle = RCAMT_GET_ENEMY_MOVEMENT_TARGETS_TITLE; + missionSuccessChance = gRebelCommandSettings.iGetEnemyMovementTargetsSuccessChance; + missionDuration = gRebelCommandSettings.iGetEnemyMovementTargetsDuration; + } + break; + + case RCAM_IMPROVE_LOCAL_SHOPS: + { + missionTitle = RCAMT_IMPROVE_LOCAL_SHOPS_TITLE; + missionSuccessChance = gRebelCommandSettings.iImproveLocalShopsSuccessChance; + missionDuration = gRebelCommandSettings.iImproveLocalShopsDuration; + } + break; + + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: + { + missionTitle = RCAMT_REDUCE_STRATEGIC_DECISION_SPEED_TITLE; + missionSuccessChance = gRebelCommandSettings.iReduceStrategicDecisionSpeedSuccessChance; + missionDuration = gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration; + } + break; + + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: + { + missionTitle = RCAMT_REDUCE_UNALERTED_ENEMY_VISION_TITLE; + missionSuccessChance = gRebelCommandSettings.iReduceUnalertedEnemyVisionSuccessChance; + missionDuration = gRebelCommandSettings.iReduceUnalertedEnemyVisionDuration; + } + break; + + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: + { + missionTitle = RCAMT_SABOTAGE_INFANTRY_EQUIPMENT_TITLE; + missionSuccessChance = gRebelCommandSettings.iSabotageInfantryEquipmentSuccessChance; + missionDuration = gRebelCommandSettings.iSabotageInfantryEquipmentDuration; + } + break; + + case RCAM_SABOTAGE_MECHANICAL_UNITS: + { + missionTitle = RCAMT_SABOTAGE_MECHANICAL_UNITS_TITLE; + missionSuccessChance = gRebelCommandSettings.iSabotageMechanicalUnitsSuccessChance; + missionDuration = gRebelCommandSettings.iSabotageMechanicalUnitsDuration; + } + break; + + case RCAM_TRAIN_MILITIA_ANYWHERE: + { + missionTitle = RCAMT_TRAIN_MILITIA_ANYWHERE_TITLE; + missionSuccessChance = gRebelCommandSettings.iTrainMilitiaAnywhereSuccessChance; + missionDuration = gRebelCommandSettings.iTrainMilitiaAnywhereDuration; + } + break; + + default: break; + } + + missionSuccessChance += GetMissionSuccessChanceBonus(&merc); + + if (Random(100) > missionSuccessChance) + { + // mission failed! + missionDuration = 0; + } + else + { + missionDuration += durationBonus; + } + + swprintf(text, L"[%s (%d supplies)]", szRebelCommandAgentMissionsText[missionTitle], cost); + + if (agentIndex[index] == mercs.size()) + { + // sent a generic rebel + MissionHelpers::missionParam = SerialiseMissionFirstEvent(TRUE, 0 /* no profile needed */, static_cast(rebelCommandSaveInfo.availableMissions[index]), missionDuration, 0 /* no extra data */); + swprintf(text, L"%s Send a rebel agent to prepare this mission?", text); + } + else + { + MissionHelpers::missionParam = SerialiseMissionFirstEvent(FALSE, agentIndex[index], static_cast(rebelCommandSaveInfo.availableMissions[index]), missionDuration, static_cast(extraBits)); + if (merc.bSex == MALE) + swprintf(text, L"%s Send %s to prepare this mission? He will return in 24 hours.", text, merc.zNickname); + else + swprintf(text, L"%s Send %s to prepare this mission? She will return in 24 hours.", text, merc.zNickname); + } + + DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, text, LAPTOP_SCREEN, MSG_BOX_FLAG_YESNO, [](UINT8 exitValue) { + if (exitValue == MSG_BOX_RETURN_YES) + { + MissionFirstEvent evt; + DeserialiseMissionFirstEvent(MissionHelpers::missionParam, evt); + + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) + { + SOLDIERTYPE* pSoldier = MercPtrs[i]; + + if (pSoldier->ubProfile != evt.mercProfileId) + continue; + + // rftr todo: send selected merc on assignment, like mini event adventure + for (INT8 i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) + { + if (evt.missionId == rebelCommandSaveInfo.availableMissions[i]) + { + rebelCommandSaveInfo.availableMissions[i] = RCAM_NONE; + break; + } + } + + // actually start the mission + AddStrategicEvent(EVENT_REBELCOMMAND, GetWorldTotalMin() + 60 * 24, MissionHelpers::missionParam); + missionMap[static_cast(evt.missionId)] = MissionHelpers::missionParam; + + return; + } + } + }); +} // end website void ApplyEnemyPenalties(SOLDIERTYPE* pSoldier) @@ -1560,7 +2712,7 @@ void ApplyEnemyPenalties(SOLDIERTYPE* pSoldier) continue; // and that it's not level 0 - const UINT8 level = rebelCommandSaveInfo.regions[a].actionLevels[index]; + const UINT8 level = rebelCommandSaveInfo.regions[a].GetLevel(index); if (level == 0) continue; @@ -1572,7 +2724,7 @@ void ApplyEnemyPenalties(SOLDIERTYPE* pSoldier) sectors.push_back(std::tuple(x, y, GetRegionLoyalty(a))); // check if soldier is within range of the city - for (const auto tuple : sectors) + for (const auto& tuple : sectors) { const INT16 x = std::get<0>(tuple); const INT16 y = std::get<1>(tuple); @@ -1639,7 +2791,7 @@ FLOAT GetAssignmentBonus(INT16 x, INT16 y) if (index >= 0) { - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); value += info.adminActions[RCAA_MERC_SUPPORT].fValue1 * level; } @@ -1659,7 +2811,7 @@ INT32 GetMiningPolicyBonus(INT16 townId) if (index >= 0) { - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); return static_cast(info.adminActions[RCAA_MINING_POLICY].fValue1 * level); } @@ -1738,7 +2890,7 @@ void GetBonusMilitia(INT16 sx, INT16 sy, UINT8& green, UINT8& regular, UINT8& el continue; // and that it's not level 0 - const UINT8 level = rebelCommandSaveInfo.regions[a].actionLevels[index]; + const UINT8 level = rebelCommandSaveInfo.regions[a].GetLevel(index); if (level == 0) continue; @@ -1750,7 +2902,7 @@ void GetBonusMilitia(INT16 sx, INT16 sy, UINT8& green, UINT8& regular, UINT8& el sectors.push_back(std::tuple(x, y, GetRegionLoyalty(a))); // check if sector is within range of the city - for (const auto tuple : sectors) + for (const auto& tuple : sectors) { const INT16 x = std::get<0>(tuple); const INT16 y = std::get<1>(tuple); @@ -1801,7 +2953,7 @@ INT16 GetFortificationsBonus(UINT8 sector) return 0; // no levels in region - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); if (level == 0) return 0; @@ -1838,7 +2990,7 @@ FLOAT GetHarriersSpeedPenalty(UINT8 sector) continue; // no levels in region - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); if (level == 0) continue; @@ -1848,7 +3000,7 @@ FLOAT GetHarriersSpeedPenalty(UINT8 sector) // run through townSectors to find the biggest harriers penalty BOOLEAN found = FALSE; - for (const auto trio : townSectors) + for (const auto& trio : townSectors) { const INT16 sx = std::get<0>(trio); const INT16 sy = std::get<1>(trio); @@ -1940,7 +3092,7 @@ void HandleScouting() continue; // no levels in region - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); if (level == 0) continue; @@ -1954,7 +3106,7 @@ void HandleScouting() { for (int y = MINIMUM_VALID_Y_COORDINATE; y <= MAXIMUM_VALID_Y_COORDINATE; ++y) { - for (const auto trio : townSectors) + for (const auto& trio : townSectors) { const INT16 sx = std::get<0>(trio); const INT16 sy = std::get<1>(trio); @@ -2004,7 +3156,7 @@ FLOAT GetPathfindersSpeedBonus(UINT8 sector) continue; // no levels in region - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); if (level == 0) continue; @@ -2014,7 +3166,7 @@ FLOAT GetPathfindersSpeedBonus(UINT8 sector) // run through townSectors to find the biggest pathfinders bonus BOOLEAN found = FALSE; - for (const auto trio : townSectors) + for (const auto& trio : townSectors) { const INT16 sx = std::get<0>(trio); const INT16 sy = std::get<1>(trio); @@ -2068,7 +3220,9 @@ void RaidMines(INT32 &playerIncome, INT32 &enemyIncome) INT32 stolenIncome = static_cast(enemyIncome * rebelCommandSaveInfo.directives[RCD_RAID_MINES].GetValue1() * Random(100) / 100.f); playerIncome += stolenIncome; - enemyIncome -= stolenIncome; + enemyIncome -= stolenIncome*2; + + if (enemyIncome < 0) enemyIncome = 0; if (stolenIncome > 0) { @@ -2265,15 +3419,20 @@ void DailyUpdate() rebelCommandSaveInfo.iActiveDirective = directive; // increment supplies - const UINT8 progress = CurrentPlayerProgressPercentage(); - iIncomingSuppliesPerDay = static_cast(progress * gRebelCommandSettings.fIncomeModifier + (directive == RCD_GATHER_SUPPLIES ? rebelCommandSaveInfo.directives[RCD_GATHER_SUPPLIES].GetValue1() : 0)); + iIncomingSuppliesPerDay = CalcIncomingSuppliesPerDay(static_cast(directive)); rebelCommandSaveInfo.iSupplies += iIncomingSuppliesPerDay; + // rftr todo: check bitmask + //if (purchasing supplies through agent mission) + // what gets subtracted? current balance? daily income? how do we pick? + // get regional bonuses + const INT32 supplyUpkeep = static_cast(gRebelCommandSettings.fIncomeModifier + 0.5f); INT16 intelGain = 0; INT16 supplyGain = 0; INT16 moneyGain = 0; - for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) + CHAR16 text[200]; + for (int a = FIRST_TOWN+1; a < NUM_TOWNS; ++a) { // check to see if the town is lost if (IsTownUnderCompleteControlByEnemy(a) && rebelCommandSaveInfo.regions[a].adminStatus == RAS_ACTIVE) @@ -2302,27 +3461,51 @@ void DailyUpdate() const UINT8 loyalty = GetRegionLoyalty(a); for (int b = 0; b < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++b) { - const INT8 level = rebelCommandSaveInfo.regions[a].actionLevels[b]; + const INT8 level = rebelCommandSaveInfo.regions[a].GetLevel(b); + if (level == 0) continue; + + if (CanAdminActionBeToggled(rebelCommandSaveInfo.regions[a].actions[b]) && !rebelCommandSaveInfo.regions[a].IsActive(b)) continue; + switch (static_cast(rebelCommandSaveInfo.regions[a].actions[b])) { case RCAA_SUPPLY_LINE: - case RCAA_SAFEHOUSES: case RCAA_SUPPLY_DISRUPTION: case RCAA_SCOUTS: case RCAA_MERC_SUPPORT: - case RCAA_MINING_POLICY: case RCAA_PATHFINDERS: - case RCAA_HARRIERS: case RCAA_FORTIFICATIONS: // no daily bonuses break; + case RCAA_HARRIERS: + case RCAA_MINING_POLICY: + case RCAA_SAFEHOUSES: + // no daily bonuses, but gotta pay upkeep + if (rebelCommandSaveInfo.iSupplies > supplyUpkeep) + { + // what to do on success? + } + else + { + swprintf(text, L"Not enough supplies for harriers/miningpolicy/safehouses!"); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text); + } + break; + case RCAA_REBEL_RADIO: IncrementTownLoyalty(a, static_cast(info.adminActions[RCAA_REBEL_RADIO].fValue1 * level)); break; case RCAA_DEAD_DROPS: - intelGain += Random(static_cast(info.adminActions[RCAA_DEAD_DROPS].fValue1 * level * loyalty / 100.f)); + if (rebelCommandSaveInfo.iSupplies > supplyUpkeep) + { + intelGain += Random(static_cast(info.adminActions[RCAA_DEAD_DROPS].fValue1 * level * loyalty / 100.f)); + } + else + { + swprintf(text, L"Not enough supplies for dead drops"); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text); + } break; case RCAA_SMUGGLERS: @@ -2330,10 +3513,18 @@ void DailyUpdate() break; case RCAA_WAREHOUSES: - AddResources( - static_cast(info.adminActions[RCAA_WAREHOUSES].fValue1 * level * Random(100) * loyalty / 10000.f), - static_cast(info.adminActions[RCAA_WAREHOUSES].fValue2 * level * Random(100) * loyalty / 10000.f), - static_cast(info.adminActions[RCAA_WAREHOUSES].fValue3 * level * Random(100) * loyalty / 10000.f)); + if (rebelCommandSaveInfo.iSupplies > supplyUpkeep) + { + AddResources( + static_cast(info.adminActions[RCAA_WAREHOUSES].fValue1 * level * Random(100) * loyalty / 10000.f), + static_cast(info.adminActions[RCAA_WAREHOUSES].fValue2 * level * Random(100) * loyalty / 10000.f), + static_cast(info.adminActions[RCAA_WAREHOUSES].fValue3 * level * Random(100) * loyalty / 10000.f)); + } + else + { + swprintf(text, L"Not enough supplies for warehouses"); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text); + } break; case RCAA_TAXES: @@ -2353,7 +3544,6 @@ void DailyUpdate() } } - CHAR16 text[200]; if (intelGain > 0) { swprintf(text, szRebelCommandText[RCT_DEAD_DROP_INCOME], intelGain); @@ -2374,6 +3564,46 @@ void DailyUpdate() { AddTransactionToPlayersBook(REBEL_COMMAND, 0, GetWorldTotalMin(), moneyGain); } + + // update missions + // rftr todo: test me + // rftr todo: don't do this every day! + std::unordered_set validMissions; + for (int i = 0; i < RCAM_NUM_MISSIONS; ++i) + { + validMissions.insert(static_cast(i)); + } + + for (const auto& pair : missionMap) + { + const RebelCommandAgentMissions mission = pair.first; + validMissions.erase(mission); + } + + if (validMissions.size() >= NUM_ARC_AGENT_SLOTS) + { + for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) + { + const INT8 mission = static_cast(Random(validMissions.size())); + rebelCommandSaveInfo.availableMissions[i] = mission; + validMissions.erase(static_cast(mission)); + } + } + else + { + int idx = 0; + for (auto iter = validMissions.cbegin(); iter != validMissions.cend(); ++iter) + { + rebelCommandSaveInfo.availableMissions[idx] = *iter; + idx++; + } + + while (idx < NUM_ARC_AGENT_SLOTS) + { + rebelCommandSaveInfo.availableMissions[idx] = RCAM_NONE; + idx++; + } + } } void HourlyUpdate() @@ -2510,6 +3740,42 @@ BOOLEAN Load(HWFILE file) Init(); } + // rftr TEST it works hooray, assuming I don't change the layout of the bitflag + //const UINT32 param1 = SerialiseMissionFirstEvent(FALSE, 8, RCAM_DEEP_DEPLOYMENT, 200, 0x0F); + //const UINT32 param2 = SerialiseMissionSecondEvent(FALSE, 10, RCAM_GET_ENEMY_MOVEMENT_TARGETS, 0xFF); + //MissionFirstEvent evt1; + //MissionSecondEvent evt2; + //DeserialiseMissionFirstEvent(param1, evt1); + //DeserialiseMissionSecondEvent(param2, evt2); + + // go through every strategic event to find active agent missions + std::vector> missions = GetAllStrategicEventsOfType(EVENT_REBELCOMMAND); + missionMap.clear(); + // example is EVENT_BLOODCAT_ATTACK + for (std::vector>::iterator it = missions.begin(); it != missions.end(); ++it) + { + // FIRST EVENT EXAMPLE + //AddStrategicEvent(EVENT_REBELCOMMAND, GetWorldTotalMin() + 60 * 24, my32bitparam); + // SECOND EVENT EXAMPLE + //AddStrategicEvent(EVENT_REBELCOMMAND, GetWorldTotalMin() + 60 * durationInHours, my32bitparam); + //const UINT32 first = it->first; // timestamp - when this event will be fired + //const UINT32 second = it->second; // uiParam + + MissionFirstEvent evt1; + DeserialiseMissionFirstEvent(it->second, evt1); + if (evt1.isFirstEvent) + { + missionMap[static_cast(evt1.missionId)] = it->second; + } + + MissionSecondEvent evt2; + DeserialiseMissionSecondEvent(it->second, evt2); + + if (evt2.isSecondEvent) + { + missionMap[static_cast(evt2.missionId)] = it->second; + } + } return TRUE; } @@ -2667,6 +3933,87 @@ void SetupInfo() aa.fValue3 = 0.f; info.adminActions.insert(info.adminActions.begin() + RCAA_FORTIFICATIONS, aa); + MissionHelpers::missionInfo.clear(); + // example format + // { + // { new skill traits to check }, + // { old skill traits to check. use -1 to not match against anything }, + // { duration bonus for checked trait }, + // { float modifier for checked trait. either this line or the next is zeroed out. }, + // { int modifier for checked trait. either this line or the previous is zeroed out. }, + // { value to place in extra bits, used to determine what bonus is applied. } + // } + //RCAM_DEEP_DEPLOYMENT + MissionHelpers::missionInfo.push_back( + { + {COVERT_NT, SCOUTING_NT, STEALTHY_NT, SURVIVAL_NT}, + {-1, -1, STEALTHY_OT, CAMOUFLAGED_OT}, + {gRebelCommandSettings.iDeepDeploymentDuration_Bonus_Covert, gRebelCommandSettings.iDeepDeploymentDuration_Bonus_Scouting, gRebelCommandSettings.iDeepDeploymentDuration_Bonus_Stealthy, gRebelCommandSettings.iDeepDeploymentDuration_Bonus_Survival}, + {0.f, 0.f, 0.f, 0.f}, + {gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Covert, gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Scouting, gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Stealthy, gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Survival}, + {MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_COVERT, MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_SCOUTING, MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_STEALTHY, MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_SURVIVAL} + }); + //RCAM_GET_ENEMY_MOVEMENT_TARGETS + MissionHelpers::missionInfo.push_back( + { + {COVERT_NT, RADIO_OPERATOR_NT}, + {-1, -1}, + {gRebelCommandSettings.iGetEnemyMovementTargetsDuration_Bonus_Covert, gRebelCommandSettings.iGetEnemyMovementTargetsDuration_Bonus_Radio}, + {0.f, 0.f}, + {0, 0}, + {0, 0} + }); + //RCAM_IMPROVE_LOCAL_SHOPS + MissionHelpers::missionInfo.push_back({ }); // no entries necessary - no modifiers + //RCAM_REDUCE_STRATEGIC_DECISION_SPEED + MissionHelpers::missionInfo.push_back( + { + {COVERT_NT, SQUADLEADER_NT, SNITCH_NT}, + {-1, -1, -1}, + {gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration_Bonus_Covert, gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration_Bonus_Deputy, gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration_Bonus_Snitch}, + {gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Covert, gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Deputy, gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Snitch}, + {0, 0, 0}, + {MissionHelpers::REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_COVERT, MissionHelpers::REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_DEPUTY, MissionHelpers::REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_SNITCH} + }); + //RCAM_REDUCE_UNALERTED_ENEMY_VISION + MissionHelpers::missionInfo.push_back( + { + {COVERT_NT, RADIO_OPERATOR_NT, STEALTHY_NT}, + {-1, -1, STEALTHY_OT}, + {gRebelCommandSettings.iReduceUnalertedEnemyVisionDuration_Bonus_Covert, gRebelCommandSettings.iReduceUnalertedEnemyVisionDuration_Bonus_Radio, 0}, + {gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Covert, gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Radio, gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Stealthy}, + {0, 0, 0}, + {MissionHelpers::REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_COVERT, MissionHelpers::REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_RADIO, MissionHelpers::REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_STEALTHY} + }); + //RCAM_SABOTAGE_INFANTRY_EQUIPMENT + MissionHelpers::missionInfo.push_back({ + {AUTO_WEAPONS_NT, COVERT_NT, DEMOLITIONS_NT, GUNSLINGER_NT, RANGER_NT, SNIPER_NT }, + {AUTO_WEAPS_OT, -1, -1, -1, -1, PROF_SNIPER_OT }, + {gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Auto_Weapons, gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Covert, gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Demolitions, gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Gunslinger, gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Ranger, gRebelCommandSettings.iSabotageInfantryEquipmentDuration_Bonus_Sniper}, + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Auto_Weapons, gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Covert, gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Demolitions, gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Gunslinger, gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Ranger, gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Sniper}, + {MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_AUTO_WEAPONS, MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_COVERT, MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_DEMOLITIONS, MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_GUNSLINGER, MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_RANGER, MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_SNIPER} + }); + //RCAM_SABOTAGE_MECHANICAL_UNITS + MissionHelpers::missionInfo.push_back( + { + {COVERT_NT, DEMOLITIONS_NT, HEAVY_WEAPONS_NT}, + {-1, -1, HEAVY_WEAPS_OT}, + {gRebelCommandSettings.iSabotageMechanicalUnitsDuration_Bonus_Covert, gRebelCommandSettings.iSabotageMechanicalUnitsDuration_Bonus_Demolitions, gRebelCommandSettings.iSabotageMechanicalUnitsDuration_Bonus_Heavy_Weapons}, + {0.f, 0.f, 0.f}, + {gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Covert, gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Demolitions, gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Heavy_Weapons}, + {MissionHelpers::SABOTAGE_MECHANICAL_UNITS_COVERT, MissionHelpers::SABOTAGE_MECHANICAL_UNITS_DEMOLITIONS, MissionHelpers::SABOTAGE_MECHANICAL_UNITS_HEAVY_WEAPONS} + }); + //RCAM_TRAIN_MILITIA_ANYWHERE + MissionHelpers::missionInfo.push_back( + { + {COVERT_NT, SURVIVAL_NT, TEACHING_NT}, + {-1, CAMOUFLAGED_OT, TEACHING_OT}, + {gRebelCommandSettings.iTrainMilitiaAnywhereDuration_Bonus_Covert, gRebelCommandSettings.iTrainMilitiaAnywhereDuration_Bonus_Survival, gRebelCommandSettings.iTrainMilitiaAnywhereDuration_Bonus_Teaching}, + {0.f, 0.f, 0.f}, + {gRebelCommandSettings.iTrainMilitiaAnywhereMaxTrainers, gRebelCommandSettings.iTrainMilitiaAnywhereMaxTrainers, gRebelCommandSettings.iTrainMilitiaAnywhereMaxTrainers_Teaching}, + {0, 0, MissionHelpers::TRAIN_MILITIA_ANYWHERE_TEACHING} + }); } void UpgradeMilitiaStats() @@ -2692,6 +4039,313 @@ void UpgradeMilitiaStats() }); } +void ApplyEnemyMechanicalUnitPenalties(SOLDIERTYPE* pSoldier) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + // rftr todo: check agent mission bitmask + + pSoldier->stats.bLife -= 40; + pSoldier->stats.bLifeMax = pSoldier->stats.bLife; + pSoldier->stats.bAgility -= 40; + pSoldier->stats.bDexterity -= 40; + pSoldier->stats.bStrength -= 40; + pSoldier->stats.bMarksmanship -= 40; + + pSoldier->stats.bLife = max(33, pSoldier->stats.bLife); + pSoldier->stats.bLifeMax = max(33, pSoldier->stats.bLifeMax); + pSoldier->stats.bAgility = max(33, pSoldier->stats.bAgility); + pSoldier->stats.bDexterity = max(33, pSoldier->stats.bDexterity); + pSoldier->stats.bStrength = max(33, pSoldier->stats.bStrength); + pSoldier->stats.bMarksmanship = max(33, pSoldier->stats.bMarksmanship); +} + +void ApplyMilitiaTraits(SOLDIERTYPE* pSoldier) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + // rftr todo: check bitmask for specific possible traits +} + +void ApplyVisionModifier(const SOLDIERTYPE* pSoldier, INT32& sight) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + // rftr todo: check bitmask + + if (pSoldier->bTeam == ENEMY_TEAM && pSoldier->aiData.bAlertStatus == STATUS_GREEN) + { + sight = static_cast(sight * (1.f - 0.15f)); + } +} + +BOOLEAN CanAssignTraitsToMilitia() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return FALSE; + + // rftr todo: check bitmask + + return TRUE; +} + +BOOLEAN CanTrainMilitiaAnywhere() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return FALSE; + + // rftr todo: check bitmask + return TRUE; +} + +UINT8 GetMaxTrainersForTrainMilitiaAnywhere() +{ + return 1; +} + +INT16 GetAdditionalDeployRange(const UINT8 insertionCode) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0; + + const std::unordered_map::iterator iter = missionMap.find(RCAM_DEEP_DEPLOYMENT); + + if (iter == missionMap.end()) + return 0; + + // rftr todo: check bitmask + + switch (insertionCode) + { + case INSERTION_CODE_NORTH: + case INSERTION_CODE_SOUTH: + { + INT16 range = 0; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + // we only get a range bonus if the mission is active! + if (evt.isSecondEvent) + { + range = gRebelCommandSettings.iDeepDeploymentRangeNS; + + // rftr todo: bitmagic to get bonus range + switch (evt.extraBits) + { + case MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_COVERT: range += gRebelCommandSettings.iDeepDeploymentRangeNS_Bonus_Covert; break; + case MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_SCOUTING: range += gRebelCommandSettings.iDeepDeploymentRangeNS_Bonus_Scouting; break; + case MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_STEALTHY: range += gRebelCommandSettings.iDeepDeploymentRangeNS_Bonus_Stealthy; break; + case MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_SURVIVAL: range += gRebelCommandSettings.iDeepDeploymentRangeNS_Bonus_Survival; break; + + default: break; + } + } + + return range; + } + + case INSERTION_CODE_WEST: + case INSERTION_CODE_EAST: + { + INT16 range = 0; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + // we only get a range bonus if the mission is active! + if (evt.isSecondEvent) + { + range = gRebelCommandSettings.iDeepDeploymentRangeEW; + + switch (evt.extraBits) + { + case MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_COVERT: range += gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Covert; break; + case MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_SCOUTING: range += gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Scouting; break; + case MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_STEALTHY: range += gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Stealthy; break; + case MissionHelpers::DEEP_DEPLOYMENT_RANGE_BONUS_SURVIVAL: range += gRebelCommandSettings.iDeepDeploymentRangeEW_Bonus_Survival; break; + + default: break; + } + } + + return range; + } + } + + return 0; +} + +INT8 GetEnemyEquipmentCoolnessModifier() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0; + + // rftr todo: check bitmask + + return -1; +} + +INT8 GetEnemyEquipmentStatusModifier(const INT8 initialStatus) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return initialStatus; + + // rftr todo: check bitmask + + INT8 newStatus = initialStatus; + newStatus -= 10; + + newStatus = max(1, min(newStatus, 100)); + return newStatus; +} + +UINT8 GetMerchantCoolnessBonus() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0; + + const std::unordered_map::iterator iter = missionMap.find(RCAM_IMPROVE_LOCAL_SHOPS); + + if (iter == missionMap.end()) + return 0; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + // rftr todo: check bitmask + + return evt.isSecondEvent ? 1 : 0; +} + +FLOAT GetStrategicDecisionSpeedModifier() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 1.f; + + const std::unordered_map::iterator iter = missionMap.find(RCAM_REDUCE_STRATEGIC_DECISION_SPEED); + + if (iter == missionMap.end()) + return 0; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + // rftr todo: check bitmask + + FLOAT modifier = 1.f; + + if (evt.isSecondEvent) + { + switch (evt.extraBits) + { + case MissionHelpers::REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_COVERT: modifier = gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Covert; break; + default: modifier = gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier; break; + } + } + + return modifier; +} + +void HandleStrategicEvent(const UINT32 eventParam) +{ + // this handles the transition from "mission prep" (first event) to "mission active" (second event), which happens 24 hours after the player clicks on "start mission" + // rftr todo: update agent mission bitmask + MissionFirstEvent evt1; + MissionSecondEvent evt2; + DeserialiseMissionFirstEvent(eventParam, evt1); + DeserialiseMissionSecondEvent(eventParam, evt2); + + if (evt1.isFirstEvent) + { + // mission prep is over. see if we can activate the mission + missionMap.erase(static_cast(evt1.missionId)); + + // rftr todo: end the merc's assignment (like mini event adventures) + + if (evt1.isMissionSuccess) + { + const RebelCommandAgentMissions mission = static_cast(evt1.missionId); + const MERCPROFILESTRUCT merc = gMercProfiles[evt1.mercProfileId]; + + // rftr todo: mission successful! give some experience pts + + // what mission did we do? apply bonuses here, and don't forget to check them later when checking to see if a mission bonus should be applied + UINT32 durationBonus = 0; + int durationBonusSkill = 0; + INT16 intModifier = 0; + int intModifierSkill = 0; + FLOAT floatModifier = 0.f; + int floatModifierSkill = 0; + UINT16 extraBits = 0; + MissionHelpers::GetMissionInfo(mission, &merc, durationBonus, floatModifier, intModifier, durationBonusSkill, floatModifierSkill, intModifierSkill, extraBits); + + BOOLEAN validMission = FALSE; + switch (mission) + { + case RCAM_DEEP_DEPLOYMENT: + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: + case RCAM_IMPROVE_LOCAL_SHOPS: + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: + case RCAM_SABOTAGE_MECHANICAL_UNITS: + case RCAM_TRAIN_MILITIA_ANYWHERE: + { + validMission = TRUE; + } + break; + + default: + { + ScreenMsg(FONT_MCOLOR_RED, MSG_INTERFACE, L"Unrecognised mission ID: %d", mission); + } + break; + } + + if (validMission) + { + AddStrategicEvent(EVENT_REBELCOMMAND, GetWorldTotalMin() + 60 * evt1.missionDurationInHours, + SerialiseMissionSecondEvent(evt1.sentGenericRebelAgent, evt1.mercProfileId, mission, extraBits)); + + // rftr todo: tell the player that the mission has started. popupbox or screenmsg? + } + + missionMap[mission] = eventParam; + } + else + { + // rftr todo: tell the player that the mission prep failed. some popup box blurb or somesuch. + } + } + else if (evt2.isSecondEvent) + { + // mission duration is over. deactivate the mission + missionMap.erase(static_cast(evt2.missionId)); + } +} + +BOOLEAN ShowEnemyMovementTargets() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return FALSE; + + const std::unordered_map::iterator iter = missionMap.find(RCAM_GET_ENEMY_MOVEMENT_TARGETS); + + if (iter == missionMap.end()) + return FALSE; + + // rftr todo: check bitmask + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + return evt.isSecondEvent; +} + void DEBUG_DAY() { DailyUpdate(); @@ -2716,7 +4370,7 @@ template<> void DropDownTemplate::SetRefresh() using namespace RebelCommand; const INT16 newDirective = REBEL_COMMAND_DROPDOWN.GetSelectedEntryKey(); rebelCommandSaveInfo.iSelectedDirective = newDirective; - iIncomingSuppliesPerDay = static_cast(CurrentPlayerProgressPercentage() * gRebelCommandSettings.fIncomeModifier + (newDirective == RCD_GATHER_SUPPLIES ? rebelCommandSaveInfo.directives[RCD_GATHER_SUPPLIES].GetValue1() : 0)); + iIncomingSuppliesPerDay = CalcIncomingSuppliesPerDay(static_cast(newDirective)); redraw = TRUE; } diff --git a/Strategic/Rebel Command.h b/Strategic/Rebel Command.h index 8b4e24f6..f3b56d95 100644 --- a/Strategic/Rebel Command.h +++ b/Strategic/Rebel Command.h @@ -6,9 +6,43 @@ #include "Types.h" #define REBEL_COMMAND_MAX_ACTIONS_PER_REGION 6 +#define NUM_ARC_AGENT_SLOTS 2 namespace RebelCommand { + // applies to RegionSaveInfo.actionLevels: use the MSB as the active flag since I don't expect it to be used otherwise + constexpr UINT8 ADMIN_ACTION_ACTIVE_BIT = 1 << 7; + + // for rebelCommandSaveInfo.uBitMask + // requirement breakdown + // 1 bit for notification toggle + // 5 * 4 bits for random mission selection (2off, 2def) = 32 choices each + // need some bits to track mission area (friendly/hostile? num towns?) + // remaining bits to track up/downgrades? + // some should be ASD specific? + // can we not do this and just check strategic events? GetAllStrategicEventsOfType(). could be costly. iterates over event LL on each call + // can we cache in-flight events on load? no real need to save this, right? + //constexpr UINT64 AGENT_NOTIFICATION_BIT = 1 << 0; + + // the FIRST and SECOND strategic events share the same type (EVENT_REBELCOMMAND - no need to have multiple types yet, I think...) + // so the 32-bit int will have to block out information for both events + // FIRST EVENT BREAKDOWN + // A######B CCCCCCCC DDDDDDDD EEEEEEEE + // A (1 bit) - always 0 to indicate that this is the FIRST event (ie, keeping a merc busy for a set duration) + // B (1 bit) - 0 if the player sent a generic rebel agent, 1 if the player sent one of their own mercs + // C (8 bit) - the profile number of the merc that was sent. invalid if B == 0 + // D (8 bit) - the mission ID. should match up with RebelCommandAgentMissions enum + // E (8 bit) - the mission duration, in hours. if 0, mission failed. + // the rest of the bits depend on the mission? + // + + // SECOND EVENT BREAKDOWN + // A####### #######B CCCCCCCC DDDDDDDD + // A (1 bit) - always 1 to indicate that this is the SECOND event (ie, this event fires when the mission bonus expires) + // B (1 bit) - 0 if the player sent a generic rebel agent, 1 if the player sent one of their own mercs + // C (8 bit) - the profile number of the merc that was sent. invalid if B == 0 + // D (8 bit) - the mission ID. should match up with RebelCommandAgentMissions enum + // extra bits are for ??????????? enum RebelCommandDirectives { @@ -49,6 +83,36 @@ enum RebelCommandAdminActions RCAA_NUM_ACTIONS }; +enum RebelCommandAgentMissions +{ + RCAM_NONE = -1, + RCAM_DEEP_DEPLOYMENT = 0, + RCAM_GET_ENEMY_MOVEMENT_TARGETS, // aka Strategic Intel + RCAM_IMPROVE_LOCAL_SHOPS, + RCAM_REDUCE_STRATEGIC_DECISION_SPEED, // aka Slower Strategic Decisions + RCAM_REDUCE_UNALERTED_ENEMY_VISION, // aka Lower Readiness + RCAM_SABOTAGE_INFANTRY_EQUIPMENT, // aka Sabotage Equipment + RCAM_SABOTAGE_MECHANICAL_UNITS, // aka Sabotage Vehicles + RCAM_TRAIN_MILITIA_ANYWHERE, + + RCAM_NUM_MISSIONS, + + // ideas/unimplemented - rename some? + // need to have permanent and temporary boons + RCAM_SEND_SUPPLIES_TO_TOWN, + RCAM_BOOST_TOWN_ADMIN_ACTIONS, + RCAM_PROCURE_ITEMS, + RCAM_MILITIA_SKILL_TRAITS, // should override militia skill traits ini option - split into multiple (weapon spec, bodybuilding, athletic, night ops) + RCAM_OBSERVE_SECTORS, // ??? competes with scouts? + RCAM_PURCHASE_SUPPLIES, // increase daily supply income, decrease daily $ income + RCAM_SABOTAGE_ASD, // see ASD.cpp for things that can be played with + RCAM_SABOTAGE_MINE, + RCAM_REDUCE_ENEMY_POOL, // need to make sure enemy pool is not infinite // giReinforcementPool, also gfUnlimitedTroops = zDiffSetting[gGameOptions.ubDifficultyLevel].bUnlimitedPoolOfTroops; + // militia/mercs get bonus vision (???) + // share vision with civilians? + +}; + enum RegionAdminStatus { RAS_NONE, @@ -97,8 +161,13 @@ typedef struct RegionSaveInfo UINT8 actionLevels[REBEL_COMMAND_MAX_ACTIONS_PER_REGION]; UINT8 ubMaxLoyalty; + BOOLEAN IsActive(UINT8 index) { return (actionLevels[index] & ADMIN_ACTION_ACTIVE_BIT) == 0; } + // rftr: I know these fly in the face of convention, but I'm lazy and this preserves savegame compatibility without needing to add any additional code + void SetActive(UINT8 index) { actionLevels[index] &= ~ADMIN_ACTION_ACTIVE_BIT; } // active bit value = 0 + void SetInactive(UINT8 index) { actionLevels[index] |= ADMIN_ACTION_ACTIVE_BIT; } // inactive bit value = 1 INT32 GetAdminDeployCost(INT16 numAdminTeams) { return 10 * numAdminTeams * numAdminTeams; }; INT32 GetAdminReactivateCost(INT16 numAdminTeams) { return GetAdminDeployCost(numAdminTeams) / 4; }; + UINT8 GetLevel(INT16 index) { return actionLevels[index] & ~ADMIN_ACTION_ACTIVE_BIT; } } RegionSaveInfo; typedef struct SaveInfo @@ -110,9 +179,10 @@ typedef struct SaveInfo INT32 iActiveDirective; INT32 iSelectedDirective; INT8 iMilitiaStatsLevel; - UINT8 uSupplyDropCount; + UINT8 uSupplyDropCount; // keeping this around for compatibility with old saves + INT8 availableMissions[NUM_ARC_AGENT_SLOTS]; - INT8 filler[19]; + INT8 filler[17]; } SaveInfo; extern SaveInfo rebelCommandSaveInfo; @@ -122,6 +192,7 @@ void ExitWebsite(); void RenderWebsite(); void HandleWebsite(); +// admin actions void ApplyEnemyPenalties(SOLDIERTYPE* pSoldier); void ApplyMilitiaBonuses(SOLDIERTYPE* pMilitia); UINT8 GetApproximateEnemyLocationResolutionIndex(); @@ -139,17 +210,30 @@ FLOAT GetPathfindersSpeedBonus(UINT8 sector); BOOLEAN NeutraliseRole(const SOLDIERTYPE* pSoldier); void RaidMines(INT32 &playerIncome, INT32 &enemyIncome); BOOLEAN ShowApproximateEnemyLocations(); -void ShowWebsiteAvailableMessage(); + +// agent missions +void ApplyEnemyMechanicalUnitPenalties(SOLDIERTYPE* pSoldier); +void ApplyMilitiaTraits(SOLDIERTYPE* pSoldier); +void ApplyVisionModifier(const SOLDIERTYPE* pSoldier, INT32& sight); +BOOLEAN CanAssignTraitsToMilitia(); +BOOLEAN CanTrainMilitiaAnywhere(); +UINT8 GetMaxTrainersForTrainMilitiaAnywhere(); +INT16 GetAdditionalDeployRange(const UINT8 insertionCode); +INT8 GetEnemyEquipmentCoolnessModifier(); +INT8 GetEnemyEquipmentStatusModifier(const INT8 initialStatus); +UINT8 GetMerchantCoolnessBonus(); +FLOAT GetStrategicDecisionSpeedModifier(); +void HandleStrategicEvent(const UINT32 eventParam); +BOOLEAN ShowEnemyMovementTargets(); void DailyUpdate(); void HourlyUpdate(); void Init(); +void ShowWebsiteAvailableMessage(); BOOLEAN Load(HWFILE file); BOOLEAN Save(HWFILE file); } -//BOOLEAN LoadRebelCommand( HWFILE file ) { return RebelCommand::Load( file ); } - #endif diff --git a/Strategic/Strategic AI.cpp b/Strategic/Strategic AI.cpp index 96e0ab4e..573493b7 100644 --- a/Strategic/Strategic AI.cpp +++ b/Strategic/Strategic AI.cpp @@ -36,6 +36,7 @@ #include "Map Information.h" #include "interface dialogue.h" #include "ASD.h" // added by Flugente + #include "Rebel Command.h" #endif #include "GameInitOptionsScreen.h" @@ -1198,6 +1199,8 @@ void InitStrategicAI() dEnemyGeneralsSpeedupFactor = max( 0.5f, dEnemyGeneralsSpeedupFactor - gStrategicStatus.usVIPsLeft * gGameExternalOptions.fEnemyGeneralStrategicDecisionSpeedBonus ); } + dEnemyGeneralsSpeedupFactor *= RebelCommand::GetStrategicDecisionSpeedModifier(); + giReinforcementPool = zDiffSetting[gGameOptions.ubDifficultyLevel].iQueensInitialPoolOfTroops; giForcePercentage = zDiffSetting[gGameOptions.ubDifficultyLevel].iInitialGarrisonPercentages; giArmyAlertness = zDiffSetting[gGameOptions.ubDifficultyLevel].iEnemyStartingAlertLevel; @@ -3456,6 +3459,8 @@ void EvaluateQueenSituation() { dEnemyGeneralsSpeedupFactor = max( 0.5f, dEnemyGeneralsSpeedupFactor - gStrategicStatus.usVIPsLeft * gGameExternalOptions.fEnemyGeneralStrategicDecisionSpeedBonus ); } + + dEnemyGeneralsSpeedupFactor *= RebelCommand::GetStrategicDecisionSpeedModifier(); uiOffset += dEnemyGeneralsSpeedupFactor * (zDiffSetting[gGameOptions.ubDifficultyLevel].iBaseDelayInMinutesBetweenEvaluations + Random( zDiffSetting[gGameOptions.ubDifficultyLevel].iEvaluationDelayVariance )); diff --git a/Strategic/Town Militia.cpp b/Strategic/Town Militia.cpp index 5ec21b59..9a3e3ed9 100644 --- a/Strategic/Town Militia.cpp +++ b/Strategic/Town Militia.cpp @@ -1,3 +1,4 @@ +#pragma optimize("",off) #include "builddefines.h" #ifdef PRECOMPILEDHEADERS @@ -279,7 +280,7 @@ void TownMilitiaTrainingCompleted( SOLDIERTYPE *pTrainer, INT16 sMapX, INT16 sMa if( ubTownId == BLANK_SECTOR ) { - Assert( IsThisSectorASAMSector( sMapX, sMapY, 0 ) ); + Assert( IsThisSectorASAMSector( sMapX, sMapY, 0 ) || RebelCommand::CanTrainMilitiaAnywhere() ); } // force tactical to update militia status @@ -1293,7 +1294,7 @@ BOOLEAN IsSAMSiteFullOfMilitia( INT16 sSectorX, INT16 sSectorY, INT8 iMilitiaTyp INT32 iMaxMilitiaPerSector = gGameExternalOptions.iMaxMilitiaPerSector; DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Militia5"); // check if SAM site is ours? - fSamSitePresent = IsThisSectorASAMSector( sSectorX, sSectorY, 0 ); + fSamSitePresent = IsThisSectorASAMSector(sSectorX, sSectorY, 0) || RebelCommand::CanTrainMilitiaAnywhere(); if( fSamSitePresent == FALSE ) { diff --git a/Tactical/ArmsDealerInvInit.cpp b/Tactical/ArmsDealerInvInit.cpp index 9e018066..16d0fec6 100644 --- a/Tactical/ArmsDealerInvInit.cpp +++ b/Tactical/ArmsDealerInvInit.cpp @@ -1,3 +1,4 @@ +#pragma optimize("",off) #ifdef PRECOMPILEDHEADERS #include "Tactical All.h" #else @@ -15,6 +16,7 @@ #include "Random.h" #include "Shopkeeper Interface.h" #include "connect.h" + #include "Rebel Command.h" #endif //forward declarations of common classes to eliminate includes @@ -1008,8 +1010,8 @@ UINT8 GetCurrentSuitabilityForItem( INT8 bArmsDealer, UINT16 usItemIndex, BOOLEA // WDS - Improve Tony's and Devin's inventory like BR's // Tony has the better stuff sooner (than Bobby R's) if (bArmsDealer >= 0) { - ubMinCoolness += armsDealerInfo[bArmsDealer].addToCoolness; - ubMaxCoolness += armsDealerInfo[bArmsDealer].addToCoolness; + ubMinCoolness += armsDealerInfo[bArmsDealer].addToCoolness + RebelCommand::GetMerchantCoolnessBonus(); + ubMaxCoolness += armsDealerInfo[bArmsDealer].addToCoolness + RebelCommand::GetMerchantCoolnessBonus(); ubMinCoolness = max( armsDealerInfo[bArmsDealer].minCoolness, min( 9, ubMinCoolness ) ); // silversurfer: max coolness should never be lower than min coolness! //ubMaxCoolness = max( 2, min( armsDealerInfo[bArmsDealer].maxCoolness, ubMaxCoolness ) ); diff --git a/Tactical/DisplayCover.cpp b/Tactical/DisplayCover.cpp index 82a42c0d..32832e70 100644 --- a/Tactical/DisplayCover.cpp +++ b/Tactical/DisplayCover.cpp @@ -1,3 +1,4 @@ +#pragma optimize("",off) #ifdef PRECOMPILEDHEADERS #include "AI All.h" #include "DisplayCover.h" @@ -34,6 +35,7 @@ #include "UI Cursors.h" #include "soldier profile type.h" #include "Interface Cursors.h" // added by Flugente for UICursorDefines +#include "Rebel Command.h" #endif //forward declarations of common classes to eliminate includes @@ -759,7 +761,7 @@ void CalculateCoverFromSoldier( SOLDIERTYPE* pFromSoldier, const INT32& sTargetG { const UINT8& ubStance = animArr[i]; - INT32 usAdjustedSight; + INT32 usAdjustedSight = 0; if (pToSoldier == NULL) { usAdjustedSight = usSightLimit; @@ -767,6 +769,8 @@ void CalculateCoverFromSoldier( SOLDIERTYPE* pFromSoldier, const INT32& sTargetG usAdjustedSight = usSightLimit + usSightLimit * GetSightAdjustment( pToSoldier, GetStealth(pToSoldier), GetSightAdjustmentBasedOnLBE(pToSoldier), sTargetGridNo, (INT8) fRoof, ubStance ) /100; } + RebelCommand::ApplyVisionModifier(pFromSoldier, usAdjustedSight); + if ( SoldierToVirtualSoldierLineOfSightTest( pFromSoldier, sTargetGridNo, (INT8) fRoof, ubStance, FALSE, usAdjustedSight ) != 0 ) { if ( bOverlayType > i ) bOverlayType = i; @@ -784,7 +788,7 @@ static void CalculateCoverFromEnemySoldier(SOLDIERTYPE* pFromSoldier, const INT3 { const UINT8& ubStance = animArr[i]; - INT32 usAdjustedSight; + INT32 usAdjustedSight = 0; if (pToSoldier == nullptr) { usAdjustedSight = usSightLimit; @@ -793,6 +797,8 @@ static void CalculateCoverFromEnemySoldier(SOLDIERTYPE* pFromSoldier, const INT3 usAdjustedSight = usSightLimit + usSightLimit * GetSightAdjustment(pToSoldier, ToSoldierStealth, ToSoldierLBeSightAdjustment, sTargetGridNo, (INT8)fRoof, ubStance) / 100; } + RebelCommand::ApplyVisionModifier(pFromSoldier, usAdjustedSight); + if (SoldierToVirtualSoldierLineOfSightTest(pFromSoldier, sTargetGridNo, (INT8)fRoof, ubStance, FALSE, usAdjustedSight) != 0) { if (bOverlayType > i) bOverlayType = i; diff --git a/Tactical/Inventory Choosing.cpp b/Tactical/Inventory Choosing.cpp index f552825e..ff88ef95 100644 --- a/Tactical/Inventory Choosing.cpp +++ b/Tactical/Inventory Choosing.cpp @@ -20,6 +20,7 @@ #include "message.h" #include "Tactical Save.h" // added by Flugente #include "Soldier macros.h" // added by Flugente + #include "Rebel Command.h" #endif extern WorldItems gAllWorldItems; @@ -276,6 +277,10 @@ void GenerateRandomEquipment( SOLDIERCREATE_STRUCT *pp, INT8 bSoldierClass, INT8 // SANDRO - new behaviour of progress setting bEquipmentModifier = bEquipmentRating + ( ( CalcDifficultyModifier( bSoldierClass ) / 10 ) - 5 ); + + if (bSoldierClass >= SOLDIER_CLASS_ADMINISTRATOR && bSoldierClass <= SOLDIER_CLASS_ARMY) + bEquipmentModifier += RebelCommand::GetEnemyEquipmentCoolnessModifier(); + switch( gGameOptions.ubProgressSpeedOfItemsChoices ) { case ITEM_PROGRESS_VERY_SLOW: @@ -943,6 +948,8 @@ void ChooseWeaponForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bWeaponC // don't allow it to be lower than marksmanship, we don't want it to affect their chances of hitting bStatus = (INT8)max( pp->bMarksmanship, bStatus ); + // ... unless we've done something to ruin their gear + bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(bStatus); CreateItem( usGunIndex, bStatus, &(pp->Inv[ HANDPOS ]) ); pp->Inv[ HANDPOS ].fFlags |= OBJECT_UNDROPPABLE; @@ -1457,6 +1464,7 @@ void ChooseArmourForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bHelmetC //INVTYPE *pItem; //UINT16 usRandom; UINT16 usItem = 0, usHelmetItem = 0, usVestItem = 0, usLeggingsItem = 0; + INT8 bStatus = 0; //UINT16 usNumMatches; //INT8 bOrigVestClass = bVestClass; @@ -1494,7 +1502,8 @@ void ChooseArmourForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bHelmetC if(!gGameExternalOptions.fSoldiersWearAnyArmour) usHelmetItem = PickARandomItem(HELMET, pp->ubSoldierClass, bHelmetClass ); if ( usHelmetItem > 0 && Item[usHelmetItem].usItemClass == IC_ARMOUR && !(pp->Inv[ HELMETPOS ].fFlags & OBJECT_NO_OVERWRITE) && Armour[ Item[usHelmetItem].ubClassIndex ].ubArmourClass == ARMOURCLASS_HELMET ) { - CreateItem( usHelmetItem, (INT8)(70+Random(31)), &(pp->Inv[ HELMETPOS ]) ); + bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); + CreateItem( usHelmetItem, bStatus, &(pp->Inv[ HELMETPOS ]) ); pp->Inv[ HELMETPOS ].fFlags |= OBJECT_UNDROPPABLE; // roll to see if he gets an attachment, too. Higher chance the higher his entitled helmet class is @@ -1503,7 +1512,8 @@ void ChooseArmourForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bHelmetC UINT16 usAttachment = PickARandomAttachment(ARMOURATTACHMENT, pp->ubSoldierClass, usHelmetItem, bHelmetClass, FALSE); if ( usAttachment > 0 ) { - CreateItem( usAttachment, (INT8)(70+Random(31)), &gTempObject ); + bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); + CreateItem( usAttachment, bStatus, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ HELMETPOS ].AttachObject( NULL, &gTempObject, FALSE ); } @@ -1567,7 +1577,8 @@ void ChooseArmourForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bHelmetC if(!gGameExternalOptions.fSoldiersWearAnyArmour) usVestItem = PickARandomItem(VEST, pp->ubSoldierClass, bVestClass ); if ( usVestItem > 0 && Item[usVestItem].usItemClass == IC_ARMOUR && !(pp->Inv[ VESTPOS ].fFlags & OBJECT_NO_OVERWRITE) && Armour[ Item[usVestItem].ubClassIndex ].ubArmourClass == ARMOURCLASS_VEST ) { - CreateItem( usVestItem, (INT8)(70+Random(31)), &(pp->Inv[ VESTPOS ]) ); + bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); + CreateItem( usVestItem, bStatus, &(pp->Inv[ VESTPOS ]) ); pp->Inv[ VESTPOS ].fFlags |= OBJECT_UNDROPPABLE; // roll to see if he gets a CERAMIC PLATES, too. Higher chance the higher his entitled vest class is @@ -1576,7 +1587,8 @@ void ChooseArmourForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bHelmetC UINT16 usAttachment = PickARandomAttachment(ARMOURATTACHMENT, pp->ubSoldierClass, usVestItem, bVestClass, FALSE); if ( usAttachment > 0 ) { - CreateItem( usAttachment, (INT8)(70+Random(31)), &gTempObject ); + bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); + CreateItem( usAttachment, bStatus, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ VESTPOS ].AttachObject( NULL, &gTempObject, FALSE ); } @@ -1661,7 +1673,8 @@ void ChooseArmourForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bHelmetC if(!gGameExternalOptions.fSoldiersWearAnyArmour) usLeggingsItem = PickARandomItem(LEGS, pp->ubSoldierClass, bLeggingsClass ); if ( usLeggingsItem > 0 && Item[usLeggingsItem].usItemClass == IC_ARMOUR && !(pp->Inv[ LEGPOS ].fFlags & OBJECT_NO_OVERWRITE) && Armour[ Item[usLeggingsItem].ubClassIndex ].ubArmourClass == ARMOURCLASS_LEGGINGS ) { - CreateItem( usLeggingsItem, (INT8)(70+Random(31)), &(pp->Inv[ LEGPOS ]) ); + bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); + CreateItem( usLeggingsItem, bStatus, &(pp->Inv[ LEGPOS ]) ); pp->Inv[ LEGPOS ].fFlags |= OBJECT_UNDROPPABLE; // roll to see if he gets an attachment, too. Higher chance the higher his entitled Leggings class is @@ -1670,7 +1683,8 @@ void ChooseArmourForSoldierCreateStruct( SOLDIERCREATE_STRUCT *pp, INT8 bHelmetC UINT16 usAttachment = PickARandomAttachment(ARMOURATTACHMENT, pp->ubSoldierClass, usLeggingsItem, bLeggingsClass, FALSE); if ( usAttachment > 0 ) { - CreateItem( usAttachment, (INT8)(70+Random(31)), &gTempObject ); + bStatus = RebelCommand::GetEnemyEquipmentStatusModifier(70 + Random(31)); + CreateItem( usAttachment, bStatus, &gTempObject ); gTempObject.fFlags |= OBJECT_UNDROPPABLE; pp->Inv[ LEGPOS ].AttachObject( NULL, &gTempObject, FALSE); } diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 192b82a0..fc5c6839 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -1837,6 +1837,8 @@ BOOLEAN TacticalCopySoldierFromCreateStruct( SOLDIERTYPE *pSoldier, SOLDIERCREAT RebelCommand::ApplyMilitiaBonuses(pSoldier); if ((SOLDIER_CLASS_ENEMY(pSoldier->ubSoldierClass) || pSoldier->ubSoldierClass == SOLDIER_CLASS_BANDIT)) RebelCommand::ApplyEnemyPenalties(pSoldier); + if (pCreateStruct->bTeam == ENEMY_TEAM && (ENEMYROBOT(pCreateStruct) || ARMED_VEHICLE(pCreateStruct))) + RebelCommand::ApplyEnemyMechanicalUnitPenalties(pSoldier); // Flugente: enemy roles if ( gGameExternalOptions.fEnemyRoles && gGameExternalOptions.fEnemyOfficers && SOLDIER_CLASS_ENEMY( pSoldier->ubSoldierClass ) ) @@ -3223,6 +3225,8 @@ SOLDIERTYPE* TacticalCreateEnemyTank() // Flugente: why would a vehicle's armour depend on game progress? Always give them 100 HP pSoldier->stats.bLifeMax = 100; pSoldier->stats.bLife = pSoldier->stats.bLifeMax; + + RebelCommand::ApplyEnemyMechanicalUnitPenalties(pSoldier); } return( pSoldier ); @@ -3263,6 +3267,8 @@ SOLDIERTYPE* TacticalCreateEnemyJeep( ) // Flugente: why would a vehicle's armour depend on game progress? Always give them 100 HP pSoldier->stats.bLifeMax = 100; pSoldier->stats.bLife = pSoldier->stats.bLifeMax; + + RebelCommand::ApplyEnemyMechanicalUnitPenalties(pSoldier); } return(pSoldier); @@ -3303,6 +3309,8 @@ SOLDIERTYPE* TacticalCreateEnemyRobot() pSoldier->stats.bLifeMax = 80; pSoldier->stats.bLife = pSoldier->stats.bLifeMax; + + RebelCommand::ApplyEnemyMechanicalUnitPenalties(pSoldier); } return(pSoldier); diff --git a/TileEngine/Map Edgepoints.cpp b/TileEngine/Map Edgepoints.cpp index f2ef426b..afd3c251 100644 --- a/TileEngine/Map Edgepoints.cpp +++ b/TileEngine/Map Edgepoints.cpp @@ -14,6 +14,7 @@ #include "strategicmap.h" #include "worldman.h" #include "PreBattle Interface.h" // added by Flugente + #include "Rebel Command.h" #endif #include "connect.h" @@ -1401,7 +1402,7 @@ INT32 SearchForClosestPrimaryMapEdgepoint(INT32 sGridNo, UINT8 ubInsertionCode, break; } // WANNE - MP: Center - if ( ( (is_networked || GetEnemyEncounterCode() == ENEMY_AMBUSH_DEPLOYMENT_CODE ) && ubInsertionCode == INSERTION_CODE_CENTER) || ubInsertionCode == INSERTION_CODE_CHOPPER ) + if ( ( (is_networked || GetEnemyEncounterCode() == ENEMY_AMBUSH_DEPLOYMENT_CODE ) && ubInsertionCode == INSERTION_CODE_CENTER) || ubInsertionCode == INSERTION_CODE_CHOPPER || (RebelCommand::GetAdditionalDeployRange(ubInsertionCode) > 0) ) { InitCenterEdgepoint( ubInsertionCode == INSERTION_CODE_CENTER ); psArray = gps1stCenterEdgepointArray; diff --git a/TileEngine/Tactical Placement GUI.cpp b/TileEngine/Tactical Placement GUI.cpp index a14b27dd..8c897f8e 100644 --- a/TileEngine/Tactical Placement GUI.cpp +++ b/TileEngine/Tactical Placement GUI.cpp @@ -1,3 +1,4 @@ +#pragma optimize("",off) #include "builddefines.h" // WANNE 2 @@ -41,6 +42,7 @@ #include "renderworld.h"//dnl ch45 051009 #include "merc entering.h" #include "CampaignStats.h" // added by Flugente +#include "Rebel Command.h" typedef struct MERCPLACEMENT { @@ -910,6 +912,7 @@ void RenderTacticalPlacementGUI() if(sWorldScreenY <= PLACEMENT_OFFSET) { sY = (PLACEMENT_OFFSET - sWorldScreenY) / 5; + sY += RebelCommand::GetAdditionalDeployRange(INSERTION_CODE_NORTH)/5; gTPClipRect.iTop += sY; } break; @@ -917,6 +920,7 @@ void RenderTacticalPlacementGUI() if((sWorldScreenX + NORMAL_MAP_SCREEN_WIDTH) >= (MAPWIDTH - PLACEMENT_OFFSET)) { sX = ((sWorldScreenX + NORMAL_MAP_SCREEN_WIDTH) - (MAPWIDTH - PLACEMENT_OFFSET)) / 5; + sX += RebelCommand::GetAdditionalDeployRange(INSERTION_CODE_EAST)/5; gTPClipRect.iRight -= sX; } break; @@ -924,6 +928,7 @@ void RenderTacticalPlacementGUI() if((sWorldScreenY + NORMAL_MAP_SCREEN_HEIGHT) >= (MAPHEIGHT - PLACEMENT_OFFSET)) { sY = ((sWorldScreenY + NORMAL_MAP_SCREEN_HEIGHT) - (MAPHEIGHT - PLACEMENT_OFFSET)) / 5; + sY += RebelCommand::GetAdditionalDeployRange(INSERTION_CODE_SOUTH)/5; gTPClipRect.iBottom -= sY; } break; @@ -931,6 +936,7 @@ void RenderTacticalPlacementGUI() if(sWorldScreenX <= PLACEMENT_OFFSET) { sX = (PLACEMENT_OFFSET - sWorldScreenX) / 5; + sX += RebelCommand::GetAdditionalDeployRange(INSERTION_CODE_WEST)/5; gTPClipRect.iLeft += sX; } break; @@ -1234,19 +1240,19 @@ void TacticalPlacementHandle() switch(gMercPlacement[gbCursorMercID].ubStrategicInsertionCode) { case INSERTION_CODE_NORTH: - if(sWorldScreenY <= PLACEMENT_OFFSET) + if(sWorldScreenY <= (PLACEMENT_OFFSET + RebelCommand::GetAdditionalDeployRange(INSERTION_CODE_NORTH))) gfValidCursor = TRUE; break; case INSERTION_CODE_EAST: - if(sWorldScreenX >= (MAPWIDTH - PLACEMENT_OFFSET)) + if(sWorldScreenX >= ((MAPWIDTH - PLACEMENT_OFFSET - RebelCommand::GetAdditionalDeployRange(INSERTION_CODE_EAST)))) gfValidCursor = TRUE; break; case INSERTION_CODE_SOUTH: - if(sWorldScreenY >= (MAPHEIGHT - PLACEMENT_OFFSET)) + if(sWorldScreenY >= ((MAPHEIGHT - PLACEMENT_OFFSET - RebelCommand::GetAdditionalDeployRange(INSERTION_CODE_SOUTH)))) gfValidCursor = TRUE; break; case INSERTION_CODE_WEST: - if(sWorldScreenX <= PLACEMENT_OFFSET) + if(sWorldScreenX <= (PLACEMENT_OFFSET + RebelCommand::GetAdditionalDeployRange(INSERTION_CODE_WEST))) gfValidCursor = TRUE; break; } diff --git a/Utils/Text.h b/Utils/Text.h index b5fcd1e9..2ed7ca03 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -3138,6 +3138,7 @@ extern STR16 szRebelCommandText[]; extern STR16 szRebelCommandHelpText[]; extern STR16 szRebelCommandAdminActionsText[]; extern STR16 szRebelCommandDirectivesText[]; +extern STR16 szRebelCommandAgentMissionsText[]; extern STR16 szRobotText[]; enum { diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 0a730a33..1dbae02e 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -290,7 +290,7 @@ STR16 pEncyclopediaFilterCharText[] = {//major char filter button text //..L"------v" L"全部",//All - L"A.I.M", + L"A.I.M.", L"MERC", L"RPC", L"NPC", @@ -4218,7 +4218,7 @@ STR16 pHelicopterEtaStrings[] = L"Skyrider", L"着陆点", L"直升机严重受损,必须降落在敌军领地!", // warning that the sector the helicopter is going to use for refueling is under enemy control -> - L"直升机将直接返回基地,你希望在此之前放下乘客吗?", + L"直升机将直接返回基地,你希望在此之前丢下乘客吗?", L"剩余燃料:", L"到加油站距离:", }; @@ -5097,7 +5097,7 @@ STR16 pLaptopIcons[] = STR16 pBookMarkStrings[] = { - L"A.I.M", + L"A.I.M.", L"Bobby Ray's", L"I.M.P", L"M.E.R.C", @@ -7030,7 +7030,7 @@ STR16 pMessageStrings[] = L"磁盘空间不足。只有%sMB可用空间,《铁血联盟2》需要%sMB。", L"从AIM雇佣了%s。", //"Hired %s from AIM", L"%s抓住了%s。", //"%s has caught %s.", //'Merc name' has caught 'item' -- let SirTech know if name comes after item. - L"%s使用了(拾取)%s。", //L"%s has taken %s。", + L"%s使用了%s。", //L"%s has taken %s。", L"%s没有医疗技能", //"%s has no medical skill",//'Merc name' has no medical skill. //CDRom errors (such as ejecting CD while attempting to read the CD) @@ -11369,19 +11369,19 @@ STR16 szIMPGearPocketText[] = L"选择背心", // L"Select vest", L"选择裤子", //L"Select pants", L"选择头部装备", //L"Select face gear", - L"选择头部装备", //L"Select face gear", + L"选择头部装备", // L"Select face gear", - L"选择主武器", //L"Select main gun", - L"选择副武器", //L"Select sidearm", + L"选择主枪", // L"Select main gun", + L"选择手枪", //L"Select sidearm", L"选择LBE背心", //L"Select LBE vest", - L"选择左LBE枪套", //L"Select left LBE holster", - L"选择右LBE枪套", //L"Select right LBE holster", + L"选择LBE皮套", //L"Select left LBE holster", + L"选择LBE皮套", //L"Select right LBE holster", L"选择LBE战斗包", //L"Select LBE combat pack", L"选择LBE背包", //L"Select LBE backpack", - L"选择发射器/步枪", //L"Select launcher / rifle", - L"选择近战武器", //L"Select melee weapon", + L"Select launcher / rifle", + L"选择武器", //L"Select melee weapon", L"选择附加物品", //L"Select additional items", //BIGPOCK1POS L"选择附加物品", //L"Select additional items", @@ -11390,28 +11390,28 @@ STR16 szIMPGearPocketText[] = L"选择附加物品", //L"Select additional items", L"选择附加物品", //L"Select additional items", L"选择附加物品", //L"Select additional items", - L"选择医疗套件", //L"Select medkit",MEDPOCK1POS - L"选择医疗套件", //L"Select medkit", - L"选择医疗套件", //L"Select medkit", - L"选择医疗套件", //L"Select medkit", - L"选择主武器弹药", //L"Select main gun ammo",SMALLPOCK1POS - L"选择主武器弹药", //L"Select main gun ammo", - L"选择主武器弹药", //L"Select main gun ammo", - L"选择主武器弹药", //L"Select main gun ammo", - L"选择主武器弹药", //L"Select main gun ammo", - L"选择发射器/枪弹药", //L"Select launcher / rifle ammo",SMALLPOCK6POS - L"选择发射器/枪弹药", //L"Select launcher / rifle ammo", - L"选择发射器/枪弹药", //L"Select launcher / rifle ammo", - L"选择发射器/枪弹药", //L"Select launcher / rifle ammo", - L"选择发射器/枪弹药", //L"Select launcher / rifle ammo", - L"选择配枪弹药", //L"Select sidearm ammo",SMALLPOCK11POS - L"选择配枪弹药", //L"Select sidearm ammo", - L"选择配枪弹药", //L"Select sidearm ammo", - L"选择配枪弹药", //L"Select sidearm ammo", - L"选择配枪弹药", //L"Select sidearm ammo", - L"选择配枪弹药", //L"Select sidearm ammo", - L"选择配枪弹药", //L"Select sidearm ammo", - L"选择配枪弹药", //L"Select sidearm ammo", + L"Select medkit", //MEDPOCK1POS + L"Select medkit", + L"Select medkit", + L"Select medkit", + L"Select main gun ammo", //SMALLPOCK1POS + L"Select main gun ammo", + L"Select main gun ammo", + L"Select main gun ammo", + L"Select main gun ammo", + L"Select launcher / rifle ammo", //SMALLPOCK6POS + L"Select launcher / rifle ammo", + L"Select launcher / rifle ammo", + L"Select launcher / rifle ammo", + L"Select launcher / rifle ammo", + L"Select sidearm ammo", //SMALLPOCK11POS + L"Select sidearm ammo", + L"Select sidearm ammo", + L"Select sidearm ammo", + L"Select sidearm ammo", + L"Select sidearm ammo", + L"Select sidearm ammo", + L"Select sidearm ammo", L"选择附加物品", //L"Select additional items", //SMALLPOCK19POS L"选择附加物品", //L"Select additional items", L"选择附加物品", //L"Select additional items", @@ -11424,7 +11424,7 @@ STR16 szIMPGearPocketText[] = L"选择附加物品", //L"Select additional items", L"选择附加物品", //L"Select additional items", L"选择附加物品", //L"Select additional items", //SMALLPOCK30POS - L"左键单击选择项目/右键单击关闭窗口", //L"Left click to select item / Right click to close window", + L"Left click to select item / Right click to close window", }; STR16 szMilitiaStrategicMovementText[] = @@ -11966,7 +11966,7 @@ STR16 szRebelCommandAdminActionsText[] = L"民兵武器库", //L"Militia Warehouses", L"在偏远地区建造仓库,让反抗军为民兵储备武器。提供每日民兵资源。", //L"Construct warehouses in remote areas, allowing the rebels to stockpile weapons for the militia. Provides daily militia resources.", L"税务局", //L"Regional Taxes", - L"从当地人那里筹集资金来帮助你。这是一种永久的行为。增加每日收入,但地区忠诚度会逐日下降。", //L"Collect money from the locals to assist your efforts. This is a permanent action. Increases daily income, but regional loyalty falls daily.", + L"从当地人那里筹集资金来帮助你。增加每日收入,但地区忠诚度会逐日下降。", //L"Collect money from the locals to assist your efforts. Increases daily income, but regional loyalty falls daily.", L"民间援助", //L"Civilian Aid", L"指派一些反抗军直接协助和支持该地区的平民。增加每天志愿者的总数。", //L"Assign some rebels to directly assist and support civilians in the area. Increases daily volunteer pool growth.", L"私人佣兵团", //L"Merc Support", diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index f842d661..ddb901a1 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -11976,7 +11976,7 @@ STR16 szRebelCommandAdminActionsText[] = // TODO.Translate L"Militia Warehouses", L"Construct warehouses in remote areas, allowing the rebels to stockpile weapons for the militia. Provides daily militia resources.", L"Regional Taxes", - L"Collect money from the locals to assist your efforts. This is a permanent action. Increases daily income, but regional loyalty falls daily.", + L"Collect money from the locals to assist your efforts. Increases daily income, but regional loyalty falls daily.", L"Civilian Aid", L"Assign some rebels to directly assist and support civilians in the area. Increases daily volunteer pool growth.", L"Merc Support", diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 9f37ce44..2c27afb7 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -11864,12 +11864,16 @@ STR16 gLbeStatsDesc[14] = STR16 szRebelCommandText[] = { - L"Arulco Rebel Command - National Overview", - L"Arulco Rebel Command - Regional Overview", - L"Switch to Regional Overview", - L"Switch to National Overview", + L"National Overview", + L"Regional Overview", + L"Mission Overview", + L"Select View:", + L"Regional (2)", + L"National (1)", + L"Mission (3)", L"Supplies:", L"Incoming Supplies", + L"Intel:", L"/day", L"Current Directive", L"Improve Directive ($%d)", @@ -11932,12 +11936,11 @@ STR16 szRebelCommandText[] = STR16 szRebelCommandHelpText[] = { L"|S|u|p|p|l|i|e|s\n \nFood, water, medical supplies, weapons, and anything else that\nthe rebels might find useful. Supplies are obtained automatically\nby the rebels.", - L"|I|n|c|o|m|i|n|g |S|u|p|p|l|i|e|s\n \nEach day, the rebels will gather supplies on their own. As you\ntake over more towns, the amount of supplies they will be\nable to find per day will increase.", + L"|I|n|c|o|m|i|n|g |S|u|p|p|l|i|e|s\n \nEach day, the rebels will gather supplies on their own. As you\ntake over more towns, the amount of supplies they will be\nable to find per day will increase.\n \n+%d (Base income)", L"|C|u|r|r|e|n|t |D|i|r|e|c|t|i|v|e\n \nYou can choose how the rebels will prioritise their strategic\nobjectives. New directives will become available as you make\nprogress.", L"|A|d|m|i|n|i|s|t|r|a|t|i|o|n |T|e|a|m\n \nOnce deployed, an admin team is responsible for handling the\nday-to-day affairs of the region. This includes supporting\nlocals, creating rebel propaganda, establishing regional\npolicies, and more.", L"|L|o|y|a|l|t|y\n \nThe effectiveness of many Administrative Actions depends on\nthe region's loyalty to your cause. It is in your best interest\nto raise loyalty as high as possible.", L"|M|a|x|i|m|u|m |L|o|y|a|l|t|y\n \nYou will need to convince the locals to fully trust you. This\ncan be done by creating a supply line to them, showing that\nyou intend to improve their quality of life.", - L"|G|r|a|n|t |S|u|p|p|l|i|e|s\n \nSend supplies to the admin team here and allow them to use them\nas needed. This will increase the region's loyalty by a small amount\neach time you do this. However, doing this will slightly increase\nthe cost of enacting regional policies.", L"This Admin Action applies its bonus to town sectors only.", L"This Admin Action applies its bonus to town sectors, and\nsectors immediately adjacent to them.", L"This Admin Action applies its bonus to town sectors, one\nsector away at Tier 1, and up to two sectors away at Tier 2.", @@ -11966,7 +11969,7 @@ STR16 szRebelCommandAdminActionsText[] = L"Militia Warehouses", L"Construct warehouses in remote areas, allowing the rebels to stockpile weapons for the militia. Provides daily militia resources.", L"Regional Taxes", - L"Collect money from the locals to assist your efforts. This is a permanent action. Increases daily income, but regional loyalty falls daily.", + L"Collect money from the locals to assist your efforts. Increases daily income, but regional loyalty falls daily.", L"Civilian Aid", L"Assign some rebels to directly assist and support civilians in the area. Increases daily volunteer pool growth.", L"Merc Support", @@ -12030,6 +12033,28 @@ STR16 szRebelCommandDirectivesText[] = L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + L"New missions will be available every 48 hours.", + L"A mission is currently active.", + L"Deep Deployment", + L"Coordinate efforts to find ways to sneak up on the enemy, but be careful: it's equally possible to put yourself in a disadvantaged deployment area. When attacking enemy forces, the deployment area is much larger.", + L"Strategic Intel", + L"Intercept plans and discover where enemies intend to strike. When viewing teams on the strategic map, sectors prioritised by the enemy will be marked in red.", + L"Improve Local Shops", + L"Set up ways for merchants across the country to acquire better goods more easily. Shopkeepers will have better than usual inventories.", + L"Slow Strategic Decisions", + L"Sow confusion and misdirection at the highest levels of enemy command. The enemy takes longer to make decisions at a strategic level.", + L"Lower Readiness", + L"Trick enemy soldiers into letting their guard down. Enemy soldiers have reduced vision range until they are alerted to your mercs' presence.", + L"Sabotage Equipment", + L"Disrupt enemy supply chains and prevent the enemy from maintaining their gear properly. Enemy soldiers use equipment that is worse than normal.", + L"Sabotage Vehicles", + L"Sabotage vehicle maintenance hubs to reduce their combat effectiveness and readiness. Enemy vehicles encountered have reduced stats.", + L"Train Militia Anywhere", + L"Create training areas in the wilderness that can be quickly set up and torn down. Militia can be trained in uncontested sectors outside of town.", +}; + STR16 szRobotText[] = { L"The robot's installed weapon cannot be changed.", diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index b645769e..29198590 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -11958,7 +11958,7 @@ STR16 szRebelCommandAdminActionsText[] = // TODO.Translate L"Militia Warehouses", L"Construct warehouses in remote areas, allowing the rebels to stockpile weapons for the militia. Provides daily militia resources.", L"Regional Taxes", - L"Collect money from the locals to assist your efforts. This is a permanent action. Increases daily income, but regional loyalty falls daily.", + L"Collect money from the locals to assist your efforts. Increases daily income, but regional loyalty falls daily.", L"Civilian Aid", L"Assign some rebels to directly assist and support civilians in the area. Increases daily volunteer pool growth.", L"Merc Support", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index fe934da1..55478189 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -11880,7 +11880,7 @@ STR16 szRebelCommandAdminActionsText[] = // TODO.Translate L"Militia Warehouses", L"Construct warehouses in remote areas, allowing the rebels to stockpile weapons for the militia. Provides daily militia resources.", L"Regional Taxes", - L"Collect money from the locals to assist your efforts. This is a permanent action. Increases daily income, but regional loyalty falls daily.", + L"Collect money from the locals to assist your efforts. Increases daily income, but regional loyalty falls daily.", L"Civilian Aid", L"Assign some rebels to directly assist and support civilians in the area. Increases daily volunteer pool growth.", L"Merc Support", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 8dd97fc0..658de32f 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -11967,7 +11967,7 @@ STR16 szRebelCommandAdminActionsText[] = // TODO.Translate L"Militia Warehouses", L"Construct warehouses in remote areas, allowing the rebels to stockpile weapons for the militia. Provides daily militia resources.", L"Regional Taxes", - L"Collect money from the locals to assist your efforts. This is a permanent action. Increases daily income, but regional loyalty falls daily.", + L"Collect money from the locals to assist your efforts. Increases daily income, but regional loyalty falls daily.", L"Civilian Aid", L"Assign some rebels to directly assist and support civilians in the area. Increases daily volunteer pool growth.", L"Merc Support", diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index b66a93eb..97513cf8 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -11980,7 +11980,7 @@ STR16 szRebelCommandAdminActionsText[] = // TODO.Translate L"Militia Warehouses", L"Construct warehouses in remote areas, allowing the rebels to stockpile weapons for the militia. Provides daily militia resources.", L"Regional Taxes", - L"Collect money from the locals to assist your efforts. This is a permanent action. Increases daily income, but regional loyalty falls daily.", + L"Collect money from the locals to assist your efforts. Increases daily income, but regional loyalty falls daily.", L"Civilian Aid", L"Assign some rebels to directly assist and support civilians in the area. Increases daily volunteer pool growth.", L"Merc Support", diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 92a8e8ab..5d0ee7cf 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -11961,7 +11961,7 @@ STR16 szRebelCommandAdminActionsText[] = // TODO.Translate L"Militia Warehouses", L"Construct warehouses in remote areas, allowing the rebels to stockpile weapons for the militia. Provides daily militia resources.", L"Regional Taxes", - L"Collect money from the locals to assist your efforts. This is a permanent action. Increases daily income, but regional loyalty falls daily.", + L"Collect money from the locals to assist your efforts. Increases daily income, but regional loyalty falls daily.", L"Civilian Aid", L"Assign some rebels to directly assist and support civilians in the area. Increases daily volunteer pool growth.", L"Merc Support",