diff --git a/GameSettings.cpp b/GameSettings.cpp index 49ec4290f..684fee826 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -4157,6 +4157,125 @@ void LoadRebelCommandSettings() gRebelCommandSettings.iFortificationsBonus = iniReader.ReadInteger("Rebel Command Settings", "FORTIFICATIONS_BONUS", 10, 0, 100); + // agent missions + gRebelCommandSettings.iMissionBaseCost = iniReader.ReadInteger("Rebel Command Settings", "MISSION_BASE_COST", 500, 100, 10000); + gRebelCommandSettings.iMissionAdditionalCost = iniReader.ReadInteger("Rebel Command Settings", "MISSION_ADDITIONAL_COST", 250, 0, 10000); + gRebelCommandSettings.iMissionPrepTime = iniReader.ReadInteger("Rebel Command Settings", "MISSION_PREPARATION_TIME", 24, 1, 168); + gRebelCommandSettings.iMissionRefreshTimeDays = iniReader.ReadInteger("Rebel Command Settings", "MISSION_REFRESH_TIME_DAYS", 2, 1, 7); + gRebelCommandSettings.iMinLoyaltyForMission = iniReader.ReadInteger("Rebel Command Settings", "MIN_LOYALTY_FOR_MISSION", 51, 0, 100); + + 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.iDisruptAsdSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "DISRUPT_ASD_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.fDisruptAsdIncomeReductionModifier = iniReader.ReadFloat("Rebel Command Settings", "DISRUPT_ASD_INCOME_MODIFIER", 0.5f, 0.f, 1.f); + gRebelCommandSettings.fDisruptAsdIncomeReductionModifier_Covert = iniReader.ReadFloat("Rebel Command Settings", "DISRUPT_ASD_INCOME_MODIFIER_COVERT", 0.5f, 0.f, 1.f); + gRebelCommandSettings.fDisruptAsdIncomeReductionModifier_Demolitions = iniReader.ReadFloat("Rebel Command Settings", "DISRUPT_ASD_INCOME_MODIFIER_DEMOLITIONS", 0.5f, 0.f, 1.f); + gRebelCommandSettings.fDisruptAsdIncomeReductionModifier_Nightops = iniReader.ReadFloat("Rebel Command Settings", "DISRUPT_ASD_INCOME_MODIFIER_NIGHTOPS", 0.5f, 0.f, 1.f); + gRebelCommandSettings.fDisruptAsdIncomeReductionModifier_Technician = iniReader.ReadFloat("Rebel Command Settings", "DISRUPT_ASD_INCOME_MODIFIER_TECHNICIAN", 0.5f, 0.f, 1.f); + gRebelCommandSettings.iDisruptAsdDuration = iniReader.ReadInteger("Rebel Command Settings", "DISRUPT_ASD_DURATION", 72, 0, 255); + gRebelCommandSettings.iDisruptAsdDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "DISRUPT_ASD_DURATION_BONUS_COVERT", 48, 0, 255); + gRebelCommandSettings.iDisruptAsdDuration_Bonus_Demolitions = iniReader.ReadInteger("Rebel Command Settings", "DISRUPT_ASD_DURATION_BONUS_DEMOLITIONS", 48, 0, 255); + gRebelCommandSettings.iDisruptAsdDuration_Bonus_Nightops = iniReader.ReadInteger("Rebel Command Settings", "DISRUPT_ASD_DURATION_BONUS_NIGHTOPS", 48, 0, 255); + gRebelCommandSettings.iDisruptAsdDuration_Bonus_Technician = iniReader.ReadInteger("Rebel Command Settings", "DISRUPT_ASD_DURATION_BONUS_TECHNICIAN", 48, 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.iSendSuppliesToTownSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "SEND_SUPPLIES_TO_TOWN_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.iSendSuppliesToTownDuration = iniReader.ReadInteger("Rebel Command Settings", "SEND_SUPPLIES_TO_TOWN_DURATION", 72, 0, 255); + gRebelCommandSettings.iSendSuppliesToTownLoyaltyGain = iniReader.ReadInteger("Rebel Command Settings", "SEND_SUPPLIES_TO_TOWN_LOYALTY_GAIN", 500, 1, 10000); + gRebelCommandSettings.iSendSuppliesToTownInterval = iniReader.ReadInteger("Rebel Command Settings", "SEND_SUPPLIES_TO_TOWN_INTERVAL", 6, 1, 24); + + 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); + + gRebelCommandSettings.iSoldierBountiesKingpinSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_SUCCESS_CHANCE", 50, 0, 100); + gRebelCommandSettings.iSoldierBountiesKingpinDuration = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_DURATION", 24, 0, 255); + gRebelCommandSettings.iSoldierBountiesKingpinDuration_Bonus_Covert = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_DURATION_BONUS_COVERT", 24, 0, 255); + gRebelCommandSettings.iSoldierBountiesKingpinDuration_Bonus_Demolitions = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_DURATION_BONUS_DEMOLITIONS", 24, 0, 255); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Admin = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_ADMIN", 100, 0, 5000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Troop = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_TROOP", 100, 0, 5000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Elite = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_ELITE", 100, 0, 5000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Robot = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_ROBOT", 100, 0, 5000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Jeep = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_JEEP", 100, 0, 5000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Tank = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_TANK", 100, 0, 5000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Officer = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_OFFICER", 100, 0, 5000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Limit = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_LIMIT", 10000, 0, 30000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Limit_Demolitions = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_LIMIT_DEMOLITIONS", 10000, 0, 30000); + gRebelCommandSettings.iSoldierBountiesKingpinPayout_Limit_Snitch = iniReader.ReadInteger("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_LIMIT_SNITCH", 10000, 0, 30000); + gRebelCommandSettings.fSoldierBountiesKingpinPayout_Bonus_Covert = iniReader.ReadFloat("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_BONUS_COVERT", 1.1f, 0.f, 2.f); + gRebelCommandSettings.fSoldierBountiesKingpinPayout_Bonus_Deputy = iniReader.ReadFloat("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_BONUS_DEPUTY", 1.1f, 0.f, 2.f); + gRebelCommandSettings.fSoldierBountiesKingpinPayout_Bonus_Snitch = iniReader.ReadFloat("Rebel Command Settings", "SOLDIER_BOUNTIES_KINGPIN_PAYOUT_BONUS_SNITCH", 1.1f, 0.f, 2.f); } void FreeGameExternalOptions() diff --git a/GameSettings.h b/GameSettings.h index 8ed96c485..5f5db0adc 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -1842,6 +1842,126 @@ typedef struct INT16 iFortificationsBonus; + // agent missions + INT32 iMissionBaseCost; + INT32 iMissionAdditionalCost; + INT16 iMissionPrepTime; + INT8 iMissionRefreshTimeDays; + INT8 iMinLoyaltyForMission; + + 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 iDisruptAsdSuccessChance; + FLOAT fDisruptAsdIncomeReductionModifier; + FLOAT fDisruptAsdIncomeReductionModifier_Covert; + FLOAT fDisruptAsdIncomeReductionModifier_Demolitions; + FLOAT fDisruptAsdIncomeReductionModifier_Nightops; + FLOAT fDisruptAsdIncomeReductionModifier_Technician; + UINT8 iDisruptAsdDuration; + UINT8 iDisruptAsdDuration_Bonus_Covert; + UINT8 iDisruptAsdDuration_Bonus_Demolitions; + UINT8 iDisruptAsdDuration_Bonus_Nightops; + UINT8 iDisruptAsdDuration_Bonus_Technician; + + 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 iSendSuppliesToTownSuccessChance; + UINT8 iSendSuppliesToTownDuration; + INT32 iSendSuppliesToTownLoyaltyGain; + INT8 iSendSuppliesToTownInterval; + + INT8 iTrainMilitiaAnywhereSuccessChance; + INT8 iTrainMilitiaAnywhereMaxTrainers; + INT8 iTrainMilitiaAnywhereMaxTrainers_Teaching; + UINT8 iTrainMilitiaAnywhereDuration; + UINT8 iTrainMilitiaAnywhereDuration_Bonus_Covert; + UINT8 iTrainMilitiaAnywhereDuration_Bonus_Survival; + UINT8 iTrainMilitiaAnywhereDuration_Bonus_Teaching; + + INT8 iSoldierBountiesKingpinSuccessChance; + UINT8 iSoldierBountiesKingpinDuration; + UINT8 iSoldierBountiesKingpinDuration_Bonus_Covert; + UINT8 iSoldierBountiesKingpinDuration_Bonus_Demolitions; + UINT16 iSoldierBountiesKingpinPayout_Admin; + UINT16 iSoldierBountiesKingpinPayout_Troop; + UINT16 iSoldierBountiesKingpinPayout_Elite; + UINT16 iSoldierBountiesKingpinPayout_Robot; + UINT16 iSoldierBountiesKingpinPayout_Jeep; + UINT16 iSoldierBountiesKingpinPayout_Tank; + UINT16 iSoldierBountiesKingpinPayout_Officer; + INT16 iSoldierBountiesKingpinPayout_Limit; + INT16 iSoldierBountiesKingpinPayout_Limit_Demolitions; + INT16 iSoldierBountiesKingpinPayout_Limit_Snitch; + FLOAT fSoldierBountiesKingpinPayout_Bonus_Covert; + FLOAT fSoldierBountiesKingpinPayout_Bonus_Deputy; + FLOAT fSoldierBountiesKingpinPayout_Bonus_Snitch; + } REBELCOMMAND_SETTINGS; typedef struct diff --git a/GameVersion.cpp b/GameVersion.cpp index 99f52ce6d..7f4a6d8fa 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -57,6 +57,6 @@ CHAR8 czVersionNumber[16] = { "Build 22.11.04" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; -CHAR16 zRevisionNumber[16] = { L"Revision 9404" }; +CHAR16 zRevisionNumber[16] = { L"Revision 9405" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/Laptop/finances.cpp b/Laptop/finances.cpp index 261f02a82..a65f22889 100644 --- a/Laptop/finances.cpp +++ b/Laptop/finances.cpp @@ -1612,6 +1612,10 @@ void ProcessTransactionString(STR16 pString, FinanceUnitPtr pFinance) case REBEL_COMMAND_SPENDING: swprintf(pString, L"%s", pTransactionText[REBEL_COMMAND_SPENDING]); break; + + case REBEL_COMMAND_BOUNTY_PAYOUT: + swprintf(pString, L"%s", pTransactionText[REBEL_COMMAND_BOUNTY_PAYOUT]); + break; } } diff --git a/Laptop/finances.h b/Laptop/finances.h index b617e1980..6c03e4861 100644 --- a/Laptop/finances.h +++ b/Laptop/finances.h @@ -62,6 +62,7 @@ enum MINI_EVENT, // rftr: mini events REBEL_COMMAND, // rftr: rebel command REBEL_COMMAND_SPENDING, // rftr: rebel command + REBEL_COMMAND_BOUNTY_PAYOUT, // rftr: rebel command soldier bounties TEXT_NUM_FINCANCES }; diff --git a/Strategic/ASD.cpp b/Strategic/ASD.cpp index 84c670850..ceee99cfa 100644 --- a/Strategic/ASD.cpp +++ b/Strategic/ASD.cpp @@ -52,6 +52,7 @@ #include "Sound Control.h" #include "renderworld.h" #include "Isometric Utils.h" +#include "Rebel Command.h" #endif @@ -548,9 +549,22 @@ UINT32 ASDResourceCostMoney( UINT8 aType ) return gGameExternalOptions.gASDResource_Cost[aType]; } +INT32 GetStrategicAIResourceCount( UINT8 aType ) +{ + if (aType < 0 || aType >= ASD_RESOURCE_MAX) + return 0; + + return gASDResource[aType]; +} + // add resources to the AIs resource pool void AddStrategicAIResources( UINT8 aType, INT32 aAmount ) { + if (aType == ASD_MONEY) + { + aAmount *= RebelCommand::GetASDIncomeModifier(); + } + gASDResource[aType] = max( 0, gASDResource[aType] + aAmount ); if ( aType == ASD_HELI ) @@ -1500,7 +1514,7 @@ UINT32 ASDResourceCostFuel( UINT8 aType ) // if ASD has tanks, it can allow the queen to upgrade soldiers to tanks BOOLEAN ASDSoldierUpgradeToTank( ) { - if ( gGameExternalOptions.fASDActive && gGameExternalOptions.fASDAssignsTanks ) + if ( gGameExternalOptions.fASDActive && gGameExternalOptions.fASDAssignsTanks && RebelCommand::GetASDCanDeployUnits() ) { if ( gASD_Flags & ASDFACT_TANK_UNLOCKED ) { @@ -1517,7 +1531,7 @@ BOOLEAN ASDSoldierUpgradeToTank( ) BOOLEAN ASDSoldierUpgradeToJeep( ) { - if ( gGameExternalOptions.fASDActive && gGameExternalOptions.fASDAssignsJeeps ) + if ( gGameExternalOptions.fASDActive && gGameExternalOptions.fASDAssignsJeeps && RebelCommand::GetASDCanDeployUnits() ) { if ( gASD_Flags & ASDFACT_JEEP_UNLOCKED ) { @@ -1534,7 +1548,7 @@ BOOLEAN ASDSoldierUpgradeToJeep( ) BOOLEAN ASDSoldierUpgradeToRobot( ) { - if ( gGameExternalOptions.fASDActive && gGameExternalOptions.fASDAssignsRobots ) + if ( gGameExternalOptions.fASDActive && gGameExternalOptions.fASDAssignsRobots && RebelCommand::GetASDCanDeployUnits() ) { if ( gASD_Flags & ASDFACT_ROBOT_UNLOCKED ) { diff --git a/Strategic/ASD.h b/Strategic/ASD.h index 0b786fbec..62cd5b185 100644 --- a/Strategic/ASD.h +++ b/Strategic/ASD.h @@ -56,6 +56,8 @@ void SetASDFlag( UINT32 aFlag ); UINT32 ASDResourceDeliveryTime( UINT8 aType ); UINT32 ASDResourceCostMoney( UINT8 aType ); +INT32 GetStrategicAIResourceCount( UINT8 aType ); + // add resources to the AIs resource pool void AddStrategicAIResources( UINT8 aType, INT32 aAmount ); diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index 0e6398d6f..04dad5007 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -813,6 +813,11 @@ BOOLEAN BasicCanCharacterAssignment( SOLDIERTYPE * pSoldier, BOOLEAN fNotInComba return( FALSE ); } + if (pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + { + return( FALSE ); + } + return( TRUE ); } @@ -1658,6 +1663,9 @@ BOOLEAN BasicCanCharacterTrainMilitia( SOLDIERTYPE *pSoldier ) // check if sam site if( fSamSitePresent == FALSE ) { + if (RebelCommand::CanTrainMilitiaAnywhere()) + return( TRUE ); + // nope return ( FALSE ); } @@ -1971,6 +1979,9 @@ BOOLEAN CanCharacterTrainMilitia( SOLDIERTYPE *pSoldier ) } } + if (RebelCommand::CanTrainMilitiaAnywhere() && GetTownIdForSector(pSoldier->sSectorX, pSoldier->sSectorY) == BLANK_SECTOR) + ubFacilityTrainersAllowed = RebelCommand::GetMaxTrainersForTrainMilitiaAnywhere(); + // Count number of trainers already operating here if ( CountMilitiaTrainersInSoldiersSector( pSoldier, TOWN_MILITIA ) >= ubFacilityTrainersAllowed ) { @@ -2033,6 +2044,9 @@ BOOLEAN DoesSectorMercIsInHaveSufficientLoyaltyToTrainMilitia( SOLDIERTYPE *pSol { return( TRUE ); } + + if (RebelCommand::CanTrainMilitiaAnywhere()) + return(TRUE); return( FALSE ); } @@ -2085,7 +2099,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 ) @@ -2491,7 +2505,7 @@ BOOLEAN CanCharacterSleep( SOLDIERTYPE *pSoldier, BOOLEAN fExplainWhyNot ) } // POW? - if( pSoldier->bAssignment == ASSIGNMENT_POW || pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) + if( pSoldier->bAssignment == ASSIGNMENT_POW || pSoldier->bAssignment == ASSIGNMENT_MINIEVENT || pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) { return( FALSE ); } @@ -2709,7 +2723,7 @@ INT8 CanCharacterSquad( SOLDIERTYPE *pSoldier, INT8 bSquadValue ) return ( CHARACTER_CANT_JOIN_SQUAD ); } - if ( pSoldier->bAssignment == ASSIGNMENT_POW || (pSoldier->bAssignment == ASSIGNMENT_MINIEVENT && pSoldier->ubHoursRemainingOnMiniEvent > 0)) + if ( pSoldier->bAssignment == ASSIGNMENT_POW || (pSoldier->bAssignment == ASSIGNMENT_MINIEVENT && pSoldier->ubHoursRemainingOnMiniEvent > 0) || (pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) ) { // not allowed to be put on a squad return( CHARACTER_CANT_JOIN_SQUAD ); @@ -5830,7 +5844,7 @@ void FatigueCharacter( SOLDIERTYPE *pSoldier ) } // POW? - if( pSoldier->bAssignment == ASSIGNMENT_POW || pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) + if( pSoldier->bAssignment == ASSIGNMENT_POW || pSoldier->bAssignment == ASSIGNMENT_MINIEVENT || pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) { return; } @@ -6136,7 +6150,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 +7913,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); } @@ -10708,7 +10723,7 @@ void HandleShadingOfLinesForAssignmentMenus( void ) } // radio scan - if( pSoldier->CanUseRadio() ) + if( BasicCanCharacterAssignment( pSoldier, TRUE ) && pSoldier->CanUseRadio() ) { // unshade line UnShadeStringInBox( ghAssignmentBox, ASSIGN_MENU_RADIO_SCAN ); @@ -16312,7 +16327,7 @@ void HandleRestFatigueAndSleepStatus( void ) continue; } - if( ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == IN_TRANSIT ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) ) + if( ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == IN_TRANSIT ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) ) { continue; } @@ -16461,7 +16476,7 @@ void HandleRestFatigueAndSleepStatus( void ) continue; } - if( ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == IN_TRANSIT ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) ) + if( ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == IN_TRANSIT ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) ) { continue; } @@ -19442,7 +19457,8 @@ BOOLEAN CanCharacterRepairAnotherSoldiersStuff( SOLDIERTYPE *pSoldier, SOLDIERTY ( AM_A_ROBOT( pSoldier ) ) || ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) || ( pOtherSoldier->bAssignment == ASSIGNMENT_DEAD ) || - ( pOtherSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) ) + ( pOtherSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || + ( pOtherSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) ) { return( FALSE ); } @@ -20165,6 +20181,9 @@ BOOLEAN CanCharacterTrainMilitiaWithErrorReport( SOLDIERTYPE *pSoldier ) } } + if (RebelCommand::CanTrainMilitiaAnywhere() && GetTownIdForSector(pSoldier->sSectorX, pSoldier->sSectorY) == BLANK_SECTOR) + 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 a3c731ae0..03672ed70 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 e951f7659..261fbd80d 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 20077c042..29e08bb59 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 a698d903f..ffcc77b82 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/Hourly Update.cpp b/Strategic/Hourly Update.cpp index 800e5603e..876211ad6 100644 --- a/Strategic/Hourly Update.cpp +++ b/Strategic/Hourly Update.cpp @@ -712,6 +712,7 @@ void HourlyStealUpdate() && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT + && pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND && !( ( ( gWorldSectorX == pSoldier->sSectorX ) && ( gWorldSectorY == pSoldier->sSectorY ) && ( gbWorldSectorZ == pSoldier->bSectorZ ) ) && ( gTacticalStatus.fEnemyInSector || guiCurrentScreen == GAME_SCREEN ) ) ) { UINT8 ubSectorId = SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ); @@ -741,6 +742,7 @@ void HourlyStealUpdate() && pOtherSoldier->bAssignment != IN_TRANSIT && pOtherSoldier->bAssignment != ASSIGNMENT_POW && pOtherSoldier->bAssignment != ASSIGNMENT_MINIEVENT + && pOtherSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND && !SPY_LOCATION( pOtherSoldier->bAssignment ) && pOtherSoldier->bActive && !pOtherSoldier->flags.fMercAsleep diff --git a/Strategic/Map Screen Interface Bottom.cpp b/Strategic/Map Screen Interface Bottom.cpp index f1a730c4d..004a67e4c 100644 --- a/Strategic/Map Screen Interface Bottom.cpp +++ b/Strategic/Map Screen Interface Bottom.cpp @@ -1795,6 +1795,7 @@ BOOLEAN AnyUsableRealMercenariesOnTeam( void ) ( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != ASSIGNMENT_DEAD ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && + ( pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND ) && ( pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__EPC ) ) { return( TRUE ); diff --git a/Strategic/Map Screen Interface Map.cpp b/Strategic/Map Screen Interface Map.cpp index 0f0619faa..185549d92 100644 --- a/Strategic/Map Screen Interface Map.cpp +++ b/Strategic/Map Screen Interface Map.cpp @@ -49,6 +49,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 +845,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; + } + } } @@ -1328,6 +1365,7 @@ INT32 ShowAssignedTeam(INT16 sMapX, INT16 sMapY, INT32 iCount) ( pSoldier->bAssignment != IN_TRANSIT ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && + ( pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND ) && ( pSoldier->stats.bLife > 0 ) && ( !PlayerIDGroupInMotion( pSoldier->ubGroupID ) ) ) { @@ -6938,6 +6976,7 @@ BOOLEAN CanMercsScoutThisSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == ASSIGNMENT_DEAD ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || + ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) || ( pSoldier->flags.fMercAsleep == TRUE ) || ( pSoldier->stats.bLife < OKLIFE ) ) { diff --git a/Strategic/Map Screen Interface.cpp b/Strategic/Map Screen Interface.cpp index 409747576..41111229f 100644 --- a/Strategic/Map Screen Interface.cpp +++ b/Strategic/Map Screen Interface.cpp @@ -1938,7 +1938,7 @@ void UpdateCharRegionHelpText( void ) pSoldier = MercPtrs[ gCharactersList[ bSelectedInfoChar ].usSolID ]; // health/energy/morale - if( pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) + if( pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT && pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND ) { if ( pSoldier->stats.bLife != 0 ) { @@ -3713,7 +3713,7 @@ void SetUpMovingListsForSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ) pSoldier = MercPtrs[ gCharactersList[ iCounter ].usSolID ]; if( ( pSoldier->bActive ) && - ( pSoldier->bAssignment != IN_TRANSIT ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && !SPY_LOCATION( pSoldier->bAssignment ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && + ( pSoldier->bAssignment != IN_TRANSIT ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && !SPY_LOCATION( pSoldier->bAssignment ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && ( pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND ) && ( pSoldier->sSectorX == sSectorX ) && ( pSoldier->sSectorY == sSectorY ) && ( pSoldier->bSectorZ == sSectorZ ) ) { if ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) @@ -6063,8 +6063,8 @@ BOOLEAN CanCharacterMoveInStrategic( SOLDIERTYPE *pSoldier, INT8 *pbErrorNumber return( FALSE ); } - // mini event? - if ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) + // mini event/rebel command? + if ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT || pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) { *pbErrorNumber = 29; return( FALSE ); diff --git a/Strategic/MiniEvents.cpp b/Strategic/MiniEvents.cpp index da9f22ee3..905a8f9dc 100644 --- a/Strategic/MiniEvents.cpp +++ b/Strategic/MiniEvents.cpp @@ -1532,6 +1532,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/PreBattle Interface.cpp b/Strategic/PreBattle Interface.cpp index f285cd2e6..d27d0654b 100644 --- a/Strategic/PreBattle Interface.cpp +++ b/Strategic/PreBattle Interface.cpp @@ -2376,6 +2376,7 @@ BOOLEAN PlayerMercInvolvedInThisCombat( SOLDIERTYPE *pSoldier ) pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != ASSIGNMENT_DEAD && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT && + pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) && // Robot is involved if it has a valid controller with it, uninvolved otherwise ( !AM_A_ROBOT( pSoldier ) || ( pSoldier->ubRobotRemoteHolderID != NOBODY ) ) && diff --git a/Strategic/Queen Command.cpp b/Strategic/Queen Command.cpp index eb32fe440..497d71b58 100644 --- a/Strategic/Queen Command.cpp +++ b/Strategic/Queen Command.cpp @@ -299,7 +299,7 @@ UINT16 NumPlayerTeamMembersInSector( INT16 sSectorX, INT16 sSectorY, INT8 sSecto // we test several conditions before we allow adding an opinion // other merc must be active, have a profile, be someone else and not be in transit or dead if ( pTeamSoldier->bActive && !pTeamSoldier->flags.fBetweenSectors && pTeamSoldier->stats.bLife > 0 && !(pTeamSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) && - !(pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD || pTeamSoldier->bAssignment == ASSIGNMENT_POW || pTeamSoldier->bAssignment == ASSIGNMENT_MINIEVENT) && + !(pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD || pTeamSoldier->bAssignment == ASSIGNMENT_POW || pTeamSoldier->bAssignment == ASSIGNMENT_MINIEVENT || pTeamSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) && (pTeamSoldier->sSectorX == sSectorX && pTeamSoldier->sSectorY == sSectorY && pTeamSoldier->bSectorZ == sSectorZ) ) { ++teammemberspresent; @@ -2795,6 +2795,11 @@ void EnemyCapturesPlayerSoldier( SOLDIERTYPE *pSoldier ) return; } + if (pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + { + return; + } + if ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) { return; diff --git a/Strategic/Rebel Command.cpp b/Strategic/Rebel Command.cpp index 2291b913c..181eca14d 100644 --- a/Strategic/Rebel Command.cpp +++ b/Strategic/Rebel Command.cpp @@ -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 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,15 @@ 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 PrepareMission() +- add mission-specific functions + Points of interest: - Init() - set up rebel command for the first time - SetupInfo() - set constants @@ -50,6 +62,7 @@ Points of interest: #else #include "Rebel Command.h" +#include "ASD.h" #include "Button System.h" #include "Campaign.h" #include "Campaign Types.h" @@ -60,34 +73,45 @@ 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 "Quests.h" #include "random.h" #include "SaveLoadGame.h" +#include "Soldier macros.h" +#include "Squads.h" +#include "strategic.h" #include "strategicmap.h" #include "Strategic Mines.h" #include "Strategic Movement.h" #include "Strategic Town Loyalty.h" +#include "Structure Wrap.h" +#include "Tactical Save.h" #include "Text.h" #include "Town Militia.h" #include "Utilities.h" +#include "Vehicles.h" #include "WCheck.h" #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,29 +121,149 @@ 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 ItemIdCache +{ + // cache these values on load so that we don't need to search for them every time something happens + std::vector gasCans; + std::vector firstAidKits; + std::vector medKits; + std::vector toolKits; + std::vector ammo[10]; // coolness + + void Clear() + { + gasCans.clear(); + firstAidKits.clear(); + medKits.clear(); + toolKits.clear(); + for (int i = 0; i < 10; ++i) + { + ammo[i].clear(); + } + } +} + +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 DISRUPT_ASD_STEAL_FUEL = 1; +constexpr UINT16 DISRUPT_ASD_DESTROY_RESERVES = 2; +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; +constexpr UINT16 SOLDIER_BOUNTIES_KINGPIN_OFFICER_PAYOUTS = 1; +constexpr UINT16 SOLDIER_BOUNTIES_KINGPIN_VEHICLE_PAYOUTS = 2; + + +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; + + if (merc == nullptr) + return; + + if (mission == RCAM_NONE) + return; + + 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(); 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, @@ -177,6 +321,59 @@ enum RebelCommandText // keep this synced with szRebelCommandText in the text fi RCT_PREV_ARROW, RCT_NEXT_ARROW, RCT_CONFIRM_CHANGE_ADMIN_ACTION_PROMPT, + RCT_INSUFFICIENT_SUPPLIES_ADMIN_ACTIONS_DISABLED, + RCT_NEW_MISSIONS_AVAILABLE_TIME, + RCT_NEW_MISSIONS_AVAILABLE_NOTIFICATION, + RCT_MISSION_PREP_IN_PROGRESS, + RCT_MISSION_DURATION_DAYS, + RCT_MISSION_SUCCESS_CHANCE, + RCT_MISSION_AGENT_REDACTED, + RCT_MISSION_AGENT_NAME, + RCT_MISSION_AGENT_LOCATION, + RCT_MISSION_AGENT_ASSIGNMENT, + RCT_MISSION_AGENT_CONTRACT_DAYS, + RCT_MISSION_AGENT_CONTRACT_HOURS, + RCT_MISSION_AGENT_CONTRACT_NONE, + RCT_MISSION_AGENT_BONUS, + RCT_MISSION_BONUS_SUCCESS_CHANCE, + RCT_MISSION_BONUS_DEPLOY_RANGE, + RCT_MISSION_BONUS_ASD_INCOME_REDUCTION, + RCT_MISSION_BONUS_STEAL_FUEL, + RCT_MISSION_BONUS_DESTROY_RESERVES, + RCT_MISSION_BONUS_DECISION_TIME, + RCT_MISSION_BONUS_UNALERTED_VISION_PENALTY, + RCT_MISSION_BONUS_INFANTRY_GEAR_QUALITY, + RCT_MISSION_BONUS_MECHANICAL_STAT_LOSS, + RCT_MISSION_BONUS_MAX_TRAINERS, + RCT_MISSION_BONUS_PAYOUT, + RCT_MISSION_BONUS_PAYOUT_LIMIT_INCREASE, + RCT_MISSION_BONUS_OFFICER_PAYOUT, + RCT_MISSION_BONUS_VEHICLE_PAYOUT, + RCT_MISSION_BONUS_DURATION, + RCT_MISSION_CANT_START_NOT_IN_TOWN, + RCT_MISSION_CANT_START_LOW_LOYALTY, + RCT_MISSION_CANT_START_AGENT_UNAVAILABLE, + RCT_MISSION_CANT_START_CONTRACT_EXPIRING, + RCT_MISSION_CANT_USE_REBEL_AGENT, + RCT_MISSION_CANT_START_BATTLE_IN_PROGRESS, + RCT_MISSION_START_BUTTON, + RCT_MISSION_VIEW_ACTIVE, + RCT_MISSION_VIEW_LIST, + RCT_MISSION_HELP_1, + RCT_MISSION_HELP_2, + RCT_MISSION_HELP_3, + RCT_MISSION_NEXT_AVAILABILITY, + RCT_MISSION_ACTIVE_MISSIONS, + RCT_MISSION_LIST_PREPARING, + RCT_MISSION_LIST_ACTIVE, + RCT_MISSION_POPUP_PART1, + RCT_MISSION_POPUP_PART2_GENERIC, + RCT_MISSION_POPUP_PART2_MALE, + RCT_MISSION_POPUP_PART2_FEMALE, + RCT_MISSION_SUCCESS, + RCT_MISSION_FAILURE, + RCT_MISSION_EXPIRED, + }; enum RebelCommandHelpText // keep this synced with szRebelCommandHelpText in the text files @@ -187,7 +384,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 +406,124 @@ enum RebelCommandDirectivesText // keep this synced with szRebelCommandDirective DIRECTIVE_TEXT(DRAFT) }; +enum RebelCommandAgentMissionsText // keep this synced with szRebelCommandAgentMissionsText in the text files +{ + MISSION_TEXT(DEEP_DEPLOYMENT) + MISSION_TEXT(DISRUPT_ASD) + 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(SEND_SUPPLIES_TO_TOWN) + MISSION_TEXT(SOLDIER_BOUNTIES_KINGPIN) + MISSION_TEXT(TRAIN_MILITIA_ANYWHERE) +}; + enum ChangeAdminActionState { CAAS_INIT, CAAS_CHANGING, }; +enum MissionOverviewSubview +{ + MOS_MISSION_LIST, + MOS_ACTIVE_MISSION_EFFECTS, + MOS_HELP, +}; + +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,16 +531,26 @@ 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 PrepareMission(INT8 index); void ToggleWebsiteView(); void UpdateAdminActionChangeList(INT16 regionId); +void ApplyAdditionalASDEffects(); +constexpr BOOLEAN CanAdminActionBeToggled(RebelCommandAdminActions action) { return action != RebelCommandAdminActions::RCAA_SUPPLY_LINE; } +BOOLEAN CanAdminActionBeUsed(INT32 regionIndex, INT32 actionIndex); +void CreateItemAtAirport(UINT16 itemId, INT16 status); INT32 GetAdminActionCostForRegion(INT16 regionId); INT16 GetAdminActionInRegion(INT16 regionId, RebelCommandAdminActions adminAction); UINT8 GetRegionLoyalty(INT16 regionId); void HandleScouting(); +void SendSuppliesToTownMission(); +INT16 SendSuppliesToTownDurationBonus(const MERCPROFILESTRUCT* merc); void SetupInfo(); void UpgradeMilitiaStats(); @@ -250,6 +559,7 @@ std::vector btnIds; ChangeAdminActionState adminActionChangeState; // help text regions +MOUSE_REGION adminActionActiveTextRegion[5]; MOUSE_REGION adminActionHelpTextRegion[6]; MOUSE_REGION adminTeamHelpTextRegion; MOUSE_REGION directiveDescriptionHelpTextRegion; @@ -266,6 +576,9 @@ INT16 iCurrentRegionId = 1; INT32 iIncomingSuppliesPerDay = 0; SaveInfo rebelCommandSaveInfo; WebsiteState websiteState; +INT8 agentIndex[NUM_ARC_AGENT_SLOTS]; +std::unordered_map missionMap; +MissionOverviewSubview missionOverviewSubview = MOS_MISSION_LIST; // website template @@ -285,6 +598,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,6 +631,8 @@ 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); @@ -375,6 +712,42 @@ 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; +} + +void CreateItemAtAirport(UINT16 itemId, INT16 status) +{ + // create an item - use bobby ray's delivery coordinates/gridno + OBJECTTYPE obj; + const BOOLEAN success = CreateItem(itemId, status, &obj); + if (!success) + { + ScreenMsg(FONT_MCOLOR_RED, MSG_INTERFACE, L"Warning - CreateItemAtAirport() failed for itemid %d", itemId); + return; + } + const BOOLEAN airportSectorLoaded = gWorldSectorX == BOBBYR_SHIPPING_DEST_SECTOR_X && gWorldSectorY == BOBBYR_SHIPPING_DEST_SECTOR_Y && gbWorldSectorZ == BOBBYR_SHIPPING_DEST_SECTOR_Z; + + if (airportSectorLoaded) + { + SetOpenableStructureToClosed( BOBBYR_SHIPPING_DEST_GRIDNO, 0 ); + AddItemToPool( BOBBYR_SHIPPING_DEST_GRIDNO, &obj, -1, 0, 0, 0 ); + } + else + { + AddItemsToUnLoadedSector(BOBBYR_SHIPPING_DEST_SECTOR_X, BOBBYR_SHIPPING_DEST_SECTOR_Y, BOBBYR_SHIPPING_DEST_SECTOR_Z, BOBBYR_SHIPPING_DEST_GRIDNO, 1, &obj, 0, 0, 0, -1, FALSE); + } +} + INT32 GetAdminActionCostForRegion(INT16 regionId) { INT16 totalLocalActions = 0; @@ -385,10 +758,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 +774,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 +826,17 @@ INT32 GetDirectiveImprovementCost(const RebelCommandDirectives directive) return rebelCommandSaveInfo.directives[directive].GetCostToImprove(); } +INT32 GetMissionCost() +{ + const INT32 additionalCost = missionMap.size() * gRebelCommandSettings.iMissionAdditionalCost; + return gRebelCommandSettings.iMissionBaseCost + 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 +936,114 @@ 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(); + }); + + if (rebelCommandSaveInfo.iSupplies <= 0) + DisableButton(btnId); + + 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 +1174,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) @@ -760,14 +1238,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 +1248,6 @@ void ExitWebsite() ClearAllButtons(); ClearAllHelpTextRegions(); REBEL_COMMAND_DROPDOWN.Destroy(); - - DeleteVideoObjectFromIndex(guiInsuranceBackGround); } void HandleWebsite() @@ -809,6 +1277,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 +1316,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 +1327,10 @@ void RenderWebsite() RenderRegionalOverview(); break; + case RCS_AGENT_OVERVIEW: + RenderMissionOverview(); + break; + case RCS_NATIONAL_OVERVIEW: default: RenderNationalOverview(); @@ -861,10 +1348,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 +1361,69 @@ 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)); + { + 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()) @@ -899,12 +1435,6 @@ void RenderHeader(RebelCommandText titleText) ButtonHelper(btn, reason, []() { DEBUG_DAY(); }); }); btnIds.push_back(btnId); - - usPosY = WEBSITE_TOP + 365; - btnId = CreateTextButton(L"DEBUG PRINT!", FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, usPosX, usPosY, 99, 14, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) { - ButtonHelper(btn, reason, []() { DEBUG_PRINT(); }); - }); - btnIds.push_back(btnId); } } @@ -920,15 +1450,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 +1457,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 +1475,61 @@ 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)); + { + 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 +1549,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 +1583,18 @@ void RenderNationalOverview() // line between directive and militia usPosX = WEBSITE_LEFT - 1; usPosY += 10; - DisplaySmallColouredLineWithShadow(usPosX, usPosY, usPosX + 500, 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 +1641,18 @@ void RenderNationalOverview() // draw vertical line usPosX += 75; usPosY = militiaY - 3; - 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 +1673,18 @@ void RenderNationalOverview() // draw vertical line usPosX += 75; usPosY = militiaY - 3; - 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 +1711,18 @@ void RenderNationalOverview() // line usPosX = WEBSITE_LEFT + 25; usPosY = militiaY + 50; - 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,10 +1731,21 @@ void RenderNationalOverview() // draw vertical line usPosX += 120; - DisplaySmallColouredLineWithShadow(usPosX, usPosY - 2, usPosX, usPosY + 38, FROMRGB(240, 240, 240)); + { + UINT32 uiDestPitchBYTES; + UINT8 *pDestBuf; - // upkeep cost - usPosX += 20; + 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; DrawTextToScreen(szRebelCommandText[RCT_MILITIA_UPKEEP_COST], usPosX, usPosY, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); // upkeep cost per type @@ -1164,7 +1785,18 @@ void RenderNationalOverview() // line usPosX = WEBSITE_LEFT + 25; usPosY += 30; - 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 +1819,18 @@ void RenderNationalOverview() // draw vertical line usPosX = WEBSITE_LEFT + 130; usPosY -= 12; - 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 +1876,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 +1911,19 @@ 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; + { + 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 +1961,18 @@ 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)); + { + 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 +2003,18 @@ 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)); + { + 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 +2042,20 @@ 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) { - // 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 +2097,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 +2118,30 @@ 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; + { + 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,674 +2165,1510 @@ 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); } -// end website -void ApplyEnemyPenalties(SOLDIERTYPE* pSoldier) +BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index) { - if (!gGameExternalOptions.fRebelCommandEnabled) - return; - - const auto applyPenalties = [](SOLDIERTYPE* pSoldier, UINT8 level) - { - pSoldier->stats.bLife -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bLifeMax -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bAgility -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bDexterity -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bStrength -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bWisdom -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bLeadership -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bMarksmanship -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bMechanical -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bExplosive -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->stats.bMedical -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->bBreath -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - pSoldier->bBreathMax -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); - - pSoldier->stats.bLife = static_cast(max(25, pSoldier->stats.bLife)); - pSoldier->stats.bLifeMax = static_cast(max(25, pSoldier->stats.bLifeMax)); - pSoldier->stats.bAgility = static_cast(max(25, pSoldier->stats.bAgility)); - pSoldier->stats.bDexterity = static_cast(max(25, pSoldier->stats.bDexterity)); - pSoldier->stats.bStrength = static_cast(max(25, pSoldier->stats.bStrength)); - pSoldier->stats.bWisdom = static_cast(max(25, pSoldier->stats.bStrength)); - pSoldier->stats.bLeadership = static_cast(max(25, pSoldier->stats.bLeadership)); - pSoldier->stats.bMarksmanship = static_cast(max(25, pSoldier->stats.bMarksmanship)); - pSoldier->stats.bMechanical = static_cast(max(25, pSoldier->stats.bMechanical)); - pSoldier->stats.bExplosive = static_cast(max(25, pSoldier->stats.bExplosive)); - pSoldier->stats.bMedical = static_cast(max(25, pSoldier->stats.bMedical)); - pSoldier->bBreath = static_cast(max(25, info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level)); - pSoldier->bBreathMax = static_cast(max(25, info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level)); - }; - - // need to get dist between soldier and town. - // run through all towns, check manhattan distance - UINT8 foundLevel = 0; - UINT8 foundLoyalty = 0; - for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) + 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) { - // make sure town has active admins - if (rebelCommandSaveInfo.regions[a].adminStatus != RAS_ACTIVE) - continue; - - // make sure action exists in this town - const INT16 index = GetAdminActionInRegion(a, RCAA_SUPPLY_DISRUPTION); - if (index == -1) - continue; - - // and that it's not level 0 - const UINT8 level = rebelCommandSaveInfo.regions[a].actionLevels[index]; - if (level == 0) - continue; - - // get all sectors with this townid - std::vector> sectors; - for (int x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) - for (int y = MINIMUM_VALID_Y_COORDINATE; y <= MAXIMUM_VALID_Y_COORDINATE; ++y) - if (GetTownIdForSector(x, y) == a) - sectors.push_back(std::tuple(x, y, GetRegionLoyalty(a))); + SOLDIERTYPE* pSoldier = MercPtrs[i]; - // check if soldier is within range of the city - for (const auto tuple : sectors) + if (pSoldier && pSoldier->bActive + && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) + ) { - const INT16 x = std::get<0>(tuple); - const INT16 y = std::get<1>(tuple); - const UINT8 loyalty = std::get<2>(tuple); - - if (abs(x - pSoldier->sSectorX) + abs(y - pSoldier->sSectorY) <= level) - { - if (level > foundLevel) - { - foundLevel = level; - foundLoyalty = loyalty; - } - else if (level == foundLevel) - { - foundLoyalty = max(loyalty, foundLoyalty); - } - } + mercs.push_back(pSoldier); } } - // loyalty determines success rate - if (foundLevel > 0 && Random(100) <= foundLoyalty) - applyPenalties(pSoldier, foundLevel); -} + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); -void ApplyMilitiaBonuses(SOLDIERTYPE* pMilitia) -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return; + SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - pMilitia->stats.bLife += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); - pMilitia->stats.bLifeMax += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); - pMilitia->stats.bAgility += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); - pMilitia->stats.bDexterity += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); - pMilitia->stats.bStrength += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); - pMilitia->stats.bMarksmanship += (gRebelCommandSettings.iMilitiaMarksmanshipBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); + // top horizontal line + LineDraw(FALSE, x, y, x+230, y, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); + + // bottom horizontal line + LineDraw(FALSE, x, y+310, x+230, y+310, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); - pMilitia->stats.bLife = min(100, pMilitia->stats.bLife); - pMilitia->stats.bLifeMax = min(100, pMilitia->stats.bLifeMax); - pMilitia->stats.bAgility = min(100, pMilitia->stats.bAgility); - pMilitia->stats.bDexterity = min(100, pMilitia->stats.bDexterity); - pMilitia->stats.bStrength = min(100, pMilitia->stats.bStrength); - pMilitia->stats.bMarksmanship = min(100, pMilitia->stats.bMarksmanship); -} + // left vertical line + LineDraw(FALSE, x, y, x, y+310, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); -UINT8 GetApproximateEnemyLocationResolutionIndex() -{ - return static_cast(rebelCommandSaveInfo.directives[RCD_SPOTTERS].GetValue1()); -} + // right vertical line + LineDraw(FALSE, x+230, y, x+230, y+310, Get16BPPColor(FROMRGB(0, 0, 0)), pDestBuf); -FLOAT GetAssignmentBonus(INT16 x, INT16 y) -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return 0.f; + UnLockVideoSurface( FRAME_BUFFER ); - const UINT8 townId = GetTownIdForSector(x, y); + // clamp indices + // 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; - // make sure town has active admins - if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) - return 0.f; + if (rebelCommandSaveInfo.availableMissions[index] == RCAM_NONE) + { + // we shouldn't even reach this point, but leaving this here for safety + DrawTextToScreen(szRebelCommandText[RCT_MISSION_PREP_IN_PROGRESS], x, y+155, 230, FONT14ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED); + return FALSE; + } - const INT16 index = GetAdminActionInRegion(townId, RCAA_MERC_SUPPORT); - FLOAT value = 0.f; + // draw mission title + switch (rebelCommandSaveInfo.availableMissions[index]) + { + case RCAM_DEEP_DEPLOYMENT: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_DEEP_DEPLOYMENT_TITLE]); break; + case RCAM_DISRUPT_ASD: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_DISRUPT_ASD_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_SEND_SUPPLIES_TO_TOWN: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_SEND_SUPPLIES_TO_TOWN_TITLE]); break; + case RCAM_SOLDIER_BOUNTIES_KINGPIN: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_SOLDIER_BOUNTIES_KINGPIN_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); - if (index >= 0) + // draw mission base duration, in days + UINT8 missionDurationBase = 0; + switch (rebelCommandSaveInfo.availableMissions[index]) { - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; - value += info.adminActions[RCAA_MERC_SUPPORT].fValue1 * level; + case RCAM_DEEP_DEPLOYMENT: missionDurationBase = gRebelCommandSettings.iDeepDeploymentDuration; break; + case RCAM_DISRUPT_ASD: missionDurationBase = gRebelCommandSettings.iDisruptAsdDuration; 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_SEND_SUPPLIES_TO_TOWN: missionDurationBase = gRebelCommandSettings.iSendSuppliesToTownDuration; break; + case RCAM_SOLDIER_BOUNTIES_KINGPIN: missionDurationBase = gRebelCommandSettings.iSoldierBountiesKingpinDuration; break; + case RCAM_TRAIN_MILITIA_ANYWHERE: missionDurationBase = gRebelCommandSettings.iTrainMilitiaAnywhereDuration; break; + + default: break; + } + // convert from hours + missionDurationBase /= 24; + swprintf(sText, szRebelCommandText[RCT_MISSION_DURATION_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_DISRUPT_ASD: missionSuccessChanceBase = gRebelCommandSettings.iDisruptAsdSuccessChance; 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_SEND_SUPPLIES_TO_TOWN: missionSuccessChanceBase = gRebelCommandSettings.iSendSuppliesToTownSuccessChance; break; + case RCAM_SOLDIER_BOUNTIES_KINGPIN: missionSuccessChanceBase = gRebelCommandSettings.iSoldierBountiesKingpinSuccessChance; break; + case RCAM_TRAIN_MILITIA_ANYWHERE: missionSuccessChanceBase = gRebelCommandSettings.iTrainMilitiaAnywhereSuccessChance; break; + + default: break; } + swprintf(sText, szRebelCommandText[RCT_MISSION_SUCCESS_CHANCE], missionSuccessChanceBase, L"%%"); + DrawTextToScreen(sText, x+5, y+33, 0, FONT10ARIALBOLD, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); - return value/100.f; -} + // draw mission description + switch (rebelCommandSaveInfo.availableMissions[index]) + { + case RCAM_DEEP_DEPLOYMENT: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_DEEP_DEPLOYMENT_DESC]); break; + case RCAM_DISRUPT_ASD: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_DISRUPT_ASD_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_SEND_SUPPLIES_TO_TOWN: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_SEND_SUPPLIES_TO_TOWN_DESC]); break; + case RCAM_SOLDIER_BOUNTIES_KINGPIN: swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_SOLDIER_BOUNTIES_KINGPIN_DESC], gRebelCommandSettings.iSoldierBountiesKingpinPayout_Limit); 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); -INT32 GetMiningPolicyBonus(INT16 townId) -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return 0; + if (agentIndex[index] == mercs.size()) // generic rebel agent + { + // 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))); - // make sure town has active admins - if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) - return 0; + // 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); - const INT16 index = GetAdminActionInRegion(townId, RCAA_MINING_POLICY); + // draw name + swprintf(sText, szRebelCommandText[RCT_MISSION_AGENT_NAME], szRebelCommandText[RCT_MISSION_AGENT_REDACTED]); + DrawTextToScreen(sText, x+55, y+150+10, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); - if (index >= 0) - { - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; - return static_cast(info.adminActions[RCAA_MINING_POLICY].fValue1 * level); - } + // draw location + swprintf(sText, szRebelCommandText[RCT_MISSION_AGENT_LOCATION], szRebelCommandText[RCT_MISSION_AGENT_REDACTED]); + DrawTextToScreen(sText, x+55, y+150+22, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); - return 0; -} + // draw assignment + swprintf(sText, szRebelCommandText[RCT_MISSION_AGENT_ASSIGNMENT], szRebelCommandText[RCT_NONE]); + DrawTextToScreen(sText, x+55, y+150+34, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); -void GetBonusMilitia(INT16 sx, INT16 sy, UINT8& green, UINT8& regular, UINT8& elite, BOOLEAN createGroup) -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return; - - const auto createBonusMilitia = [sx, sy, &green, ®ular, &elite, createGroup](UINT8 level) + // draw contract + DrawTextToScreen(szRebelCommandText[RCT_MISSION_AGENT_CONTRACT_NONE], x+55, y+150+46, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + } + else // one of the player's mercs { - if (level == 1) + // 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, szRebelCommandText[RCT_MISSION_AGENT_NAME], 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, szRebelCommandText[RCT_MISSION_AGENT_LOCATION], locationStr); + DrawTextToScreen(sText, x+55, y+150+22, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + + // draw assignment + swprintf( sText, szRebelCommandText[RCT_MISSION_AGENT_ASSIGNMENT], 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) { - green = static_cast(info.adminActions[RCAA_SAFEHOUSES].fValue1 + Random(static_cast(1 + info.adminActions[RCAA_SAFEHOUSES].fValue2))); - regular = Random(2); - elite = 0; + const INT32 endTime = mercs[agentIndex[index]]->iEndofContractTime; + const INT32 worldMin = GetWorldTotalMin(); + const INT32 remaining = endTime - worldMin; + + if (remaining >= 24 * 60) + { + swprintf(sText, szRebelCommandText[RCT_MISSION_AGENT_CONTRACT_DAYS], remaining / (24 * 60)); + } + else + { + swprintf(sText, szRebelCommandText[RCT_MISSION_AGENT_CONTRACT_HOURS], remaining / 60); + } } - else if (level == 2) + else { - green = 0; - regular = static_cast(info.adminActions[RCAA_SAFEHOUSES].fValue1 + Random(static_cast(1 + info.adminActions[RCAA_SAFEHOUSES].fValue2))); - elite = Random(2); + swprintf(sText, szRebelCommandText[RCT_MISSION_AGENT_CONTRACT_NONE]); } + DrawTextToScreen(sText, x+55, y+150+46, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + } - for (int a = 0; a < max(green, max(regular, elite)); ++a) + // 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) { - if (a < green) - CreateNewIndividualMilitia(GREEN_MILITIA, MO_ARULCO, SECTOR(sx, sy)); + const INT8 index = MSYS_GetBtnUserData(btn, 0); + ButtonHelper(btn, reason, [btn, index]() { agentIndex[index]--; }); + }); + MSYS_SetBtnUserData(btnId, 0, index); + btnIds.push_back(btnId); - if (a < regular) - CreateNewIndividualMilitia(REGULAR_MILITIA, MO_ARULCO, SECTOR(sx, sy)); + 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); - if (a < elite) - CreateNewIndividualMilitia(ELITE_MILITIA, MO_ARULCO, SECTOR(sx, sy)); + // draw agent bonus header text + swprintf(sText, szRebelCommandText[RCT_MISSION_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, szRebelCommandText[RCT_MISSION_BONUS_SUCCESS_CHANCE], 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, szRebelCommandText[RCT_MISSION_BONUS_DEPLOY_RANGE], intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(rangeText); } + break; - if (createGroup) + case RCAM_DISRUPT_ASD: { - // randomise entry direction - std::vector> vec; - if (sx > MINIMUM_VALID_X_COORDINATE) - vec.push_back(std::pair(-1,0)); - if (sx < MAXIMUM_VALID_X_COORDINATE) - vec.push_back(std::pair(1,0)); - if (sy > MINIMUM_VALID_Y_COORDINATE) - vec.push_back(std::pair(0,-1)); - if (sy < MAXIMUM_VALID_Y_COORDINATE) - vec.push_back(std::pair(0,1)); - const UINT8 dir = Random(vec.size()); - const INT16 xmod = static_cast(std::get<0>(vec[dir])); - const INT16 ymod = static_cast(std::get<1>(vec[dir])); - - const GROUP* group = CreateNewMilitiaGroupDepartingFromSector(SECTOR(sx+xmod, sy+ymod), green, regular, elite); - - PlaceGroupInSector(group->ubGroupID, group->ubSectorX, group->ubSectorY, sx, sy, 0, FALSE); // no need to check for a battle we're jumping into - - gTacticalStatus.uiFlags |= WANT_MILITIA_REINFORCEMENTS; - } - - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szRebelCommandText[RCT_BONUS_MILITIA_JOINED]); - }; - - UINT8 foundLevel = 0; - UINT8 foundLoyalty = 0; - for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) - { - // make sure town has active admins - if (rebelCommandSaveInfo.regions[a].adminStatus != RAS_ACTIVE) - continue; - - // make sure action exists in this town - const INT16 index = GetAdminActionInRegion(a, RCAA_SAFEHOUSES); - if (index == -1) - continue; - - // and that it's not level 0 - const UINT8 level = rebelCommandSaveInfo.regions[a].actionLevels[index]; - if (level == 0) - continue; - - // get all sectors with this townid - std::vector> sectors; - for (int x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) - for (int y = MINIMUM_VALID_Y_COORDINATE; y <= MAXIMUM_VALID_Y_COORDINATE; ++y) - if (GetTownIdForSector(x, y) == a) - sectors.push_back(std::tuple(x, y, GetRegionLoyalty(a))); - - // check if sector is within range of the city - for (const auto tuple : sectors) - { - const INT16 x = std::get<0>(tuple); - const INT16 y = std::get<1>(tuple); - const UINT8 loyalty = std::get<2>(tuple); + CHAR16 text[100]; + floatModifier = max(floatModifier, gRebelCommandSettings.fDisruptAsdIncomeReductionModifier); + floatModifier *= 100.f; + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_ASD_INCOME_REDUCTION], floatModifier, L"%s", locSkillText[floatModifierSkill]); + agentBonusText.push_back(text); - // safehouse effect is only for sectors in or immediately adjacent to a town - if (abs(x - sx) + abs(y - sy) <= 1) + if (gGameOptions.fNewTraitSystem) { - if (level > foundLevel) + switch (extraBits) { - foundLevel = level; - foundLoyalty = loyalty; + case MissionHelpers::DISRUPT_ASD_STEAL_FUEL: + { + const UINT8 townId = GetTownIdForSector(BOBBYR_SHIPPING_DEST_SECTOR_X, BOBBYR_SHIPPING_DEST_SECTOR_Y); + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_STEAL_FUEL], pTownNames[townId], locSkillText[TECHNICIAN_NT]); + agentBonusText.push_back(text); } - else if (level == foundLevel) + break; + + case MissionHelpers::DISRUPT_ASD_DESTROY_RESERVES: { - foundLoyalty = max(loyalty, foundLoyalty); + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_DESTROY_RESERVES], locSkillText[DEMOLITIONS_NT]); + agentBonusText.push_back(text); + } + break; } } } - } - - // loyalty determines success rate - if (foundLevel > 0 && Random(100) <= min(foundLoyalty, gRebelCommandSettings.iSafehouseReinforceChance)) - createBonusMilitia(foundLevel); -} -INT16 GetFortificationsBonus(UINT8 sector) -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return 0; + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: + { + // no special modifiers. included for completeness. + } + break; - const UINT8 x = SECTORX(sector); - const UINT8 y = SECTORY(sector); - const UINT8 townId = GetTownIdForSector(x, y); + case RCAM_IMPROVE_LOCAL_SHOPS: + { + // no special modifiers. included for completeness. + } + break; - // not a town - if (townId < FIRST_TOWN || townId >= NUM_TOWNS) - return 0; + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: + { + floatModifier = max(floatModifier, gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier); + floatModifier -= 1.f; + floatModifier *= 100.f; + CHAR16 rangeText[100]; + swprintf(rangeText, szRebelCommandText[RCT_MISSION_BONUS_DECISION_TIME], floatModifier, L"%s", locSkillText[floatModifierSkill]); + agentBonusText.push_back(rangeText); + } + break; - // no admin team - if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) - return 0; + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: + { + floatModifier = max(floatModifier, gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier); + floatModifier *= 100.f; + CHAR16 text[100]; + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_UNALERTED_VISION_PENALTY], floatModifier, L"%s", locSkillText[floatModifierSkill]); + agentBonusText.push_back(text); + } + break; - const INT16 index = GetAdminActionInRegion(townId, RCAA_FORTIFICATIONS); + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: + { + intModifier = max(intModifier, gRebelCommandSettings.iSabotageInfantryEquipmentModifier); + CHAR16 text[100]; + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_INFANTRY_GEAR_QUALITY], intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(text); + } + break; - // action doesn't exist in region - if (index == -1) - return 0; + case RCAM_SABOTAGE_MECHANICAL_UNITS: + { + intModifier = max(intModifier, gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss); + CHAR16 text[100]; + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_MECHANICAL_STAT_LOSS], intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(text); + } + break; - // no levels in region - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; - if (level == 0) - return 0; + case RCAM_SEND_SUPPLIES_TO_TOWN: + { + // duration here is affected by lvl instead of a specific skill + CHAR16 durationText[100]; + swprintf(durationText, szRebelCommandText[RCT_MISSION_BONUS_DURATION], SendSuppliesToTownDurationBonus(&merc), pShortAttributeStrings[5]); // "Lvl" + agentBonusText.push_back(durationText); + } + break; - return static_cast(info.adminActions[RCAA_FORTIFICATIONS].fValue1 * level); -} + case RCAM_SOLDIER_BOUNTIES_KINGPIN: + { + floatModifier = max(floatModifier, 1.f); + CHAR16 text[100]; + if (floatModifier > 1.f) + { + floatModifier *= 100; + floatModifier -= 100; + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_PAYOUT], floatModifier, L"%s", locSkillText[floatModifierSkill]); + agentBonusText.push_back(text); + } -FLOAT GetHarriersSpeedPenalty(UINT8 sector) -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return 0.f; + if (intModifier > 0) + { + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_PAYOUT_LIMIT_INCREASE], intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(text); + } - const UINT8 x = SECTORX(sector); - const UINT8 y = SECTORY(sector); + if (extraBits == MissionHelpers::SOLDIER_BOUNTIES_KINGPIN_OFFICER_PAYOUTS) + { + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_OFFICER_PAYOUT], locSkillText[floatModifierSkill]); + agentBonusText.push_back(text); + } + else if (extraBits == MissionHelpers::SOLDIER_BOUNTIES_KINGPIN_VEHICLE_PAYOUTS) + { + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_VEHICLE_PAYOUT], locSkillText[floatModifierSkill]); + agentBonusText.push_back(text); + } + } + break; - // get towns that have harriers - std::vector> townSectors; - for (INT16 x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) - { - for (INT16 y = MINIMUM_VALID_Y_COORDINATE; y < MAXIMUM_VALID_Y_COORDINATE; ++y) + case RCAM_TRAIN_MILITIA_ANYWHERE: { - const UINT8 townId = GetTownIdForSector(x, y); - // not a town - if (townId < FIRST_TOWN || townId >= NUM_TOWNS) - continue; - - // no admin team - if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) - continue; - - const INT16 index = GetAdminActionInRegion(townId, RCAA_HARRIERS); + intModifier = max(intModifier, 1); + CHAR16 text[100]; + swprintf(text, szRebelCommandText[RCT_MISSION_BONUS_MAX_TRAINERS], intModifier, locSkillText[intModifierSkill]); + agentBonusText.push_back(text); + } + break; - // action doesn't exist in region - if (index == -1) - continue; + default: break; + } - // no levels in region - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; - if (level == 0) - continue; + if (durationBonus > 0) + { + CHAR16 durationText[100]; + swprintf(durationText, szRebelCommandText[RCT_MISSION_BONUS_DURATION], durationBonus, locSkillText[durationBonusSkill]); + agentBonusText.push_back(durationText); + } + } - townSectors.push_back(std::tuple(x, y, level)); + if (agentBonusText.size() == 0) + { + DrawTextToScreen(szRebelCommandText[RCT_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].c_str(), L"%%"); + DrawTextToScreen(sText, x+10, y+150+54+20+2+11*(i+1), 0, FONT10ARIAL, FONT_GREEN, FONT_MCOLOR_BLACK, FALSE, 0); } } - // run through townSectors to find the biggest harriers penalty - BOOLEAN found = FALSE; - for (const auto trio : townSectors) + // draw "start mission" button + BOOLEAN canStartMission = TRUE; + if (agentIndex[index] < static_cast(mercs.size())) { - const INT16 sx = std::get<0>(trio); - const INT16 sy = std::get<1>(trio); - const INT16 range = std::get<2>(trio); + const UINT8 townId = GetTownIdForSector(mercs[agentIndex[index]]->sSectorX, mercs[agentIndex[index]]->sSectorY); + const UINT8 townLoyalty = GetRegionLoyalty(townId); + const INT32 endTime = mercs[agentIndex[index]]->iEndofContractTime; + const INT32 worldMin = GetWorldTotalMin(); + const INT32 remaining = endTime - worldMin; - if (abs(sx - x) + abs(sy - y) <= range) + if (townId < FIRST_TOWN || townId >= NUM_TOWNS || !gfTownUsesLoyalty[townId]) { - // level 2 penalty - if (range == 2) - return range * info.adminActions[RCAA_HARRIERS].fValue1; - - // keep searching to see if we're in range of a better penalty - found = TRUE; + canStartMission = FALSE; + swprintf(sText, szRebelCommandText[RCT_MISSION_CANT_START_NOT_IN_TOWN]); + } + else if (townLoyalty < gRebelCommandSettings.iMinLoyaltyForMission && rebelCommandSaveInfo.availableMissions[index] != RCAM_SEND_SUPPLIES_TO_TOWN) + { + canStartMission = FALSE; + swprintf(sText, szRebelCommandText[RCT_MISSION_CANT_START_LOW_LOYALTY]); + } + else if (mercs[agentIndex[index]]->bAssignment == ASSIGNMENT_POW || mercs[agentIndex[index]]->bAssignment == ASSIGNMENT_MINIEVENT || mercs[agentIndex[index]]->bAssignment == ASSIGNMENT_REBELCOMMAND) + { + canStartMission = FALSE; + swprintf(sText, szRebelCommandText[RCT_MISSION_CANT_START_AGENT_UNAVAILABLE]); + } + else if (mercs[agentIndex[index]]->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC && remaining < 24 * 60) + { + canStartMission = FALSE; + swprintf(sText, szRebelCommandText[RCT_MISSION_CANT_START_CONTRACT_EXPIRING]); } } + else if (agentIndex[index] == mercs.size() && rebelCommandSaveInfo.availableMissions[index] == RCAM_SEND_SUPPLIES_TO_TOWN) + { + canStartMission = FALSE; + swprintf(sText, szRebelCommandText[RCT_MISSION_CANT_USE_REBEL_AGENT]); + } + else if ((gTacticalStatus.uiFlags & INCOMBAT) || gTacticalStatus.fEnemyInSector) + { + canStartMission = FALSE; + swprintf(sText, szRebelCommandText[RCT_MISSION_CANT_START_BATTLE_IN_PROGRESS]); + DrawTextToScreen(sText, x, y+295, 231, FONT10ARIAL, FONT_RED, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED); + } - // level 1 penalty - if (found == TRUE) - return info.adminActions[RCAA_HARRIERS].fValue1; + if (canStartMission) + { + swprintf(sText, szRebelCommandText[RCT_MISSION_START_BUTTON], 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]() { + PrepareMission(index); + }); + }); + MSYS_SetBtnUserData(btnId, 0, index); + btnIds.push_back(btnId); + } + else + { + DrawTextToScreen(sText, x, y+295, 231, FONT10ARIAL, FONT_RED, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED); + } - // nothing found, no penalty - return 0.f; + return TRUE; } -FLOAT GetLoyaltyGainModifier() +void RenderMissionOverview() { - if (gGameExternalOptions.fRebelCommandEnabled) - return gRebelCommandSettings.fLoyaltyGainModifier * (rebelCommandSaveInfo.iActiveDirective == RCD_CREATE_PROPAGANDA ? rebelCommandSaveInfo.directives[RCD_CREATE_PROPAGANDA].GetValue1() : 1.f); - - return 1.f; -} + CHAR16 sText[800]; + INT32 btnId; -UINT8 GetMaxTownLoyalty(INT8 townId) -{ - if (gGameExternalOptions.fRebelCommandEnabled) - return rebelCommandSaveInfo.regions[townId].ubMaxLoyalty; + // title + RenderHeader(RCT_AGENT_OVERVIEW); - return MAX_LOYALTY_VALUE; -} + // display help button + btnId = CreateTextButton(L"?", FONT12ARIAL, FONT_MCOLOR_WHITE, FONT_BLACK, BUTTON_USE_DEFAULT, WEBSITE_LEFT + 15, WEBSITE_TOP + 40, 30, 20, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, [](GUI_BUTTON* btn, INT32 reason) + { + ButtonHelper(btn, reason, []() + { + missionOverviewSubview = missionOverviewSubview == MOS_HELP ? MOS_MISSION_LIST : MOS_HELP; + }); + }); -INT16 GetMilitiaTrainingSpeedBonus() -{ - if (gGameExternalOptions.fRebelCommandEnabled && rebelCommandSaveInfo.iActiveDirective == RCD_TRAIN_MILITIA) - return static_cast(rebelCommandSaveInfo.directives[RCD_TRAIN_MILITIA].GetValue2()); + btnIds.push_back(btnId); - return 0; -} + // toggle between mission picker and active mission effects + switch (missionOverviewSubview) + { + case MOS_MISSION_LIST: + swprintf(sText, szRebelCommandText[RCT_MISSION_VIEW_ACTIVE]); + break; -FLOAT GetMilitiaTrainingCostModifier() -{ - if (gGameExternalOptions.fRebelCommandEnabled && rebelCommandSaveInfo.iActiveDirective == RCD_TRAIN_MILITIA) - return rebelCommandSaveInfo.directives[RCD_TRAIN_MILITIA].GetValue1(); + case MOS_ACTIVE_MISSION_EFFECTS: + case MOS_HELP: + swprintf(sText, szRebelCommandText[RCT_MISSION_VIEW_LIST]); + break; + } + btnId = CreateTextButton(sText, FONT10ARIAL, FONT_MCOLOR_LTYELLOW, FONT_BLACK, BUTTON_USE_DEFAULT, WEBSITE_LEFT + 50, WEBSITE_TOP + 40, 435, 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; - return 1.f; -} + case MOS_ACTIVE_MISSION_EFFECTS: + case MOS_HELP: + missionOverviewSubview = MOS_MISSION_LIST; + break; + } + }); + }); -FLOAT GetMilitiaUpkeepCostModifier() -{ - if (gGameExternalOptions.fRebelCommandEnabled && rebelCommandSaveInfo.iActiveDirective == RCD_SUPPORT_MILITIA) - return rebelCommandSaveInfo.directives[RCD_SUPPORT_MILITIA].GetValue1(); + btnIds.push_back(btnId); - return 1.f; -} + // main body + switch (missionOverviewSubview) + { + case MOS_MISSION_LIST: + if (rebelCommandSaveInfo.availableMissions[0] == RCAM_NONE) + { + UINT32 nextMissionAvailableDay = GetWorldDay(); + const INT8 interval = gRebelCommandSettings.iMissionRefreshTimeDays; + nextMissionAvailableDay += (interval - (nextMissionAvailableDay % interval)); + + if (missionMap.size() > 0) + { + DrawTextToScreen(szRebelCommandText[RCT_MISSION_PREP_IN_PROGRESS], WEBSITE_LEFT + 15, WEBSITE_TOP + 155, WEBSITE_WIDTH - 30, FONT14ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED); + } -void HandleScouting() -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return; - - // get towns that have scouting - std::vector> townSectors; - for (INT16 x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) - { - for (INT16 y = MINIMUM_VALID_Y_COORDINATE; y < MAXIMUM_VALID_Y_COORDINATE; ++y) + swprintf(sText, szRebelCommandText[RCT_MISSION_NEXT_AVAILABILITY], nextMissionAvailableDay); + DrawTextToScreen(sText, WEBSITE_LEFT + 15, WEBSITE_TOP + 175, WEBSITE_WIDTH - 30, FONT14ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED); + } + else { - const UINT8 townId = GetTownIdForSector(x, y); - // not a town - if (townId < FIRST_TOWN || townId >= NUM_TOWNS) - continue; - - // no admin team - if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) - continue; - - const INT16 index = GetAdminActionInRegion(townId, RCAA_SCOUTS); - - // action doesn't exist in region - if (index == -1) - continue; - - // no levels in region - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; - if (level == 0) - continue; - - // scouting range is level+1, since by default militia can see tiles adjacent to them - townSectors.push_back(std::tuple(x, y, level+1)); + for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) + { + SetupMissionAgentBox(WEBSITE_LEFT + 15 + 240 * i, WEBSITE_TOP + 65, i); + } } - } + break; - // run through all sectors, and see if they're within scouting range of a town - for (int x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) - { - for (int y = MINIMUM_VALID_Y_COORDINATE; y <= MAXIMUM_VALID_Y_COORDINATE; ++y) + case MOS_ACTIVE_MISSION_EFFECTS: { - for (const auto trio : townSectors) + DrawTextToScreen(szRebelCommandText[RCT_MISSION_ACTIVE_MISSIONS], WEBSITE_LEFT + 25, WEBSITE_TOP + 75, 0, FONT12ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + if (missionMap.size() == 0) { - const INT16 sx = std::get<0>(trio); - const INT16 sy = std::get<1>(trio); - const INT16 range = std::get<2>(trio); - - if (abs(x - sx) + abs(y - sy) <= range) + DrawTextToScreen(szRebelCommandText[RCT_NONE], WEBSITE_LEFT + 35, WEBSITE_TOP + 90, 0, FONT12ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); + } + else + { + std::vector evt1Strings; + std::vector evt2Strings; + std::vector> missions = GetAllStrategicEventsOfType(EVENT_REBELCOMMAND); + for (std::vector>::iterator it = missions.begin(); it != missions.end(); ++it) { - if (NumNonPlayerTeamMembersInSector(x, y, ENEMY_TEAM) > 0) + const UINT32 eventTime = it->first; + const UINT32 day = eventTime / 60 / 60 / 24; + const UINT32 hour = (eventTime % (24 * 60 * 60)) / 60 / 60; + const UINT32 minute = (eventTime % (24 * 60 * 60)) % (60 * 60); + MissionFirstEvent evt1; + MissionSecondEvent evt2; + DeserialiseMissionFirstEvent(it->second, evt1); + DeserialiseMissionSecondEvent(it->second, evt2); + + if (evt1.isFirstEvent) { - SectorInfo[SECTOR(x, y)].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_HERE; - SectorInfo[SECTOR(x, y)].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER; + CHAR16 prepText[200]; + swprintf(prepText, szRebelCommandText[RCT_MISSION_LIST_PREPARING], szRebelCommandAgentMissionsText[evt1.missionId * 2], day, hour, minute); + evt1Strings.push_back(prepText); } - break; + else if (evt2.isSecondEvent) + { + CHAR16 missionText[200]; + swprintf(missionText, szRebelCommandText[RCT_MISSION_LIST_ACTIVE], szRebelCommandAgentMissionsText[evt2.missionId * 2], day, hour, minute); + evt2Strings.push_back(missionText); + } + } + + UINT16 y = WEBSITE_TOP + 90; + for (std::vector::size_type i = 0; i < evt1Strings.size(); ++i) + { + DrawTextToScreen(const_cast(evt1Strings[i].c_str()), WEBSITE_LEFT + 35, y, 0, FONT12ARIAL, FONT_DKYELLOW, FONT_MCOLOR_BLACK, FALSE, 0); + y += 15; + } + + for (std::vector::size_type i = 0; i < evt2Strings.size(); ++i) + { + DrawTextToScreen(const_cast(evt2Strings[i].c_str()), WEBSITE_LEFT + 35, y, 0, FONT12ARIAL, FONT_DKGREEN, FONT_MCOLOR_BLACK, FALSE, 0); + y += 15; } } } + break; + + case MOS_HELP: + { + UINT16 y = WEBSITE_TOP + 100; + swprintf(sText, szRebelCommandText[RCT_MISSION_HELP_1], gRebelCommandSettings.iMissionPrepTime); + y += DisplayWrappedString(WEBSITE_LEFT + 35, y, 400, 2, FONT12ARIAL, FONT_MCOLOR_BLACK, sText, FONT_MCOLOR_BLACK, FALSE, 0); + y += 20; + y += DisplayWrappedString(WEBSITE_LEFT + 35, y, 400, 2, FONT12ARIAL, FONT_MCOLOR_BLACK, szRebelCommandText[RCT_MISSION_HELP_2], FONT_MCOLOR_BLACK, FALSE, 0); + y += 20; + DisplayWrappedString(WEBSITE_LEFT + 35, y, 400, 2, FONT12ARIAL, FONT_MCOLOR_BLACK, szRebelCommandText[RCT_MISSION_HELP_3], FONT_MCOLOR_BLACK, FALSE, 0); } + break; + } + + // "new missions every X hours" text + swprintf(sText, szRebelCommandText[RCT_NEW_MISSIONS_AVAILABLE_TIME], gRebelCommandSettings.iMissionRefreshTimeDays * 24); + DrawTextToScreen(sText, WEBSITE_LEFT + 22, WEBSITE_TOP + WEBSITE_HEIGHT - 14, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); } -FLOAT GetPathfindersSpeedBonus(UINT8 sector) +void PrepareMission(INT8 index) { - if (!gGameExternalOptions.fRebelCommandEnabled) - return 0.f; - - const UINT8 x = SECTORX(sector); - const UINT8 y = SECTORY(sector); - - // get towns that have pathfinders - std::vector> townSectors; - for (INT16 x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) + const INT32 cost = GetMissionCost(); + if (rebelCommandSaveInfo.iSupplies < cost) { - for (INT16 y = MINIMUM_VALID_Y_COORDINATE; y < MAXIMUM_VALID_Y_COORDINATE; ++y) - { - const UINT8 townId = GetTownIdForSector(x, y); - // not a town - if (townId < FIRST_TOWN || townId >= NUM_TOWNS) - continue; - - // no admin team - if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) - continue; - - const INT16 index = GetAdminActionInRegion(townId, RCAA_PATHFINDERS); - - // action doesn't exist in region - if (index == -1) - continue; - - // no levels in region - const UINT8 level = rebelCommandSaveInfo.regions[townId].actionLevels[index]; - if (level == 0) - continue; - - townSectors.push_back(std::tuple(x, y, level)); - } + DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, szRebelCommandText[RCT_INSUFFICIENT_FUNDS], LAPTOP_SCREEN, MSG_BOX_FLAG_OK, NULL); + return; } - // run through townSectors to find the biggest pathfinders bonus - BOOLEAN found = FALSE; - for (const auto trio : townSectors) + // confirmation popup + std::vector mercs; + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) { - const INT16 sx = std::get<0>(trio); - const INT16 sy = std::get<1>(trio); - const INT16 range = std::get<2>(trio); + SOLDIERTYPE* pSoldier = MercPtrs[i]; - if (abs(sx - x) + abs(sy - y) <= range) + if (pSoldier && pSoldier->bActive + && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) + ) { - // level 2 bonus - if (range == 2) - return range * info.adminActions[RCAA_PATHFINDERS].fValue1; - - // keep searching to see if we're in range of a better bonus - found = TRUE; + mercs.push_back(pSoldier); } } - // level 1 bonus - if (found == TRUE) - return info.adminActions[RCAA_PATHFINDERS].fValue1; - - // nothing found, no bonus - return 0.f; -} - -BOOLEAN NeutraliseRole(const SOLDIERTYPE* pSoldier) -{ - if (!gGameExternalOptions.fRebelCommandEnabled || !gGameExternalOptions.fEnemyRoles) - return FALSE; - - if (rebelCommandSaveInfo.iActiveDirective != RCD_HVT_STRIKES) - return FALSE; + const MERCPROFILESTRUCT* merc = agentIndex[index] == mercs.size() ? nullptr : &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; - if (!SOLDIER_CLASS_ENEMY(pSoldier->ubSoldierClass)) - return FALSE; + case RCAM_DISRUPT_ASD: + { + missionTitle = RCAMT_DISRUPT_ASD_TITLE; + missionSuccessChance = gRebelCommandSettings.iDisruptAsdSuccessChance; + missionDuration = gRebelCommandSettings.iDisruptAsdDuration; + } + break; - const UINT32 chance = static_cast(rebelCommandSaveInfo.directives[RCD_HVT_STRIKES].GetValue1()); + case RCAM_GET_ENEMY_MOVEMENT_TARGETS: + { + missionTitle = RCAMT_GET_ENEMY_MOVEMENT_TARGETS_TITLE; + missionSuccessChance = gRebelCommandSettings.iGetEnemyMovementTargetsSuccessChance; + missionDuration = gRebelCommandSettings.iGetEnemyMovementTargetsDuration; + } + break; - return Random(100) < chance; -} + case RCAM_IMPROVE_LOCAL_SHOPS: + { + missionTitle = RCAMT_IMPROVE_LOCAL_SHOPS_TITLE; + missionSuccessChance = gRebelCommandSettings.iImproveLocalShopsSuccessChance; + missionDuration = gRebelCommandSettings.iImproveLocalShopsDuration; + } + break; -void RaidMines(INT32 &playerIncome, INT32 &enemyIncome) -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return; + case RCAM_REDUCE_STRATEGIC_DECISION_SPEED: + { + missionTitle = RCAMT_REDUCE_STRATEGIC_DECISION_SPEED_TITLE; + missionSuccessChance = gRebelCommandSettings.iReduceStrategicDecisionSpeedSuccessChance; + missionDuration = gRebelCommandSettings.iReduceStrategicDecisionSpeedDuration; + } + break; - if (rebelCommandSaveInfo.iActiveDirective != RCD_RAID_MINES) - return; + case RCAM_REDUCE_UNALERTED_ENEMY_VISION: + { + missionTitle = RCAMT_REDUCE_UNALERTED_ENEMY_VISION_TITLE; + missionSuccessChance = gRebelCommandSettings.iReduceUnalertedEnemyVisionSuccessChance; + missionDuration = gRebelCommandSettings.iReduceUnalertedEnemyVisionDuration; + } + break; - if (Random(100) < static_cast(gRebelCommandSettings.iRaidMinesFailChance)) - return; + case RCAM_SABOTAGE_INFANTRY_EQUIPMENT: + { + missionTitle = RCAMT_SABOTAGE_INFANTRY_EQUIPMENT_TITLE; + missionSuccessChance = gRebelCommandSettings.iSabotageInfantryEquipmentSuccessChance; + missionDuration = gRebelCommandSettings.iSabotageInfantryEquipmentDuration; + } + break; - INT32 stolenIncome = static_cast(enemyIncome * rebelCommandSaveInfo.directives[RCD_RAID_MINES].GetValue1() * Random(100) / 100.f); - playerIncome += stolenIncome; - enemyIncome -= stolenIncome; + case RCAM_SABOTAGE_MECHANICAL_UNITS: + { + missionTitle = RCAMT_SABOTAGE_MECHANICAL_UNITS_TITLE; + missionSuccessChance = gRebelCommandSettings.iSabotageMechanicalUnitsSuccessChance; + missionDuration = gRebelCommandSettings.iSabotageMechanicalUnitsDuration; + } + break; - if (stolenIncome > 0) + case RCAM_SEND_SUPPLIES_TO_TOWN: { - CHAR16 text[200]; - swprintf(text, szRebelCommandText[RCT_MINE_RAID_SUCCESSFUL], stolenIncome); - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text); + missionTitle = RCAMT_SEND_SUPPLIES_TO_TOWN_TITLE; + missionSuccessChance = gRebelCommandSettings.iSendSuppliesToTownSuccessChance; + missionDuration = gRebelCommandSettings.iSendSuppliesToTownDuration + SendSuppliesToTownDurationBonus(merc); + extraBits = SECTOR(merc->sSectorX, merc->sSectorY); } -} + break; -BOOLEAN ShowApproximateEnemyLocations() -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return FALSE; + case RCAM_SOLDIER_BOUNTIES_KINGPIN: + { + missionTitle = RCAMT_SOLDIER_BOUNTIES_KINGPIN_TITLE; + missionSuccessChance = gRebelCommandSettings.iSoldierBountiesKingpinSuccessChance; + missionDuration = gRebelCommandSettings.iSoldierBountiesKingpinDuration; + } + break; + + case RCAM_TRAIN_MILITIA_ANYWHERE: + { + missionTitle = RCAMT_TRAIN_MILITIA_ANYWHERE_TITLE; + missionSuccessChance = gRebelCommandSettings.iTrainMilitiaAnywhereSuccessChance; + missionDuration = gRebelCommandSettings.iTrainMilitiaAnywhereDuration; + } + break; - return rebelCommandSaveInfo.iActiveDirective == RCD_SPOTTERS; -} + default: break; + } -void ShowWebsiteAvailableMessage() -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return; + missionSuccessChance += GetMissionSuccessChanceBonus(merc); - StopTimeCompression(); - DoMessageBox(MSG_BOX_MINIEVENT_STYLE, szRebelCommandText[RCT_WEBSITE_AVAILABLE], guiCurrentScreen, MSG_BOX_FLAG_OK | MSG_BOX_FLAG_BIGGER, NULL, NULL); -} + if (Random(100) > static_cast(missionSuccessChance)) + { + // mission failed! + missionDuration = 0; + } + else + { + missionDuration += durationBonus; + } -void DailyUpdate() -{ - if (!gGameExternalOptions.fRebelCommandEnabled) - return; + swprintf(text, szRebelCommandText[RCT_MISSION_POPUP_PART1], szRebelCommandAgentMissionsText[missionTitle], cost); - // make sure we have a valid directive - if (rebelCommandSaveInfo.iActiveDirective < RCD_GATHER_SUPPLIES || rebelCommandSaveInfo.iActiveDirective >= RCD_NUM_DIRECTIVES) + if (agentIndex[index] == mercs.size()) { - rebelCommandSaveInfo.iActiveDirective = RCD_GATHER_SUPPLIES; - rebelCommandSaveInfo.iSelectedDirective = RCD_GATHER_SUPPLIES; + // sent a generic rebel + MissionHelpers::missionParam = SerialiseMissionFirstEvent(TRUE, 0 /* no profile needed */, static_cast(rebelCommandSaveInfo.availableMissions[index]), missionDuration, 0 /* no extra data */); + swprintf(text, szRebelCommandText[RCT_MISSION_POPUP_PART2_GENERIC], text); } - - // apply old directive bonus before setting new one - switch (rebelCommandSaveInfo.iActiveDirective) + else { - case RCD_GATHER_SUPPLIES: - case RCD_SUPPORT_MILITIA: - case RCD_TRAIN_MILITIA: - case RCD_CREATE_PROPAGANDA: - case RCD_HVT_STRIKES: - case RCD_SPOTTERS: - case RCD_RAID_MINES: - // effects applied elsewhere - break; + MissionHelpers::missionParam = SerialiseMissionFirstEvent(FALSE, mercs[agentIndex[index]]->ubProfile, static_cast(rebelCommandSaveInfo.availableMissions[index]), missionDuration, static_cast(extraBits)); + if (merc->bSex == MALE) + swprintf(text, szRebelCommandText[RCT_MISSION_POPUP_PART2_MALE], text, merc->zNickname, gRebelCommandSettings.iMissionPrepTime); + else + swprintf(text, szRebelCommandText[RCT_MISSION_POPUP_PART2_FEMALE], text, merc->zNickname, gRebelCommandSettings.iMissionPrepTime); + } - case RCD_ELITE_MILITIA: + DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, text, LAPTOP_SCREEN, MSG_BOX_FLAG_YESNO, [](UINT8 exitValue) { + if (exitValue == MSG_BOX_RETURN_YES) { - const UINT8 elites = static_cast(rebelCommandSaveInfo.directives[RCD_ELITE_MILITIA].GetValue1()); - // get the top-right most omerta tile - INT16 sx = 1, sy = 1; - for (sx = MAXIMUM_VALID_X_COORDINATE; sx >= MINIMUM_VALID_X_COORDINATE; --sx) + MissionFirstEvent evt; + DeserialiseMissionFirstEvent(MissionHelpers::missionParam, evt); + + if (!evt.sentGenericRebelAgent) { - for (sy = MINIMUM_VALID_Y_COORDINATE; sy <= MAXIMUM_VALID_Y_COORDINATE; ++sy) + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) { - if (GetTownIdForSector(sx, sy) == OMERTA) + SOLDIERTYPE* pSoldier = MercPtrs[i]; + if (pSoldier->ubProfile == evt.mercProfileId) { - goto foundOmerta; + TakeSoldierOutOfVehicle(pSoldier); + RemoveCharacterFromSquads(pSoldier); + pSoldier->bSectorZ += REBEL_COMMAND_Z_OFFSET; + pSoldier->bBleeding = 0; + SetTimeOfAssignmentChangeForMerc(pSoldier); + ChangeSoldiersAssignment(pSoldier, ASSIGNMENT_REBELCOMMAND); + break; } } } - foundOmerta: - - StrategicAddMilitiaToSector(sx, sy, ELITE_MILITIA, elites); - for (int i = 0 ; i < elites ; ++i) + // disable missions until next refresh + for (INT8 i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) { - CreateNewIndividualMilitia( ELITE_MILITIA, MO_ARULCO, SECTOR(sx, sy) ); + rebelCommandSaveInfo.availableMissions[i] = RCAM_NONE; } - } - break; - - case RCD_CREATE_TURNCOATS: - { - if (LaptopSaveInfo.dIntelPool >= gRebelCommandSettings.fCreateTurncoatsIntelCost) - { - UINT32 groupNum = 1; - GROUP* groupPtr = gpGroupList; - GROUP* selectedGroupPtr = nullptr; - // pick a random group - while (groupPtr != nullptr) - { - if (groupPtr->usGroupTeam != ENEMY_TEAM) - { - groupPtr = groupPtr->next; - continue; - } + // queue up the mission start event. make sure we use the top of the hour because I'm lazy and we're handling the assignment here instead of Assignments.cpp + const UINT32 time = GetWorldTotalMin(); + AddStrategicEvent(EVENT_REBELCOMMAND, time + (60 - time % 60) + 60 * gRebelCommandSettings.iMissionPrepTime, MissionHelpers::missionParam); + missionMap.insert(std::make_pair(static_cast(evt.missionId), MissionHelpers::missionParam)); - if (Random(groupNum) == 0) - { - selectedGroupPtr = groupPtr; - } + rebelCommandSaveInfo.iSupplies -= GetMissionCost(); + } - groupNum++; - groupPtr = groupPtr->next; - } + // update the mission list to show that we've started + redraw = TRUE; + }); +} +// end website - if (selectedGroupPtr == nullptr) - break; // couldn't find a group... +void ApplyEnemyPenalties(SOLDIERTYPE* pSoldier) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + const auto applyPenalties = [](SOLDIERTYPE* pSoldier, UINT8 level) + { + pSoldier->stats.bLife -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bLifeMax -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bAgility -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bDexterity -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bStrength -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bWisdom -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bLeadership -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bMarksmanship -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bMechanical -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bExplosive -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->stats.bMedical -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->bBreath -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + pSoldier->bBreathMax -= static_cast(info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level); + + pSoldier->stats.bLife = static_cast(max(25, pSoldier->stats.bLife)); + pSoldier->stats.bLifeMax = static_cast(max(25, pSoldier->stats.bLifeMax)); + pSoldier->stats.bAgility = static_cast(max(25, pSoldier->stats.bAgility)); + pSoldier->stats.bDexterity = static_cast(max(25, pSoldier->stats.bDexterity)); + pSoldier->stats.bStrength = static_cast(max(25, pSoldier->stats.bStrength)); + pSoldier->stats.bWisdom = static_cast(max(25, pSoldier->stats.bStrength)); + pSoldier->stats.bLeadership = static_cast(max(25, pSoldier->stats.bLeadership)); + pSoldier->stats.bMarksmanship = static_cast(max(25, pSoldier->stats.bMarksmanship)); + pSoldier->stats.bMechanical = static_cast(max(25, pSoldier->stats.bMechanical)); + pSoldier->stats.bExplosive = static_cast(max(25, pSoldier->stats.bExplosive)); + pSoldier->stats.bMedical = static_cast(max(25, pSoldier->stats.bMedical)); + pSoldier->bBreath = static_cast(max(25, info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level)); + pSoldier->bBreathMax = static_cast(max(25, info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1 * level)); + }; + + // need to get dist between soldier and town. + // run through all towns, check manhattan distance + UINT8 foundLevel = 0; + UINT8 foundLoyalty = 0; + for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) + { + // make sure town has active admins + if (rebelCommandSaveInfo.regions[a].adminStatus != RAS_ACTIVE) + continue; + + // make sure action exists in this town + const INT16 index = GetAdminActionInRegion(a, RCAA_SUPPLY_DISRUPTION); + if (index == -1) + continue; + + // and that it's not level 0 + const UINT8 level = rebelCommandSaveInfo.regions[a].GetLevel(index); + if (level == 0) + continue; + + // get all sectors with this townid + std::vector> sectors; + for (int x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) + for (int y = MINIMUM_VALID_Y_COORDINATE; y <= MAXIMUM_VALID_Y_COORDINATE; ++y) + if (GetTownIdForSector(x, y) == a) + sectors.push_back(std::tuple(x, y, GetRegionLoyalty(a))); + + // check if soldier is within range of the city + for (const auto& tuple : sectors) + { + const INT16 x = std::get<0>(tuple); + const INT16 y = std::get<1>(tuple); + const UINT8 loyalty = std::get<2>(tuple); + + if (abs(x - pSoldier->sSectorX) + abs(y - pSoldier->sSectorY) <= level) + { + if (level > foundLevel) + { + foundLevel = level; + foundLoyalty = loyalty; + } + else if (level == foundLevel) + { + foundLoyalty = max(loyalty, foundLoyalty); + } + } + } + } + + // loyalty determines success rate + if (foundLevel > 0 && Random(100) <= foundLoyalty) + applyPenalties(pSoldier, foundLevel); +} + +void ApplyMilitiaBonuses(SOLDIERTYPE* pMilitia) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + pMilitia->stats.bLife += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); + pMilitia->stats.bLifeMax += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); + pMilitia->stats.bAgility += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); + pMilitia->stats.bDexterity += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); + pMilitia->stats.bStrength += (gRebelCommandSettings.iMilitiaStatBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); + pMilitia->stats.bMarksmanship += (gRebelCommandSettings.iMilitiaMarksmanshipBonusPerLevel * rebelCommandSaveInfo.iMilitiaStatsLevel); + + pMilitia->stats.bLife = min(100, pMilitia->stats.bLife); + pMilitia->stats.bLifeMax = min(100, pMilitia->stats.bLifeMax); + pMilitia->stats.bAgility = min(100, pMilitia->stats.bAgility); + pMilitia->stats.bDexterity = min(100, pMilitia->stats.bDexterity); + pMilitia->stats.bStrength = min(100, pMilitia->stats.bStrength); + pMilitia->stats.bMarksmanship = min(100, pMilitia->stats.bMarksmanship); +} + +UINT8 GetApproximateEnemyLocationResolutionIndex() +{ + return static_cast(rebelCommandSaveInfo.directives[RCD_SPOTTERS].GetValue1()); +} + +FLOAT GetAssignmentBonus(INT16 x, INT16 y) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0.f; + + const UINT8 townId = GetTownIdForSector(x, y); + + // make sure town has active admins + if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) + return 0.f; + + const INT16 index = GetAdminActionInRegion(townId, RCAA_MERC_SUPPORT); + FLOAT value = 0.f; + + if (index >= 0) + { + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); + value += info.adminActions[RCAA_MERC_SUPPORT].fValue1 * level; + } + + return value/100.f; +} + +INT32 GetMiningPolicyBonus(INT16 townId) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0; + + // make sure town has active admins + if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) + return 0; + + const INT16 index = GetAdminActionInRegion(townId, RCAA_MINING_POLICY); + + if (index >= 0) + { + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); + return static_cast(info.adminActions[RCAA_MINING_POLICY].fValue1 * level); + } + + return 0; +} + +void GetBonusMilitia(INT16 sx, INT16 sy, UINT8& green, UINT8& regular, UINT8& elite, BOOLEAN createGroup) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + const auto createBonusMilitia = [sx, sy, &green, ®ular, &elite, createGroup](UINT8 level) + { + if (level == 1) + { + green = static_cast(info.adminActions[RCAA_SAFEHOUSES].fValue1 + Random(static_cast(1 + info.adminActions[RCAA_SAFEHOUSES].fValue2))); + regular = Random(2); + elite = 0; + } + else if (level == 2) + { + green = 0; + regular = static_cast(info.adminActions[RCAA_SAFEHOUSES].fValue1 + Random(static_cast(1 + info.adminActions[RCAA_SAFEHOUSES].fValue2))); + elite = Random(2); + } + + for (int a = 0; a < max(green, max(regular, elite)); ++a) + { + if (a < green) + CreateNewIndividualMilitia(GREEN_MILITIA, MO_ARULCO, SECTOR(sx, sy)); + + if (a < regular) + CreateNewIndividualMilitia(REGULAR_MILITIA, MO_ARULCO, SECTOR(sx, sy)); + + if (a < elite) + CreateNewIndividualMilitia(ELITE_MILITIA, MO_ARULCO, SECTOR(sx, sy)); + } + + if (createGroup) + { + // randomise entry direction + std::vector> vec; + if (sx > MINIMUM_VALID_X_COORDINATE) + vec.push_back(std::pair(-1,0)); + if (sx < MAXIMUM_VALID_X_COORDINATE) + vec.push_back(std::pair(1,0)); + if (sy > MINIMUM_VALID_Y_COORDINATE) + vec.push_back(std::pair(0,-1)); + if (sy < MAXIMUM_VALID_Y_COORDINATE) + vec.push_back(std::pair(0,1)); + const UINT8 dir = Random(vec.size()); + const INT16 xmod = static_cast(std::get<0>(vec[dir])); + const INT16 ymod = static_cast(std::get<1>(vec[dir])); + + const GROUP* group = CreateNewMilitiaGroupDepartingFromSector(SECTOR(sx+xmod, sy+ymod), green, regular, elite); + + PlaceGroupInSector(group->ubGroupID, group->ubSectorX, group->ubSectorY, sx, sy, 0, FALSE); // no need to check for a battle we're jumping into + + gTacticalStatus.uiFlags |= WANT_MILITIA_REINFORCEMENTS; + } + + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szRebelCommandText[RCT_BONUS_MILITIA_JOINED]); + }; + + UINT8 foundLevel = 0; + UINT8 foundLoyalty = 0; + for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) + { + // make sure town has active admins + if (rebelCommandSaveInfo.regions[a].adminStatus != RAS_ACTIVE) + continue; + + // make sure action exists in this town + const INT16 index = GetAdminActionInRegion(a, RCAA_SAFEHOUSES); + if (index == -1) + continue; + + // and that it's not level 0 + const UINT8 level = rebelCommandSaveInfo.regions[a].GetLevel(index); + if (level == 0) + continue; + + // get all sectors with this townid + std::vector> sectors; + for (int x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) + for (int y = MINIMUM_VALID_Y_COORDINATE; y <= MAXIMUM_VALID_Y_COORDINATE; ++y) + if (GetTownIdForSector(x, y) == a) + sectors.push_back(std::tuple(x, y, GetRegionLoyalty(a))); + + // check if sector is within range of the city + for (const auto& tuple : sectors) + { + const INT16 x = std::get<0>(tuple); + const INT16 y = std::get<1>(tuple); + const UINT8 loyalty = std::get<2>(tuple); + + // safehouse effect is only for sectors in or immediately adjacent to a town + if (abs(x - sx) + abs(y - sy) <= 1) + { + if (level > foundLevel) + { + foundLevel = level; + foundLoyalty = loyalty; + } + else if (level == foundLevel) + { + foundLoyalty = max(loyalty, foundLoyalty); + } + } + } + } + + // loyalty determines success rate + if (foundLevel > 0 && Random(100) <= min(foundLoyalty, gRebelCommandSettings.iSafehouseReinforceChance)) + createBonusMilitia(foundLevel); +} + +INT16 GetFortificationsBonus(UINT8 sector) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0; + + const UINT8 x = SECTORX(sector); + const UINT8 y = SECTORY(sector); + const UINT8 townId = GetTownIdForSector(x, y); + + // not a town + if (townId < FIRST_TOWN || townId >= NUM_TOWNS) + return 0; + + // no admin team + if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) + return 0; + + const INT16 index = GetAdminActionInRegion(townId, RCAA_FORTIFICATIONS); + + // action doesn't exist in region + if (index == -1) + return 0; + + // no levels in region + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); + if (level == 0) + return 0; + + return static_cast(info.adminActions[RCAA_FORTIFICATIONS].fValue1 * level); +} + +FLOAT GetHarriersSpeedPenalty(UINT8 sector) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0.f; + + const UINT8 x = SECTORX(sector); + const UINT8 y = SECTORY(sector); + + // get towns that have harriers + std::vector> townSectors; + for (INT16 x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) + { + for (INT16 y = MINIMUM_VALID_Y_COORDINATE; y < MAXIMUM_VALID_Y_COORDINATE; ++y) + { + const UINT8 townId = GetTownIdForSector(x, y); + // not a town + if (townId < FIRST_TOWN || townId >= NUM_TOWNS) + continue; + + // no admin team + if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) + continue; + + const INT16 index = GetAdminActionInRegion(townId, RCAA_HARRIERS); + + // action doesn't exist in region + if (index == -1) + continue; + + // no levels in region + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); + if (level == 0) + continue; + + townSectors.push_back(std::tuple(x, y, level)); + } + } + + // run through townSectors to find the biggest harriers penalty + BOOLEAN found = FALSE; + for (const auto& trio : townSectors) + { + const INT16 sx = std::get<0>(trio); + const INT16 sy = std::get<1>(trio); + const INT16 range = std::get<2>(trio); + + if (abs(sx - x) + abs(sy - y) <= range) + { + // level 2 penalty + if (range == 2) + return range * info.adminActions[RCAA_HARRIERS].fValue1; + + // keep searching to see if we're in range of a better penalty + found = TRUE; + } + } + + // level 1 penalty + if (found == TRUE) + return info.adminActions[RCAA_HARRIERS].fValue1; + + // nothing found, no penalty + return 0.f; +} + +FLOAT GetLoyaltyGainModifier() +{ + if (gGameExternalOptions.fRebelCommandEnabled) + return gRebelCommandSettings.fLoyaltyGainModifier * (rebelCommandSaveInfo.iActiveDirective == RCD_CREATE_PROPAGANDA ? rebelCommandSaveInfo.directives[RCD_CREATE_PROPAGANDA].GetValue1() : 1.f); + + return 1.f; +} + +UINT8 GetMaxTownLoyalty(INT8 townId) +{ + if (gGameExternalOptions.fRebelCommandEnabled) + return rebelCommandSaveInfo.regions[townId].ubMaxLoyalty; + + return MAX_LOYALTY_VALUE; +} + +INT16 GetMilitiaTrainingSpeedBonus() +{ + if (gGameExternalOptions.fRebelCommandEnabled && rebelCommandSaveInfo.iActiveDirective == RCD_TRAIN_MILITIA) + return static_cast(rebelCommandSaveInfo.directives[RCD_TRAIN_MILITIA].GetValue2()); + + return 0; +} + +FLOAT GetMilitiaTrainingCostModifier() +{ + if (gGameExternalOptions.fRebelCommandEnabled && rebelCommandSaveInfo.iActiveDirective == RCD_TRAIN_MILITIA) + return rebelCommandSaveInfo.directives[RCD_TRAIN_MILITIA].GetValue1(); + + return 1.f; +} + +FLOAT GetMilitiaUpkeepCostModifier() +{ + if (gGameExternalOptions.fRebelCommandEnabled && rebelCommandSaveInfo.iActiveDirective == RCD_SUPPORT_MILITIA) + return rebelCommandSaveInfo.directives[RCD_SUPPORT_MILITIA].GetValue1(); + + return 1.f; +} + +void HandleScouting() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + // get towns that have scouting + std::vector> townSectors; + for (INT16 x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) + { + for (INT16 y = MINIMUM_VALID_Y_COORDINATE; y < MAXIMUM_VALID_Y_COORDINATE; ++y) + { + const UINT8 townId = GetTownIdForSector(x, y); + // not a town + if (townId < FIRST_TOWN || townId >= NUM_TOWNS) + continue; + + // no admin team + if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) + continue; + + const INT16 index = GetAdminActionInRegion(townId, RCAA_SCOUTS); + + // action doesn't exist in region + if (index == -1) + continue; + + // no levels in region + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); + if (level == 0) + continue; + + // scouting range is level+1, since by default militia can see tiles adjacent to them + townSectors.push_back(std::tuple(x, y, level+1)); + } + } + + // run through all sectors, and see if they're within scouting range of a town + for (int x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) + { + for (int y = MINIMUM_VALID_Y_COORDINATE; y <= MAXIMUM_VALID_Y_COORDINATE; ++y) + { + for (const auto& trio : townSectors) + { + const INT16 sx = std::get<0>(trio); + const INT16 sy = std::get<1>(trio); + const INT16 range = std::get<2>(trio); + + if (abs(x - sx) + abs(y - sy) <= range) + { + if (NumNonPlayerTeamMembersInSector(x, y, ENEMY_TEAM) > 0) + { + SectorInfo[SECTOR(x, y)].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_HERE; + SectorInfo[SECTOR(x, y)].uiFlags |= SF_ASSIGN_NOTICED_ENEMIES_KNOW_NUMBER; + } + break; + } + } + } + } +} + +FLOAT GetPathfindersSpeedBonus(UINT8 sector) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0.f; + + const UINT8 x = SECTORX(sector); + const UINT8 y = SECTORY(sector); + + // get towns that have pathfinders + std::vector> townSectors; + for (INT16 x = MINIMUM_VALID_X_COORDINATE; x <= MAXIMUM_VALID_X_COORDINATE; ++x) + { + for (INT16 y = MINIMUM_VALID_Y_COORDINATE; y < MAXIMUM_VALID_Y_COORDINATE; ++y) + { + const UINT8 townId = GetTownIdForSector(x, y); + // not a town + if (townId < FIRST_TOWN || townId >= NUM_TOWNS) + continue; + + // no admin team + if (rebelCommandSaveInfo.regions[townId].adminStatus != RAS_ACTIVE) + continue; + + const INT16 index = GetAdminActionInRegion(townId, RCAA_PATHFINDERS); + + // action doesn't exist in region + if (index == -1) + continue; + + // no levels in region + const UINT8 level = rebelCommandSaveInfo.regions[townId].GetLevel(index); + if (level == 0) + continue; + + townSectors.push_back(std::tuple(x, y, level)); + } + } + + // run through townSectors to find the biggest pathfinders bonus + BOOLEAN found = FALSE; + for (const auto& trio : townSectors) + { + const INT16 sx = std::get<0>(trio); + const INT16 sy = std::get<1>(trio); + const INT16 range = std::get<2>(trio); + + if (abs(sx - x) + abs(sy - y) <= range) + { + // level 2 bonus + if (range == 2) + return range * info.adminActions[RCAA_PATHFINDERS].fValue1; + + // keep searching to see if we're in range of a better bonus + found = TRUE; + } + } + + // level 1 bonus + if (found == TRUE) + return info.adminActions[RCAA_PATHFINDERS].fValue1; + + // nothing found, no bonus + return 0.f; +} + +BOOLEAN NeutraliseRole(const SOLDIERTYPE* pSoldier) +{ + if (!gGameExternalOptions.fRebelCommandEnabled || !gGameExternalOptions.fEnemyRoles) + return FALSE; + + if (rebelCommandSaveInfo.iActiveDirective != RCD_HVT_STRIKES) + return FALSE; + + if (!SOLDIER_CLASS_ENEMY(pSoldier->ubSoldierClass)) + return FALSE; + + const UINT32 chance = static_cast(rebelCommandSaveInfo.directives[RCD_HVT_STRIKES].GetValue1()); + + return Random(100) < chance; +} + +void RaidMines(INT32 &playerIncome, INT32 &enemyIncome) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + if (rebelCommandSaveInfo.iActiveDirective != RCD_RAID_MINES) + return; + + if (Random(100) < static_cast(gRebelCommandSettings.iRaidMinesFailChance)) + return; + + INT32 stolenIncome = static_cast(enemyIncome * rebelCommandSaveInfo.directives[RCD_RAID_MINES].GetValue1() * Random(100) / 100.f); + playerIncome += stolenIncome; + enemyIncome -= stolenIncome*2; + + if (enemyIncome < 0) enemyIncome = 0; + + if (stolenIncome > 0) + { + CHAR16 text[200]; + swprintf(text, szRebelCommandText[RCT_MINE_RAID_SUCCESSFUL], stolenIncome); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text); + } +} + +BOOLEAN ShowApproximateEnemyLocations() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return FALSE; + + return rebelCommandSaveInfo.iActiveDirective == RCD_SPOTTERS; +} + +void ShowWebsiteAvailableMessage() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + StopTimeCompression(); + DoMessageBox(MSG_BOX_MINIEVENT_STYLE, szRebelCommandText[RCT_WEBSITE_AVAILABLE], guiCurrentScreen, MSG_BOX_FLAG_OK | MSG_BOX_FLAG_BIGGER, NULL, NULL); +} + +void DailyUpdate() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + // make sure we have a valid directive + if (rebelCommandSaveInfo.iActiveDirective < RCD_GATHER_SUPPLIES || rebelCommandSaveInfo.iActiveDirective >= RCD_NUM_DIRECTIVES) + { + rebelCommandSaveInfo.iActiveDirective = RCD_GATHER_SUPPLIES; + rebelCommandSaveInfo.iSelectedDirective = RCD_GATHER_SUPPLIES; + } + + // apply old directive bonus before setting new one + switch (rebelCommandSaveInfo.iActiveDirective) + { + case RCD_GATHER_SUPPLIES: + case RCD_SUPPORT_MILITIA: + case RCD_TRAIN_MILITIA: + case RCD_CREATE_PROPAGANDA: + case RCD_HVT_STRIKES: + case RCD_SPOTTERS: + case RCD_RAID_MINES: + // effects applied elsewhere + break; + + case RCD_ELITE_MILITIA: + { + const UINT8 elites = static_cast(rebelCommandSaveInfo.directives[RCD_ELITE_MILITIA].GetValue1()); + // get the top-right most omerta tile + INT16 sx = 1, sy = 1; + for (sx = MAXIMUM_VALID_X_COORDINATE; sx >= MINIMUM_VALID_X_COORDINATE; --sx) + { + for (sy = MINIMUM_VALID_Y_COORDINATE; sy <= MAXIMUM_VALID_Y_COORDINATE; ++sy) + { + if (GetTownIdForSector(sx, sy) == OMERTA) + { + goto foundOmerta; + } + } + } + + foundOmerta: + + StrategicAddMilitiaToSector(sx, sy, ELITE_MILITIA, elites); + for (int i = 0 ; i < elites ; ++i) + { + CreateNewIndividualMilitia( ELITE_MILITIA, MO_ARULCO, SECTOR(sx, sy) ); + } + } + break; + + case RCD_CREATE_TURNCOATS: + { + if (LaptopSaveInfo.dIntelPool >= gRebelCommandSettings.fCreateTurncoatsIntelCost) + { + UINT32 groupNum = 1; + GROUP* groupPtr = gpGroupList; + GROUP* selectedGroupPtr = nullptr; + + // pick a random group + while (groupPtr != nullptr) + { + if (groupPtr->usGroupTeam != ENEMY_TEAM) + { + groupPtr = groupPtr->next; + continue; + } + + if (Random(groupNum) == 0) + { + selectedGroupPtr = groupPtr; + } + + groupNum++; + groupPtr = groupPtr->next; + } + + if (selectedGroupPtr == nullptr) + break; // couldn't find a group... // determine which enemies become turncoats UINT8 turncoatsToCreate = static_cast(rebelCommandSaveInfo.directives[RCD_CREATE_TURNCOATS].GetValue1()); @@ -2182,514 +3677,1378 @@ void DailyUpdate() Assert(netEnemyCount >= 0); turncoatsToCreate = min(turncoatsToCreate, netEnemyCount); - const UINT8 maxAdmins = selectedGroupPtr->pEnemyGroup->ubNumAdmins - selectedGroupPtr->pEnemyGroup->ubNumAdmins_Turncoat; - const UINT8 maxTroops = selectedGroupPtr->pEnemyGroup->ubNumTroops - selectedGroupPtr->pEnemyGroup->ubNumTroops_Turncoat; - const UINT8 maxElites = selectedGroupPtr->pEnemyGroup->ubNumElites - selectedGroupPtr->pEnemyGroup->ubNumElites_Turncoat; + const UINT8 maxAdmins = selectedGroupPtr->pEnemyGroup->ubNumAdmins - selectedGroupPtr->pEnemyGroup->ubNumAdmins_Turncoat; + const UINT8 maxTroops = selectedGroupPtr->pEnemyGroup->ubNumTroops - selectedGroupPtr->pEnemyGroup->ubNumTroops_Turncoat; + const UINT8 maxElites = selectedGroupPtr->pEnemyGroup->ubNumElites - selectedGroupPtr->pEnemyGroup->ubNumElites_Turncoat; + + UINT8 admins = 0; + UINT8 troops = 0; + UINT8 elites = 0; + + while (turncoatsToCreate > 0) + { + std::vector vec; + // weighted odds + if (admins < maxAdmins) + { + vec.push_back(0); + vec.push_back(0); + vec.push_back(0); + } + + if (troops < maxTroops) + { + vec.push_back(1); + vec.push_back(1); + } + + if (elites < maxElites) + { + vec.push_back(2); + } + + Assert(vec.size() > 0); + + const int choice = vec[Random(vec.size())]; + + switch (choice) + { + case 0: + admins++; + break; + + case 1: + troops++; + break; + + case 2: + elites++; + break; + } + + turncoatsToCreate--; + } + + // add the turncoats + selectedGroupPtr->pEnemyGroup->ubNumAdmins_Turncoat += admins; + selectedGroupPtr->pEnemyGroup->ubNumTroops_Turncoat += troops; + selectedGroupPtr->pEnemyGroup->ubNumElites_Turncoat += elites; + + LaptopSaveInfo.dIntelPool -= gRebelCommandSettings.fCreateTurncoatsIntelCost; + } + else + { + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szRebelCommandText[RCT_INSUFFICIENT_INTEL_TO_CREATE_TURNCOATS]); + } + } + break; + + case RCD_DRAFT: + { + AddVolunteers(static_cast(rebelCommandSaveInfo.directives[RCD_DRAFT].GetValue1() * CurrentPlayerProgressPercentage())); + for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) + { + DecrementTownLoyalty(a, static_cast(rebelCommandSaveInfo.directives[RCD_DRAFT].GetValue2())); + } + } + break; + } + + + // set new directive + const INT16 directive = rebelCommandSaveInfo.iSelectedDirective; + rebelCommandSaveInfo.iActiveDirective = directive; + + // increment supplies + iIncomingSuppliesPerDay = CalcIncomingSuppliesPerDay(static_cast(directive)); + rebelCommandSaveInfo.iSupplies += iIncomingSuppliesPerDay; + + // get regional bonuses + INT16 intelGain = 0; + INT16 supplyGain = 0; + INT16 moneyGain = 0; + 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) + rebelCommandSaveInfo.regions[a].adminStatus = RAS_INACTIVE; + + // ignore this region if there is no active admin team + if (rebelCommandSaveInfo.regions[a].adminStatus != RAS_ACTIVE) + continue; + + UINT32 coolness = 0; + // clunky... get town coolness. just get the top/left most sector + for (int x = MINIMUM_VALID_X_COORDINATE ; x <= MAXIMUM_VALID_X_COORDINATE ; ++x) + { + for(int y = MINIMUM_VALID_Y_COORDINATE ; y < MAXIMUM_VALID_Y_COORDINATE ; ++y) + { + if (GetTownIdForSector(x, y) == a) + { + coolness = gCoolnessBySector[SECTOR(x, y)]; + goto foundCoolness; + } + } + } + foundCoolness: + coolness = max(coolness, 1); + + const UINT8 loyalty = GetRegionLoyalty(a); + for (int b = 0; b < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++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; + + // toggle admin action off on a negative supply balance + if (rebelCommandSaveInfo.iSupplies <= 0) + rebelCommandSaveInfo.regions[a].SetInactive(b); + + if (rebelCommandSaveInfo.regions[a].IsActive(b)) + { + switch (static_cast(rebelCommandSaveInfo.regions[a].actions[b])) + { + case RCAA_SUPPLY_LINE: + case RCAA_SUPPLY_DISRUPTION: + case RCAA_SCOUTS: + case RCAA_MERC_SUPPORT: + case RCAA_PATHFINDERS: + case RCAA_FORTIFICATIONS: + case RCAA_HARRIERS: + case RCAA_MINING_POLICY: + case RCAA_SAFEHOUSES: + // no daily bonuses + 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)); + break; + + case RCAA_SMUGGLERS: + supplyGain += Random(static_cast(info.adminActions[RCAA_SMUGGLERS].fValue1 * level * loyalty / 100.f)); + 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)); + break; + + case RCAA_TAXES: + moneyGain += static_cast(info.adminActions[RCAA_TAXES].fValue1 * coolness * level * loyalty * (75.f + Random(26))/ 10000.f); + DecrementTownLoyalty(a, static_cast(info.adminActions[RCAA_TAXES].fValue2 * level)); + break; + + case RCAA_ASSIST_CIVILIANS: + AddVolunteers(static_cast(info.adminActions[RCAA_ASSIST_CIVILIANS].fValue1 * level * loyalty / 100.f)); + break; + + + default: + AssertMsg(false, "Unknown Admin Action"); + break; + } + } + } + } + + if (rebelCommandSaveInfo.iSupplies <= 0) + { + ScreenMsg(FONT_MCOLOR_RED, MSG_INTERFACE, szRebelCommandText[RCT_INSUFFICIENT_SUPPLIES_ADMIN_ACTIONS_DISABLED]); + } + + if (intelGain > 0) + { + swprintf(text, szRebelCommandText[RCT_DEAD_DROP_INCOME], intelGain); + ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text); + + AddIntel(intelGain, FALSE); + } + + if (supplyGain > 0) + { + swprintf(text, szRebelCommandText[RCT_SMUGGLER_INCOME], supplyGain); + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text ); + + rebelCommandSaveInfo.iSupplies += supplyGain; + } + + if (moneyGain > 0) + { + AddTransactionToPlayersBook(REBEL_COMMAND, 0, GetWorldTotalMin(), moneyGain); + } + + // update missions + if (rebelCommandSaveInfo.cachedBountyPayout > 0) + { + AddTransactionToPlayersBook(REBEL_COMMAND_BOUNTY_PAYOUT, 0, GetWorldTotalMin(), rebelCommandSaveInfo.cachedBountyPayout); + rebelCommandSaveInfo.cachedBountyPayout = 0; + } + + // RCAM_DISRUPT_ASD + ApplyAdditionalASDEffects(); + + if (GetWorldDay() % gRebelCommandSettings.iMissionRefreshTimeDays == 0) + { + std::unordered_set validMissions; + for (int i = 0; i < RCAM_NUM_MISSIONS; ++i) + { + if (i == RCAM_SOLDIER_BOUNTIES_KINGPIN && !(CheckFact(FACT_KINGPIN_INTRODUCED_SELF, 0) == TRUE && CheckFact(FACT_KINGPIN_DEAD, 0) == FALSE && CheckFact(FACT_KINGPIN_IS_ENEMY, 0) == FALSE && CurrentPlayerProgressPercentage() >= 30)) continue; + else if (i == RCAM_DISRUPT_ASD && gGameExternalOptions.fASDActive == FALSE) continue; + + 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 missionIndex = static_cast(Random(validMissions.size())); + auto iter = validMissions.cbegin(); + for (INT8 j = 0; j < missionIndex; ++j) iter++; + rebelCommandSaveInfo.availableMissions[i] = *iter; + validMissions.erase(static_cast(rebelCommandSaveInfo.availableMissions[i])); + } + } + else // 1 mission available + { + 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++; + } + } + + ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, szRebelCommandText[RCT_NEW_MISSIONS_AVAILABLE_NOTIFICATION]); + } +} + +void HourlyUpdate() +{ + HandleScouting(); + + // RCAM_SEND_SUPPLIES_TO_TOWN + SendSuppliesToTownMission(); + + // it's midnight! do the daily update + if (GetWorldHour() == 0) + { + DailyUpdate(); + } +} + +void Init() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + INT8 startingMaxLoyalty; + + switch (gGameOptions.ubDifficultyLevel) + { + case DIF_LEVEL_EASY: + startingMaxLoyalty = gRebelCommandSettings.iMaxLoyaltyNovice; + break; + + case DIF_LEVEL_HARD: + startingMaxLoyalty = gRebelCommandSettings.iMaxLoyaltyExpert; + break; + + case DIF_LEVEL_INSANE: + startingMaxLoyalty = gRebelCommandSettings.iMaxLoyaltyInsane; + break; + + case DIF_LEVEL_MEDIUM: + default: + startingMaxLoyalty = gRebelCommandSettings.iMaxLoyaltyExperienced; + break; + } + + // set base values + iIncomingSuppliesPerDay = static_cast(CurrentPlayerProgressPercentage() * gRebelCommandSettings.fIncomeModifier); + rebelCommandSaveInfo.iSelectedDirective = RCD_GATHER_SUPPLIES; + rebelCommandSaveInfo.iActiveDirective = RCD_GATHER_SUPPLIES; + rebelCommandSaveInfo.iMilitiaStatsLevel = 0; + // let's be nice and give some supplies to people who enable this feature partway through a campaign so they don't start from zero + rebelCommandSaveInfo.iSupplies = static_cast(GetWorldDay() * CurrentPlayerProgressPercentage() * gRebelCommandSettings.fIncomeModifier / 3); + + // initialise admin actions -- first one is always supply line + std::vector actions; + actions.push_back(RCAA_REBEL_RADIO); + actions.push_back(RCAA_SAFEHOUSES); + actions.push_back(RCAA_SUPPLY_DISRUPTION); + actions.push_back(RCAA_SCOUTS); + actions.push_back(RCAA_SMUGGLERS); + actions.push_back(RCAA_TAXES); + actions.push_back(RCAA_MERC_SUPPORT); + if (gGameExternalOptions.fMilitiaResources == TRUE) + actions.push_back(RCAA_WAREHOUSES); + if (gGameExternalOptions.fIntelResource == TRUE) + actions.push_back(RCAA_DEAD_DROPS); + if (gGameExternalOptions.fMilitiaVolunteerPool == TRUE) + actions.push_back(RCAA_ASSIST_CIVILIANS); + // RCAA_MINING_POLICY is added below in the region init + actions.push_back(RCAA_PATHFINDERS); + actions.push_back(RCAA_HARRIERS); + actions.push_back(RCAA_FORTIFICATIONS); + + SetupInfo(); + + for (int d = 0 ; d < RCD_NUM_DIRECTIVES ; ++d) + { + rebelCommandSaveInfo.directives[d].id = static_cast(d); + rebelCommandSaveInfo.directives[d].iLevel = 0; + } + + // init regions + for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) + { + // init max loyalty + if (a == OMERTA) + { + // as rebel hq, omerta gets 100% max loyalty and an admin team + rebelCommandSaveInfo.regions[OMERTA].adminStatus = RAS_ACTIVE; + rebelCommandSaveInfo.regions[OMERTA].ubMaxLoyalty = MAX_LOYALTY_VALUE; + } + else + { + // init admins + rebelCommandSaveInfo.regions[a].adminStatus = RAS_NONE; + rebelCommandSaveInfo.regions[a].ubMaxLoyalty = startingMaxLoyalty; + gTownLoyalty[a].ubRating = min(gTownLoyalty[a].ubRating, startingMaxLoyalty); + } + + // init admin actions per region. each region has supply line + 5 random actions + std::vector vec(actions); + + // check to see if this town has a mine - if it does, add it to this region's admin action pool + if (GetMineSectorForTown(a) != -1) + vec.push_back(RCAA_MINING_POLICY); + + // randomise pool + for (size_t z = 0; z < vec.size(); ++z) + { + const int randomIndex = Random(vec.size()); + std::swap(vec[randomIndex], vec[z]); + } + vec.resize(5); + std::sort(vec.begin(), vec.end()); + for (int b = 0; b < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++b) + { + if (b == 0) + rebelCommandSaveInfo.regions[a].actions[0] = RCAA_SUPPLY_LINE; // first action is always supply line + else + rebelCommandSaveInfo.regions[a].actions[b] = vec[b - 1]; + + rebelCommandSaveInfo.regions[a].actionLevels[b] = 0; + } + } + + // init missions + for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) + { + rebelCommandSaveInfo.availableMissions[i] = RCAM_NONE; + } +} + +BOOLEAN Load(HWFILE file) +{ + if (guiCurrentSaveGameVersion >= REBELCOMMAND) + { + UINT32 numBytesRead = 0; + + numBytesRead = FileRead(file, &rebelCommandSaveInfo, sizeof(RebelCommand::SaveInfo), &numBytesRead); + + SetupInfo(); + } + else + { + Init(); + } - UINT8 admins = 0; - UINT8 troops = 0; - UINT8 elites = 0; + // missions update check + if (rebelCommandSaveInfo.availableMissions[0] == rebelCommandSaveInfo.availableMissions[1] && rebelCommandSaveInfo.availableMissions[0] != RCAM_NONE) + { + // init missions + for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) + { + rebelCommandSaveInfo.availableMissions[i] = RCAM_NONE; + } + } - while (turncoatsToCreate > 0) + // go through every strategic event to find active agent missions + std::vector> missions = GetAllStrategicEventsOfType(EVENT_REBELCOMMAND); + missionMap.clear(); + for (std::vector>::iterator it = missions.begin(); it != missions.end(); ++it) + { + MissionFirstEvent evt1; + DeserialiseMissionFirstEvent(it->second, evt1); + if (evt1.isFirstEvent) + { + missionMap.insert(std::make_pair(static_cast(evt1.missionId), it->second)); + } + + MissionSecondEvent evt2; + DeserialiseMissionSecondEvent(it->second, evt2); + + if (evt2.isSecondEvent) + { + missionMap.insert(std::make_pair(static_cast(evt2.missionId), it->second)); + } + } + return TRUE; +} + +BOOLEAN Save(HWFILE file) +{ + UINT32 uiNumBytesWritten = 0; + + if (!FileWrite(file, &rebelCommandSaveInfo, sizeof(RebelCommand::SaveInfo), &uiNumBytesWritten)) + return FALSE; + + return TRUE; +} + +void SetupInfo() +{ + iCurrentRegionId = 1; + + const auto toFloatVec = [](const std::vector intVec, std::vector& floatVec) + { + floatVec.clear(); + for (const auto val : intVec) + floatVec.push_back(static_cast(val)); + }; + + // initialise directives + Directive d; + info.directives.clear(); + + d.iCostToImprove = gRebelCommandSettings.iGatherSuppliesCosts; + toFloatVec(gRebelCommandSettings.iGatherSuppliesIncome, d.fValue1); + d.fValue2.clear(); + info.directives.insert(info.directives.begin() + RCD_GATHER_SUPPLIES, d); + + d.iCostToImprove = gRebelCommandSettings.iSupportMilitiaCosts; + d.fValue1 = gRebelCommandSettings.fSupportMilitiaDiscounts; + d.fValue2.clear(); + info.directives.insert(info.directives.begin() + RCD_SUPPORT_MILITIA, d); + + d.iCostToImprove = gRebelCommandSettings.iTrainMilitiaCosts; + d.fValue1 = gRebelCommandSettings.fTrainMilitiaDiscount; + toFloatVec(gRebelCommandSettings.iTrainMilitiaSpeedBonus, d.fValue2); + info.directives.insert(info.directives.begin() + RCD_TRAIN_MILITIA, d); + + d.iCostToImprove = gRebelCommandSettings.iCreatePropagandaCosts; + d.fValue1 = gRebelCommandSettings.fCreatePropagandaModifier; + d.fValue2.clear(); + info.directives.insert(info.directives.begin() + RCD_CREATE_PROPAGANDA, d); + + d.iCostToImprove = gRebelCommandSettings.iEliteMilitiaCosts; + toFloatVec(gRebelCommandSettings.iEliteMilitiaPerDay, d.fValue1); + d.fValue2.clear(); + info.directives.insert(info.directives.begin() + RCD_ELITE_MILITIA, d); + + d.iCostToImprove = gRebelCommandSettings.iHvtStrikesCosts; + toFloatVec(gRebelCommandSettings.iHvtStrikesChance, d.fValue1); + d.fValue2.clear(); + info.directives.insert(info.directives.begin() + RCD_HVT_STRIKES, d); + + d.iCostToImprove = gRebelCommandSettings.iSpottersCosts; + toFloatVec(gRebelCommandSettings.iSpottersModifier, d.fValue1); + d.fValue2.clear(); + info.directives.insert(info.directives.begin() + RCD_SPOTTERS, d); + + d.iCostToImprove = gRebelCommandSettings.iRaidMinesCosts; + d.fValue1 = gRebelCommandSettings.fRaidMinesPercentage; + d.fValue2.clear(); + info.directives.insert(info.directives.begin() + RCD_RAID_MINES, d); + + d.iCostToImprove = gRebelCommandSettings.iCreateTurncoatsCosts; + toFloatVec(gRebelCommandSettings.iCreateTurncoatsPerDay, d.fValue1); + d.fValue2.clear(); + info.directives.insert(info.directives.begin() + RCD_CREATE_TURNCOATS, d); + + d.iCostToImprove = gRebelCommandSettings.iDraftCosts; + toFloatVec(gRebelCommandSettings.iDraftPerDayModifier, d.fValue1); + toFloatVec(gRebelCommandSettings.iDraftLoyaltyLossPerDay, d.fValue2); + info.directives.insert(info.directives.begin() + RCD_DRAFT, d); + + // init admin actions + AdminAction aa; + info.adminActions.clear(); + + aa.fValue1 = static_cast(gRebelCommandSettings.iSupplyLineMaxLoyaltyIncrease); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_SUPPLY_LINE, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iRebelRadioDailyLoyaltyGain); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_REBEL_RADIO, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iSafehouseMinimumSoldiers); + aa.fValue2 = static_cast(gRebelCommandSettings.iSafehouseBonusSoldiers); + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_SAFEHOUSES, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iSupplyDisruptionEnemyStatLoss); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_SUPPLY_DISRUPTION, aa); + + aa.fValue1 = 0.f; + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_SCOUTS, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iDeadDropsDailyIntel); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_DEAD_DROPS, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iSmugglersDailySupplies); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_SMUGGLERS, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iWarehousesDailyMilitiaGuns); + aa.fValue2 = static_cast(gRebelCommandSettings.iWarehousesDailyMilitiaArmour); + aa.fValue3 = static_cast(gRebelCommandSettings.iWarehousesDailyMilitiaMisc); + info.adminActions.insert(info.adminActions.begin() + RCAA_WAREHOUSES, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iTaxesDailyIncome); + aa.fValue2 = static_cast(gRebelCommandSettings.iTaxesDailyLoyaltyLoss); + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_TAXES, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iAssistCiviliansDailyVolunteers); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_ASSIST_CIVILIANS, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iMercSupportBonus); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_MERC_SUPPORT, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iMiningPolicyBonus); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_MINING_POLICY, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.uPathfindersSpeedBonus); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_PATHFINDERS, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.uHarriersSpeedPenalty); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_HARRIERS, aa); + + aa.fValue1 = static_cast(gRebelCommandSettings.iFortificationsBonus); + aa.fValue2 = 0.f; + aa.fValue3 = 0.f; + info.adminActions.insert(info.adminActions.begin() + RCAA_FORTIFICATIONS, aa); + + // rftr todo: this should really be a map... but as long as we insert mission info in the same order as the enum then we're good + 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 }, + // { int modifier for checked trait }, + // { 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_DISRUPT_ASD + MissionHelpers::missionInfo.push_back( + { + {COVERT_NT, TECHNICIAN_NT, DEMOLITIONS_NT, NIGHT_OPS_NT}, + {-1, -1, -1, NIGHTOPS_OT}, + {gRebelCommandSettings.iDisruptAsdDuration_Bonus_Covert, gRebelCommandSettings.iDisruptAsdDuration_Bonus_Technician, gRebelCommandSettings.iDisruptAsdDuration_Bonus_Demolitions, gRebelCommandSettings.iDisruptAsdDuration_Bonus_Nightops}, + {gRebelCommandSettings.fDisruptAsdIncomeReductionModifier_Covert, gRebelCommandSettings.fDisruptAsdIncomeReductionModifier_Technician, gRebelCommandSettings.fDisruptAsdIncomeReductionModifier_Technician, gRebelCommandSettings.fDisruptAsdIncomeReductionModifier_Nightops}, + {0, 0, 0, 0}, + {0, MissionHelpers::DISRUPT_ASD_STEAL_FUEL, MissionHelpers::DISRUPT_ASD_DESTROY_RESERVES, 0} + }); + //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_SEND_SUPPLIES_TO_TOWN + MissionHelpers::missionInfo.push_back({ }); // no entries necessary - no modifiers + //RCAM_SOLDIER_BOUNTIES_KINGPIN + MissionHelpers::missionInfo.push_back( + { + {COVERT_NT, SQUADLEADER_NT, SNITCH_NT, DEMOLITIONS_NT}, + {-1, -1, -1, -1}, + {gRebelCommandSettings.iSoldierBountiesKingpinDuration_Bonus_Covert, 0, 0, gRebelCommandSettings.iSoldierBountiesKingpinDuration_Bonus_Demolitions}, + {gRebelCommandSettings.fSoldierBountiesKingpinPayout_Bonus_Covert, gRebelCommandSettings.fSoldierBountiesKingpinPayout_Bonus_Deputy, gRebelCommandSettings.fSoldierBountiesKingpinPayout_Bonus_Snitch, 1.f}, + {0, 0, gRebelCommandSettings.iSoldierBountiesKingpinPayout_Limit_Snitch, gRebelCommandSettings.iSoldierBountiesKingpinPayout_Limit_Demolitions}, + {0, MissionHelpers::SOLDIER_BOUNTIES_KINGPIN_OFFICER_PAYOUTS, 0, MissionHelpers::SOLDIER_BOUNTIES_KINGPIN_VEHICLE_PAYOUTS} + }); + //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} + }); + + // cache item IDs + ItemIdCache::Clear(); + // (Item[i].usItemClass & IC_AMMO) && (Magazine[ Item[i].ubClassIndex ].ubMagType == AMMO_BOX or AMMO_CRATE?) + for (UINT16 i = 0; i < MAXITEMS; ++i) + { + if (Item[i].gascan) ItemIdCache::gasCans.push_back(i); + else if (Item[i].firstaidkit) ItemIdCache::firstAidKits.push_back(i); + else if (Item[i].medicalkit) ItemIdCache::medKits.push_back(i); + else if (Item[i].toolkit) ItemIdCache::toolKits.push_back(i); + else if (Item[i].usItemClass & IC_AMMO) + { + if (Magazine[Item[i].ubClassIndex].ubMagType == AMMO_BOX) + { + if ((gGameOptions.fGunNut || !Item[i].biggunlist) + && (gGameOptions.ubGameStyle == STYLE_SCIFI || !Item[i].scifi)) { - std::vector vec; - // weighted odds - if (admins < maxAdmins) - { - vec.push_back(0); - vec.push_back(0); - vec.push_back(0); - } + // coolness runs from 1-10, so apply offset + ItemIdCache::ammo[Item[i].ubCoolness-1].push_back(i); + } + } + } + + } +} - if (troops < maxTroops) - { - vec.push_back(1); - vec.push_back(1); - } +void UpgradeMilitiaStats() +{ + const INT32 cost = gRebelCommandSettings.iMilitiaUpgradeCosts[rebelCommandSaveInfo.iMilitiaStatsLevel]; + if (cost > LaptopSaveInfo.iCurrentBalance) + { + DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, szRebelCommandText[RCT_INSUFFICIENT_FUNDS], LAPTOP_SCREEN, MSG_BOX_FLAG_OK, NULL); + return; + } + + CHAR16 text[200]; + swprintf(text, szRebelCommandText[RCT_MILITIA_UPGRADE_STATS_PROMPT], cost); + DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, text, LAPTOP_SCREEN, MSG_BOX_FLAG_YESNO, [](UINT8 exitValue) { + if (exitValue == MSG_BOX_RETURN_YES) + { + const INT32 cost = gRebelCommandSettings.iMilitiaUpgradeCosts[rebelCommandSaveInfo.iMilitiaStatsLevel++]; + + AddTransactionToPlayersBook(REBEL_COMMAND_SPENDING, 0, GetWorldTotalMin(), -cost); + + RenderWebsite(); + } + }); +} + +void ApplySoldierBounty(const SOLDIERTYPE* pSoldier) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; + + if (pSoldier->bTeam != ENEMY_TEAM) + return; + + const std::unordered_map::iterator iter = missionMap.find(RCAM_SOLDIER_BOUNTIES_KINGPIN); + + if (iter == missionMap.end()) + return; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + if (!evt.isSecondEvent) + return; - if (elites < maxElites) - { - vec.push_back(2); - } + UINT16 payout = 0; + + if (TANK(pSoldier) && evt.extraBits == MissionHelpers::SOLDIER_BOUNTIES_KINGPIN_VEHICLE_PAYOUTS) + payout += gRebelCommandSettings.iSoldierBountiesKingpinPayout_Tank; + else if (COMBAT_JEEP(pSoldier) && evt.extraBits == MissionHelpers::SOLDIER_BOUNTIES_KINGPIN_VEHICLE_PAYOUTS) + payout += gRebelCommandSettings.iSoldierBountiesKingpinPayout_Jeep; + else if (ENEMYROBOT(pSoldier) && evt.extraBits == MissionHelpers::SOLDIER_BOUNTIES_KINGPIN_VEHICLE_PAYOUTS) + payout += gRebelCommandSettings.iSoldierBountiesKingpinPayout_Robot; + else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ADMINISTRATOR) + payout += gRebelCommandSettings.iSoldierBountiesKingpinPayout_Admin; + else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ARMY) + payout += gRebelCommandSettings.iSoldierBountiesKingpinPayout_Troop; + else if (pSoldier->ubSoldierClass == SOLDIER_CLASS_ELITE) + payout += gRebelCommandSettings.iSoldierBountiesKingpinPayout_Elite; + else // unknown kill, bail out! + return; - Assert(vec.size() > 0); + // payout per role + if (pSoldier->usSoldierFlagMask & SOLDIER_ENEMY_OFFICER && (evt.extraBits == MissionHelpers::SOLDIER_BOUNTIES_KINGPIN_OFFICER_PAYOUTS)) + payout += gRebelCommandSettings.iSoldierBountiesKingpinPayout_Officer; - const int choice = vec[Random(vec.size())]; - switch (choice) - { - case 0: - admins++; - break; + // apply payout limit + UINT32 durationBonus = 0; + int durationBonusSkill = 0; + INT16 intModifier = 0; + int intModifierSkill = 0; + FLOAT floatModifier = 0.f; + int floatModifierSkill = 0; + UINT16 extraBits = 0; + MissionHelpers::GetMissionInfo(RCAM_SOLDIER_BOUNTIES_KINGPIN, &gMercProfiles[evt.mercProfileId], durationBonus, floatModifier, intModifier, durationBonusSkill, floatModifierSkill, intModifierSkill, extraBits); - case 1: - troops++; - break; + const INT32 payoutLimit = max(gRebelCommandSettings.iSoldierBountiesKingpinPayout_Limit, intModifier); + // clamp payout like this in case the player maxes out payouts in config and we have to deal with a uint overflow + if (payoutLimit - payout < rebelCommandSaveInfo.cachedBountyPayout) + payout = payoutLimit - rebelCommandSaveInfo.cachedBountyPayout; - case 2: - elites++; - break; - } + rebelCommandSaveInfo.cachedBountyPayout += payout; +} - turncoatsToCreate--; - } +void ApplyEnemyMechanicalUnitPenalties(SOLDIERTYPE* pSoldier) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; - // add the turncoats - selectedGroupPtr->pEnemyGroup->ubNumAdmins_Turncoat += admins; - selectedGroupPtr->pEnemyGroup->ubNumTroops_Turncoat += troops; - selectedGroupPtr->pEnemyGroup->ubNumElites_Turncoat += elites; + const std::unordered_map::iterator iter = missionMap.find(RCAM_SABOTAGE_MECHANICAL_UNITS); - LaptopSaveInfo.dIntelPool -= gRebelCommandSettings.fCreateTurncoatsIntelCost; - } - else - { - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szRebelCommandText[RCT_INSUFFICIENT_INTEL_TO_CREATE_TURNCOATS]); - } - } - break; + if (iter == missionMap.end()) + return; - case RCD_DRAFT: + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + if (evt.isSecondEvent) + { + INT8 statLoss = 0; + switch (evt.extraBits) { - AddVolunteers(static_cast(rebelCommandSaveInfo.directives[RCD_DRAFT].GetValue1() * CurrentPlayerProgressPercentage())); - for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) - { - DecrementTownLoyalty(a, static_cast(rebelCommandSaveInfo.directives[RCD_DRAFT].GetValue2())); - } + case MissionHelpers::SABOTAGE_MECHANICAL_UNITS_COVERT: statLoss = gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Covert; break; + case MissionHelpers::SABOTAGE_MECHANICAL_UNITS_DEMOLITIONS: statLoss = gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Demolitions; break; + case MissionHelpers::SABOTAGE_MECHANICAL_UNITS_HEAVY_WEAPONS: statLoss = gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss_Heavy_Weapons; break; + default: statLoss = gRebelCommandSettings.iSabotageMechanicalUnitsStatLoss; break; } - break; + + pSoldier->stats.bLife -= statLoss; + pSoldier->stats.bLifeMax = pSoldier->stats.bLife; + pSoldier->stats.bAgility -= statLoss; + pSoldier->stats.bDexterity -= statLoss; + pSoldier->stats.bStrength -= statLoss; + pSoldier->stats.bMarksmanship -= statLoss; + + 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; - // set new directive - const INT16 directive = rebelCommandSaveInfo.iSelectedDirective; - rebelCommandSaveInfo.iActiveDirective = directive; + // rftr todo: check bitmask for specific possible traits +} - // increment supplies - const UINT8 progress = CurrentPlayerProgressPercentage(); - iIncomingSuppliesPerDay = static_cast(progress * gRebelCommandSettings.fIncomeModifier + (directive == RCD_GATHER_SUPPLIES ? rebelCommandSaveInfo.directives[RCD_GATHER_SUPPLIES].GetValue1() : 0)); - rebelCommandSaveInfo.iSupplies += iIncomingSuppliesPerDay; +void ApplyVisionModifier(const SOLDIERTYPE* pSoldier, INT32& sight) +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; - // get regional bonuses - INT16 intelGain = 0; - INT16 supplyGain = 0; - INT16 moneyGain = 0; - for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) - { - // check to see if the town is lost - if (IsTownUnderCompleteControlByEnemy(a) && rebelCommandSaveInfo.regions[a].adminStatus == RAS_ACTIVE) - rebelCommandSaveInfo.regions[a].adminStatus = RAS_INACTIVE; + const std::unordered_map::iterator iter = missionMap.find(RCAM_REDUCE_UNALERTED_ENEMY_VISION); - // ignore this region if there is no active admin team - if (rebelCommandSaveInfo.regions[a].adminStatus != RAS_ACTIVE) - continue; + if (iter == missionMap.end()) + return; - UINT32 coolness = 0; - // clunky... get town coolness. just get the top/left most sector - for (int x = MINIMUM_VALID_X_COORDINATE ; x <= MAXIMUM_VALID_X_COORDINATE ; ++x) + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + if (evt.isSecondEvent) + { + FLOAT modifier = 0.f; + switch (evt.extraBits) { - for(int y = MINIMUM_VALID_Y_COORDINATE ; y < MAXIMUM_VALID_Y_COORDINATE ; ++y) - { - if (GetTownIdForSector(x, y) == a) - { - coolness = gCoolnessBySector[SECTOR(x, y)]; - goto foundCoolness; - } - } + case MissionHelpers::REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_COVERT: modifier = gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Covert; break; + case MissionHelpers::REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_RADIO: modifier = gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Radio; break; + case MissionHelpers::REDUCE_UNALERTED_ENEMY_VISION_MODIFIER_STEALTHY: modifier = gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier_Stealthy; break; + default: modifier = gRebelCommandSettings.fReduceUnlaertedEnemyVisionModifier; break; } - foundCoolness: - coolness = max(coolness, 1); - const UINT8 loyalty = GetRegionLoyalty(a); - for (int b = 0; b < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++b) + if (pSoldier->bTeam == ENEMY_TEAM && pSoldier->aiData.bAlertStatus == STATUS_GREEN) { - const INT8 level = rebelCommandSaveInfo.regions[a].actionLevels[b]; - 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; + sight = static_cast(sight * (1.f - modifier)); + } + } +} - case RCAA_REBEL_RADIO: - IncrementTownLoyalty(a, static_cast(info.adminActions[RCAA_REBEL_RADIO].fValue1 * level)); - break; +BOOLEAN CanAssignTraitsToMilitia() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return FALSE; - case RCAA_DEAD_DROPS: - intelGain += Random(static_cast(info.adminActions[RCAA_DEAD_DROPS].fValue1 * level * loyalty / 100.f)); - break; - - case RCAA_SMUGGLERS: - supplyGain += Random(static_cast(info.adminActions[RCAA_SMUGGLERS].fValue1 * level * loyalty / 100.f)); - break; + // rftr todo: check bitmask + + return TRUE; +} - 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)); - break; +BOOLEAN CanTrainMilitiaAnywhere() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return FALSE; - case RCAA_TAXES: - moneyGain += static_cast(info.adminActions[RCAA_TAXES].fValue1 * coolness * level * loyalty * (75.f + Random(26))/ 10000.f); - DecrementTownLoyalty(a, static_cast(info.adminActions[RCAA_TAXES].fValue2 * level)); - break; + const std::unordered_map::iterator iter = missionMap.find(RCAM_TRAIN_MILITIA_ANYWHERE); - case RCAA_ASSIST_CIVILIANS: - AddVolunteers(static_cast(info.adminActions[RCAA_ASSIST_CIVILIANS].fValue1 * level * loyalty / 100.f)); - break; + if (iter == missionMap.end()) + return FALSE; + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); - default: - AssertMsg(false, "Unknown Admin Action"); - break; - } - } - } + return evt.isSecondEvent; +} - CHAR16 text[200]; - if (intelGain > 0) - { - swprintf(text, szRebelCommandText[RCT_DEAD_DROP_INCOME], intelGain); - ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text); +UINT8 GetMaxTrainersForTrainMilitiaAnywhere() +{ + const std::unordered_map::iterator iter = missionMap.find(RCAM_TRAIN_MILITIA_ANYWHERE); - AddIntel(intelGain, FALSE); - } + if (iter == missionMap.end()) + return FALSE; - if (supplyGain > 0) - { - swprintf(text, szRebelCommandText[RCT_SMUGGLER_INCOME], supplyGain); - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s", text ); + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); - rebelCommandSaveInfo.iSupplies += supplyGain; - } + if (!evt.isSecondEvent) + return 0; - if (moneyGain > 0) + switch (evt.extraBits) { - AddTransactionToPlayersBook(REBEL_COMMAND, 0, GetWorldTotalMin(), moneyGain); + case MissionHelpers::TRAIN_MILITIA_ANYWHERE_TEACHING: return gRebelCommandSettings.iTrainMilitiaAnywhereMaxTrainers_Teaching; + default: return gRebelCommandSettings.iTrainMilitiaAnywhereMaxTrainers; } } -void HourlyUpdate() +INT16 GetAdditionalDeployRange(const UINT8 insertionCode) { - HandleScouting(); + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0; - // it's midnight! do the daily update - if (GetWorldHour() == 0) + const std::unordered_map::iterator iter = missionMap.find(RCAM_DEEP_DEPLOYMENT); + + if (iter == missionMap.end()) + return 0; + + INT16 range = 0; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + // we only get a range bonus if the mission is active! + if (evt.isSecondEvent) { - DailyUpdate(); + switch (insertionCode) + { + case INSERTION_CODE_NORTH: + case INSERTION_CODE_SOUTH: + { + range = gRebelCommandSettings.iDeepDeploymentRangeNS; + + 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: + { + 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; } -void Init() +BOOLEAN GetASDCanDeployUnits() { if (!gGameExternalOptions.fRebelCommandEnabled) - return; + return TRUE; - INT8 startingMaxLoyalty; + const std::unordered_map::iterator iter = missionMap.find(RCAM_DISRUPT_ASD); - switch (gGameOptions.ubDifficultyLevel) - { - case DIF_LEVEL_EASY: - startingMaxLoyalty = gRebelCommandSettings.iMaxLoyaltyNovice; - break; + if (iter == missionMap.end()) + return TRUE; - case DIF_LEVEL_HARD: - startingMaxLoyalty = gRebelCommandSettings.iMaxLoyaltyExpert; - break; + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); - case DIF_LEVEL_INSANE: - startingMaxLoyalty = gRebelCommandSettings.iMaxLoyaltyInsane; - break; + return evt.isSecondEvent ? FALSE : TRUE; +} - case DIF_LEVEL_MEDIUM: - default: - startingMaxLoyalty = gRebelCommandSettings.iMaxLoyaltyExperienced; - break; - } +FLOAT GetASDIncomeModifier() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 1.f; - // set base values - iIncomingSuppliesPerDay = static_cast(CurrentPlayerProgressPercentage() * gRebelCommandSettings.fIncomeModifier); - rebelCommandSaveInfo.iSelectedDirective = RCD_GATHER_SUPPLIES; - rebelCommandSaveInfo.iActiveDirective = RCD_GATHER_SUPPLIES; - rebelCommandSaveInfo.iMilitiaStatsLevel = 0; - // let's be nice and give some supplies to people who enable this feature partway through a campaign so they don't start from zero - rebelCommandSaveInfo.iSupplies = static_cast(GetWorldDay() * CurrentPlayerProgressPercentage() * gRebelCommandSettings.fIncomeModifier / 3); + const std::unordered_map::iterator iter = missionMap.find(RCAM_DISRUPT_ASD); - // initialise admin actions -- first one is always supply line - std::vector actions; - actions.push_back(RCAA_REBEL_RADIO); - actions.push_back(RCAA_SAFEHOUSES); - actions.push_back(RCAA_SUPPLY_DISRUPTION); - actions.push_back(RCAA_SCOUTS); - actions.push_back(RCAA_SMUGGLERS); - actions.push_back(RCAA_TAXES); - actions.push_back(RCAA_MERC_SUPPORT); - if (gGameExternalOptions.fMilitiaResources == TRUE) - actions.push_back(RCAA_WAREHOUSES); - if (gGameExternalOptions.fIntelResource == TRUE) - actions.push_back(RCAA_DEAD_DROPS); - if (gGameExternalOptions.fMilitiaVolunteerPool == TRUE) - actions.push_back(RCAA_ASSIST_CIVILIANS); - // RCAA_MINING_POLICY is added below in the region init - actions.push_back(RCAA_PATHFINDERS); - actions.push_back(RCAA_HARRIERS); - actions.push_back(RCAA_FORTIFICATIONS); + if (iter == missionMap.end()) + return 1.f; - SetupInfo(); + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); - for (int d = 0 ; d < RCD_NUM_DIRECTIVES ; ++d) + if (evt.isSecondEvent) { - rebelCommandSaveInfo.directives[d].id = static_cast(d); - rebelCommandSaveInfo.directives[d].iLevel = 0; + const MERCPROFILESTRUCT* merc = &gMercProfiles[evt.mercProfileId]; + UINT32 durationBonus; + FLOAT floatModifier; + INT16 intModifier; + int durSkill; + int floatSkill; + int intSkill; + UINT16 extraBits; + MissionHelpers::GetMissionInfo(RCAM_DISRUPT_ASD, merc, durationBonus, floatModifier, intModifier, durSkill, floatSkill, intSkill, extraBits); + + return 1.f - floatModifier; } - // init regions - for (int a = FIRST_TOWN; a < NUM_TOWNS; ++a) - { - // init max loyalty - if (a == OMERTA) - { - // as rebel hq, omerta gets 100% max loyalty and an admin team - rebelCommandSaveInfo.regions[OMERTA].adminStatus = RAS_ACTIVE; - rebelCommandSaveInfo.regions[OMERTA].ubMaxLoyalty = MAX_LOYALTY_VALUE; - } - else - { - // init admins - rebelCommandSaveInfo.regions[a].adminStatus = RAS_NONE; - rebelCommandSaveInfo.regions[a].ubMaxLoyalty = startingMaxLoyalty; - gTownLoyalty[a].ubRating = min(gTownLoyalty[a].ubRating, startingMaxLoyalty); - } + return 1.f; +} - // init admin actions per region. each region has supply line + 5 random actions - std::vector vec(actions); +void ApplyAdditionalASDEffects() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; - // check to see if this town has a mine - if it does, add it to this region's admin action pool - if (GetMineSectorForTown(a) != -1) - vec.push_back(RCAA_MINING_POLICY); + const std::unordered_map::iterator iter = missionMap.find(RCAM_DISRUPT_ASD); + + if (iter == missionMap.end()) + return; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); - // randomise pool - for (size_t z = 0; z < vec.size(); ++z) + if (evt.isSecondEvent) + { + switch (evt.extraBits) { - const int randomIndex = Random(vec.size()); - std::swap(vec[randomIndex], vec[z]); - } - vec.resize(5); - std::sort(vec.begin(), vec.end()); - for (int b = 0; b < REBEL_COMMAND_MAX_ACTIONS_PER_REGION; ++b) + case MissionHelpers::DISRUPT_ASD_STEAL_FUEL: { - if (b == 0) - rebelCommandSaveInfo.regions[a].actions[0] = RCAA_SUPPLY_LINE; // first action is always supply line - else - rebelCommandSaveInfo.regions[a].actions[b] = vec[b - 1]; + // spawn a gas can + CreateItemAtAirport(ItemIdCache::gasCans.at(ItemIdCache::gasCans.size()), 75 + Random(26)); - rebelCommandSaveInfo.regions[a].actionLevels[b] = 0; + // say it came from the ASD's reserves + AddStrategicAIResources(ASD_FUEL, -(gGameExternalOptions.gASDResource_Fuel_Jeep + Random(gGameExternalOptions.gASDResource_Fuel_Jeep))); + } + break; + + case MissionHelpers::DISRUPT_ASD_DESTROY_RESERVES: + { + // priority: tank > jeep > robots + if (GetStrategicAIResourceCount(ASD_TANK) > 0) + AddStrategicAIResources(ASD_TANK, -(1 + Random(2))); + else if (GetStrategicAIResourceCount(ASD_JEEP) > 0) + AddStrategicAIResources(ASD_JEEP, -(1 + Random(2))); + else if (GetStrategicAIResourceCount(ASD_ROBOT) > 0) + AddStrategicAIResources(ASD_ROBOT, -(2 + Random(3))); + + // let's also try to destroy a good amount of fuel + AddStrategicAIResources(ASD_FUEL, -(gGameExternalOptions.gASDResource_Fuel_Tank + Random(gGameExternalOptions.gASDResource_Fuel_Tank))); + } + break; } } } -BOOLEAN Load(HWFILE file) +INT8 GetEnemyEquipmentCoolnessModifier() { - if (guiCurrentSaveGameVersion >= REBELCOMMAND) - { - UINT32 numBytesRead = 0; + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0; - numBytesRead = FileRead(file, &rebelCommandSaveInfo, sizeof(RebelCommand::SaveInfo), &numBytesRead); + const std::unordered_map::iterator iter = missionMap.find(RCAM_SABOTAGE_INFANTRY_EQUIPMENT); - SetupInfo(); - } - else - { - Init(); - } + if (iter == missionMap.end()) + return 0; - return TRUE; + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + return evt.isSecondEvent ? -1 : 0; } -BOOLEAN Save(HWFILE file) +INT8 GetEnemyEquipmentStatusModifier(const INT8 initialStatus) { - UINT32 uiNumBytesWritten = 0; + if (!gGameExternalOptions.fRebelCommandEnabled) + return initialStatus; - if (!FileWrite(file, &rebelCommandSaveInfo, sizeof(RebelCommand::SaveInfo), &uiNumBytesWritten)) - return FALSE; - - return TRUE; + const std::unordered_map::iterator iter = missionMap.find(RCAM_SABOTAGE_INFANTRY_EQUIPMENT); + + if (iter == missionMap.end()) + return initialStatus; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + if (evt.isSecondEvent) + { + INT8 modifier = 0; + switch (evt.extraBits) + { + case MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_AUTO_WEAPONS: modifier = gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Auto_Weapons; break; + case MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_COVERT: modifier = gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Covert; break; + case MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_DEMOLITIONS: modifier = gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Demolitions; break; + case MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_GUNSLINGER: modifier = gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Gunslinger; break; + case MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_RANGER: modifier = gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Ranger; break; + case MissionHelpers::SABOTAGE_ENEMY_INFANTRY_EQUIPMENT_MODIFIER_SNIPER: modifier = gRebelCommandSettings.iSabotageInfantryEquipmentModifier_Sniper; break; + } + + return max(1, min(initialStatus - modifier, 100)); + } + + return initialStatus; } -void SetupInfo() +UINT8 GetMerchantCoolnessBonus() { - iCurrentRegionId = 1; + if (!gGameExternalOptions.fRebelCommandEnabled) + return 0; - const auto toFloatVec = [](const std::vector intVec, std::vector& floatVec) - { - floatVec.clear(); - for (const auto val : intVec) - floatVec.push_back(static_cast(val)); - }; + const std::unordered_map::iterator iter = missionMap.find(RCAM_IMPROVE_LOCAL_SHOPS); - // initialise directives - Directive d; - info.directives.clear(); + if (iter == missionMap.end()) + return 0; - d.iCostToImprove = gRebelCommandSettings.iGatherSuppliesCosts; - toFloatVec(gRebelCommandSettings.iGatherSuppliesIncome, d.fValue1); - d.fValue2.clear(); - info.directives.insert(info.directives.begin() + RCD_GATHER_SUPPLIES, d); + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); - d.iCostToImprove = gRebelCommandSettings.iSupportMilitiaCosts; - d.fValue1 = gRebelCommandSettings.fSupportMilitiaDiscounts; - d.fValue2.clear(); - info.directives.insert(info.directives.begin() + RCD_SUPPORT_MILITIA, d); + return evt.isSecondEvent ? 1 : 0; +} - d.iCostToImprove = gRebelCommandSettings.iTrainMilitiaCosts; - d.fValue1 = gRebelCommandSettings.fTrainMilitiaDiscount; - toFloatVec(gRebelCommandSettings.iTrainMilitiaSpeedBonus, d.fValue2); - info.directives.insert(info.directives.begin() + RCD_TRAIN_MILITIA, d); +FLOAT GetStrategicDecisionSpeedModifier() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return 1.f; - d.iCostToImprove = gRebelCommandSettings.iCreatePropagandaCosts; - d.fValue1 = gRebelCommandSettings.fCreatePropagandaModifier; - d.fValue2.clear(); - info.directives.insert(info.directives.begin() + RCD_CREATE_PROPAGANDA, d); + const std::unordered_map::iterator iter = missionMap.find(RCAM_REDUCE_STRATEGIC_DECISION_SPEED); - d.iCostToImprove = gRebelCommandSettings.iEliteMilitiaCosts; - toFloatVec(gRebelCommandSettings.iEliteMilitiaPerDay, d.fValue1); - d.fValue2.clear(); - info.directives.insert(info.directives.begin() + RCD_ELITE_MILITIA, d); + if (iter == missionMap.end()) + return 1.f; - d.iCostToImprove = gRebelCommandSettings.iHvtStrikesCosts; - toFloatVec(gRebelCommandSettings.iHvtStrikesChance, d.fValue1); - d.fValue2.clear(); - info.directives.insert(info.directives.begin() + RCD_HVT_STRIKES, d); + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); - d.iCostToImprove = gRebelCommandSettings.iSpottersCosts; - toFloatVec(gRebelCommandSettings.iSpottersModifier, d.fValue1); - d.fValue2.clear(); - info.directives.insert(info.directives.begin() + RCD_SPOTTERS, d); + FLOAT modifier = 1.f; - d.iCostToImprove = gRebelCommandSettings.iRaidMinesCosts; - d.fValue1 = gRebelCommandSettings.fRaidMinesPercentage; - d.fValue2.clear(); - info.directives.insert(info.directives.begin() + RCD_RAID_MINES, d); + if (evt.isSecondEvent) + { + switch (evt.extraBits) + { + case MissionHelpers::REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_COVERT: modifier = gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Covert; break; + case MissionHelpers::REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_DEPUTY: modifier = gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Deputy; break; + case MissionHelpers::REDUCE_STRATEGIC_DECISION_SPEED_MODIFIER_SNITCH: modifier = gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier_Snitch; break; + default: modifier = gRebelCommandSettings.fReduceStrategicDecisionSpeedModifier; break; + } + } - d.iCostToImprove = gRebelCommandSettings.iCreateTurncoatsCosts; - toFloatVec(gRebelCommandSettings.iCreateTurncoatsPerDay, d.fValue1); - d.fValue2.clear(); - info.directives.insert(info.directives.begin() + RCD_CREATE_TURNCOATS, d); + return modifier; +} - d.iCostToImprove = gRebelCommandSettings.iDraftCosts; - toFloatVec(gRebelCommandSettings.iDraftPerDayModifier, d.fValue1); - toFloatVec(gRebelCommandSettings.iDraftLoyaltyLossPerDay, d.fValue2); - info.directives.insert(info.directives.begin() + RCD_DRAFT, d); +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" + MissionFirstEvent evt1; + MissionSecondEvent evt2; + DeserialiseMissionFirstEvent(eventParam, evt1); + DeserialiseMissionSecondEvent(eventParam, evt2); + CHAR16 msgBoxText[200]; + CHAR16 screenMsgText[200]; + + if (evt1.isFirstEvent) + { + // mission prep is over. see if we can activate the mission + missionMap.erase(static_cast(evt1.missionId)); - // init admin actions - AdminAction aa; - info.adminActions.clear(); + // make sure the merc's still on our team + BOOLEAN foundMerc = FALSE; + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) + { + const SOLDIERTYPE* pSoldier = MercPtrs[i]; - aa.fValue1 = static_cast(gRebelCommandSettings.iSupplyLineMaxLoyaltyIncrease); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_SUPPLY_LINE, aa); + if (pSoldier->ubProfile == evt1.mercProfileId && pSoldier->bActive) + { + foundMerc = TRUE; + break; + } + } - aa.fValue1 = static_cast(gRebelCommandSettings.iRebelRadioDailyLoyaltyGain); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_REBEL_RADIO, aa); + if (evt1.isMissionSuccess) + { + const RebelCommandAgentMissions mission = static_cast(evt1.missionId); + const MERCPROFILESTRUCT merc = gMercProfiles[evt1.mercProfileId]; + + if (!foundMerc) + goto MissionFailed_MercNoLongerOnTeam; + + // 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_DISRUPT_ASD: + 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_SEND_SUPPLIES_TO_TOWN: + case RCAM_SOLDIER_BOUNTIES_KINGPIN: + case RCAM_TRAIN_MILITIA_ANYWHERE: + { + validMission = TRUE; + } + break; - aa.fValue1 = static_cast(gRebelCommandSettings.iSafehouseMinimumSoldiers); - aa.fValue2 = static_cast(gRebelCommandSettings.iSafehouseBonusSoldiers); - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_SAFEHOUSES, aa); + default: + { + ScreenMsg(FONT_MCOLOR_RED, MSG_INTERFACE, L"Unrecognised mission ID: %d", mission); + } + break; + } - aa.fValue1 = static_cast(gRebelCommandSettings.iSupplyDisruptionEnemyStatLoss); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_SUPPLY_DISRUPTION, aa); + if (validMission) + { + const UINT32 activatedMissionParam = SerialiseMissionSecondEvent(evt1.sentGenericRebelAgent, evt1.mercProfileId, mission, extraBits); + AddStrategicEvent(EVENT_REBELCOMMAND, GetWorldTotalMin() + 60 * evt1.missionDurationInHours, activatedMissionParam); - aa.fValue1 = 0.f; - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_SCOUTS, aa); + if (!evt1.sentGenericRebelAgent) + { + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) + { + SOLDIERTYPE* pSoldier = MercPtrs[i]; + if (pSoldier->ubProfile == evt1.mercProfileId) + { + // mission successful! give some experience pts + StatChange(pSoldier, LDRAMT, 20, FROM_SUCCESS); + StatChange(pSoldier, WISDOMAMT, 15, FROM_SUCCESS); + break; + } + } + } - aa.fValue1 = static_cast(gRebelCommandSettings.iDeadDropsDailyIntel); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_DEAD_DROPS, aa); + missionMap.insert(std::make_pair(mission, activatedMissionParam)); + swprintf(msgBoxText, szRebelCommandText[RCT_MISSION_SUCCESS], szRebelCommandAgentMissionsText[evt1.missionId * 2]); + swprintf(screenMsgText, szRebelCommandText[RCT_MISSION_SUCCESS], szRebelCommandAgentMissionsText[evt1.missionId * 2]); + ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, screenMsgText); + } + } + else + { + if (!evt1.sentGenericRebelAgent && foundMerc) + { + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) + { + SOLDIERTYPE* pSoldier = MercPtrs[i]; + if (pSoldier->ubProfile == evt1.mercProfileId) + { + // mission failed! we tried, give some pity exp + StatChange(pSoldier, LDRAMT, 20, FROM_FAILURE); + StatChange(pSoldier, WISDOMAMT, 15, FROM_FAILURE); + break; + } + } + } - aa.fValue1 = static_cast(gRebelCommandSettings.iSmugglersDailySupplies); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_SMUGGLERS, aa); + MissionFailed_MercNoLongerOnTeam: + swprintf(msgBoxText, szRebelCommandText[RCT_MISSION_FAILURE], szRebelCommandAgentMissionsText[evt1.missionId * 2]); + swprintf(screenMsgText, szRebelCommandText[RCT_MISSION_FAILURE], szRebelCommandAgentMissionsText[evt1.missionId * 2]); + ScreenMsg(FONT_MCOLOR_RED, MSG_INTERFACE, screenMsgText); + StopTimeCompression(); + } - aa.fValue1 = static_cast(gRebelCommandSettings.iWarehousesDailyMilitiaGuns); - aa.fValue2 = static_cast(gRebelCommandSettings.iWarehousesDailyMilitiaArmour); - aa.fValue3 = static_cast(gRebelCommandSettings.iWarehousesDailyMilitiaMisc); - info.adminActions.insert(info.adminActions.begin() + RCAA_WAREHOUSES, aa); + if (!evt1.sentGenericRebelAgent && foundMerc) + { + for (UINT8 i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; ++i) + { + SOLDIERTYPE* pSoldier = MercPtrs[i]; + if (pSoldier->ubProfile == evt1.mercProfileId) + { + // merc ready for reassignment + pSoldier->bSectorZ -= REBEL_COMMAND_Z_OFFSET; + AssignmentDone(pSoldier, TRUE, FALSE); + AddCharacterToAnySquad(pSoldier); + break; + } + } - aa.fValue1 = static_cast(gRebelCommandSettings.iTaxesDailyIncome); - aa.fValue2 = static_cast(gRebelCommandSettings.iTaxesDailyLoyaltyLoss); - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_TAXES, aa); + } + } + else if (evt2.isSecondEvent) + { + // mission duration is over. deactivate the mission + missionMap.erase(static_cast(evt2.missionId)); + swprintf(msgBoxText, szRebelCommandText[RCT_MISSION_EXPIRED], szRebelCommandAgentMissionsText[evt2.missionId * 2]); + swprintf(screenMsgText, szRebelCommandText[RCT_MISSION_EXPIRED], szRebelCommandAgentMissionsText[evt2.missionId * 2]); + ScreenMsg(FONT_MCOLOR_RED, MSG_INTERFACE, screenMsgText); + StopTimeCompression(); + } - aa.fValue1 = static_cast(gRebelCommandSettings.iAssistCiviliansDailyVolunteers); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_ASSIST_CIVILIANS, aa); + DoMessageBox(MSG_BOX_BASIC_STYLE, msgBoxText, guiCurrentScreen, MSG_BOX_FLAG_OK, NULL, NULL); +} - aa.fValue1 = static_cast(gRebelCommandSettings.iMercSupportBonus); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_MERC_SUPPORT, aa); +void SendSuppliesToTownMission() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return; - aa.fValue1 = static_cast(gRebelCommandSettings.iMiningPolicyBonus); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_MINING_POLICY, aa); + const std::unordered_map::iterator iter = missionMap.find(RCAM_SEND_SUPPLIES_TO_TOWN); - aa.fValue1 = static_cast(gRebelCommandSettings.uPathfindersSpeedBonus); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_PATHFINDERS, aa); + if (iter == missionMap.end()) + return; - aa.fValue1 = static_cast(gRebelCommandSettings.uHarriersSpeedPenalty); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_HARRIERS, aa); + if (GetWorldHour() % gRebelCommandSettings.iSendSuppliesToTownInterval != 0) + return; - aa.fValue1 = static_cast(gRebelCommandSettings.iFortificationsBonus); - aa.fValue2 = 0.f; - aa.fValue3 = 0.f; - info.adminActions.insert(info.adminActions.begin() + RCAA_FORTIFICATIONS, aa); + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + if (evt.isSecondEvent) + { + IncrementTownLoyalty(evt.extraBits, gRebelCommandSettings.iSendSuppliesToTownLoyaltyGain); + } } -void UpgradeMilitiaStats() +INT16 SendSuppliesToTownDurationBonus(const MERCPROFILESTRUCT* merc) { - const INT32 cost = gRebelCommandSettings.iMilitiaUpgradeCosts[rebelCommandSaveInfo.iMilitiaStatsLevel]; - if (cost > LaptopSaveInfo.iCurrentBalance) - { - DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, szRebelCommandText[RCT_INSUFFICIENT_FUNDS], LAPTOP_SCREEN, MSG_BOX_FLAG_OK, NULL); - return; - } + return merc ? merc->bExpLevel * 8 : 0; +} - CHAR16 text[200]; - swprintf(text, szRebelCommandText[RCT_MILITIA_UPGRADE_STATS_PROMPT], cost); - DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, text, LAPTOP_SCREEN, MSG_BOX_FLAG_YESNO, [](UINT8 exitValue) { - if (exitValue == MSG_BOX_RETURN_YES) - { - const INT32 cost = gRebelCommandSettings.iMilitiaUpgradeCosts[rebelCommandSaveInfo.iMilitiaStatsLevel++]; +BOOLEAN ShowEnemyMovementTargets() +{ + if (!gGameExternalOptions.fRebelCommandEnabled) + return FALSE; - AddTransactionToPlayersBook(REBEL_COMMAND_SPENDING, 0, GetWorldTotalMin(), -cost); + const std::unordered_map::iterator iter = missionMap.find(RCAM_GET_ENEMY_MOVEMENT_TARGETS); - RenderWebsite(); - } - }); + if (iter == missionMap.end()) + return FALSE; + + MissionSecondEvent evt; + DeserialiseMissionSecondEvent(iter->second, evt); + + return evt.isSecondEvent; } void DEBUG_DAY() @@ -2697,18 +5056,6 @@ void DEBUG_DAY() DailyUpdate(); } -void DEBUG_PRINT() -{ - CHAR16 text[500]; - swprintf(text, L"radio_loyalty[%.0f] safehouse_chance/min/bon[%d, %.0f, %.0f] ", info.adminActions[RCAA_REBEL_RADIO].fValue1, gRebelCommandSettings.iSafehouseReinforceChance, info.adminActions[RCAA_SAFEHOUSES].fValue1, info.adminActions[RCAA_SAFEHOUSES].fValue2); - swprintf(text, L"%s supply_dis[%.0f] deaddrops[%.0f] smugglers[%.0f]", text, info.adminActions[RCAA_SUPPLY_DISRUPTION].fValue1, info.adminActions[RCAA_DEAD_DROPS].fValue1, info.adminActions[RCAA_SMUGGLERS].fValue1); - swprintf(text, L"%s warehouse[%.2f/%.2f/%.2f]", text, info.adminActions[RCAA_WAREHOUSES].fValue1, info.adminActions[RCAA_WAREHOUSES].fValue2, info.adminActions[RCAA_WAREHOUSES].fValue3); - swprintf(text, L"%s tax_inc/loy[%.0f/%.0f]", text, info.adminActions[RCAA_TAXES].fValue1, info.adminActions[RCAA_TAXES].fValue2); - swprintf(text, L"%s volunteers[%.0f] support[%.0f]", text, info.adminActions[RCAA_ASSIST_CIVILIANS].fValue1, info.adminActions[RCAA_MERC_SUPPORT].fValue1); - swprintf(text, L"%s minebonus[%0.1f]", text, info.adminActions[RCAA_MINING_POLICY].fValue1); - DoMessageBox(MSG_BOX_MINIEVENT_STYLE, text, guiCurrentScreen, MSG_BOX_FLAG_OK | MSG_BOX_FLAG_BIGGER, NULL, NULL); -} - } template<> void DropDownTemplate::SetRefresh() @@ -2716,7 +5063,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 8b4e24f6b..f8c9bc241 100644 --- a/Strategic/Rebel Command.h +++ b/Strategic/Rebel Command.h @@ -1,14 +1,38 @@ #ifndef REBEL_COMMAND_H #define REBEL_COMMAND_H +#include "CampaignStats.h" #include "mapscreen.h" #include "Soldier Control.h" #include "Types.h" +#define REBEL_COMMAND_Z_OFFSET 9 #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; + + // 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. + // extra bits are mission-specific + + // 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 mission-specific enum RebelCommandDirectives { @@ -49,6 +73,34 @@ enum RebelCommandAdminActions RCAA_NUM_ACTIONS }; +enum RebelCommandAgentMissions +{ + RCAM_NONE = -1, + RCAM_DEEP_DEPLOYMENT = 0, + RCAM_DISRUPT_ASD, // only available if ASD enabled + 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_SEND_SUPPLIES_TO_TOWN, // ignores minimum loyalty requirement + RCAM_SOLDIER_BOUNTIES_KINGPIN, + RCAM_TRAIN_MILITIA_ANYWHERE, + + RCAM_NUM_MISSIONS, + + // ideas/unimplemented + RCAM_BOOST_TOWN_ADMIN_ACTIONS, // store agent location townid in extrabits + RCAM_PROCURE_ITEMS, + RCAM_MILITIA_SKILL_TRAITS, // should override militia skill traits ini option - split into multiple (weapon spec, bodybuilding, athletic, night ops) + RCAM_PURCHASE_SUPPLIES, // increase daily supply income, decrease daily $ income + 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 +149,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 +167,11 @@ 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]; + UINT16 cachedBountyPayout; - INT8 filler[19]; + INT8 filler[15]; } SaveInfo; extern SaveInfo rebelCommandSaveInfo; @@ -122,6 +181,7 @@ void ExitWebsite(); void RenderWebsite(); void HandleWebsite(); +// admin actions void ApplyEnemyPenalties(SOLDIERTYPE* pSoldier); void ApplyMilitiaBonuses(SOLDIERTYPE* pMilitia); UINT8 GetApproximateEnemyLocationResolutionIndex(); @@ -139,17 +199,33 @@ FLOAT GetPathfindersSpeedBonus(UINT8 sector); BOOLEAN NeutraliseRole(const SOLDIERTYPE* pSoldier); void RaidMines(INT32 &playerIncome, INT32 &enemyIncome); BOOLEAN ShowApproximateEnemyLocations(); -void ShowWebsiteAvailableMessage(); + +// agent missions +void ApplySoldierBounty(const SOLDIERTYPE* pSoldier); +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); +BOOLEAN GetASDCanDeployUnits(); +FLOAT GetASDIncomeModifier(); +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 96e0ab4ef..573493b77 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/Strategic Movement.cpp b/Strategic/Strategic Movement.cpp index 67d0e69db..e06685456 100644 --- a/Strategic/Strategic Movement.cpp +++ b/Strategic/Strategic Movement.cpp @@ -5475,7 +5475,7 @@ BOOLEAN TestForBloodcatAmbush( GROUP *pGroup ) { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && !(MercPtrs[ i ]->flags.uiStatusFlags & SOLDIER_VEHICLE) ) { - if ( MercPtrs[ i ]->sSectorX == pGroup->ubSectorX && MercPtrs[ i ]->sSectorY == pGroup->ubSectorY && MercPtrs[ i ]->bAssignment != ASSIGNMENT_POW && MercPtrs[ i ]->bAssignment != ASSIGNMENT_MINIEVENT && MercPtrs[ i ]->stats.bLife >= OKLIFE ) + if ( MercPtrs[ i ]->sSectorX == pGroup->ubSectorX && MercPtrs[ i ]->sSectorY == pGroup->ubSectorY && MercPtrs[ i ]->bAssignment != ASSIGNMENT_POW && MercPtrs[ i ]->bAssignment != ASSIGNMENT_MINIEVENT && MercPtrs[i]->bAssignment != ASSIGNMENT_REBELCOMMAND && MercPtrs[ i ]->stats.bLife >= OKLIFE ) { if( HAS_SKILL_TRAIT( MercPtrs[ i ], SCOUTING_NT ) && MercPtrs[ i ]->ubProfile != NO_PROFILE ) { @@ -5880,6 +5880,7 @@ BOOLEAN GroupHasInTransitDeadOrPOWMercs( GROUP *pGroup ) if( ( pPlayer->pSoldier->bAssignment == IN_TRANSIT ) || ( pPlayer->pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pPlayer->pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || + ( pPlayer->pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) || SPY_LOCATION( pPlayer->pSoldier->bAssignment ) || ( pPlayer->pSoldier->bAssignment == ASSIGNMENT_DEAD ) ) { diff --git a/Strategic/Town Militia.cpp b/Strategic/Town Militia.cpp index 5ec21b592..98ce1858b 100644 --- a/Strategic/Town Militia.cpp +++ b/Strategic/Town Militia.cpp @@ -279,7 +279,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 +1293,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/Strategic/mapscreen.cpp b/Strategic/mapscreen.cpp index e9798e15b..325f557be 100644 --- a/Strategic/mapscreen.cpp +++ b/Strategic/mapscreen.cpp @@ -2411,7 +2411,8 @@ void DrawCharBars( void ) if( ( pSoldier->stats.bLife == 0 ) || ( pSoldier->bAssignment == ASSIGNMENT_DEAD ) || ( pSoldier->bAssignment == ASSIGNMENT_POW ) || - ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) ) + ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || + ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) ) { return; } @@ -2829,7 +2830,7 @@ void DrawCharHealth( INT16 sCharNum ) pSoldier = &Menptr[gCharactersList[sCharNum].usSolID]; - if( pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) + if( pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT && pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND ) { // find starting X coordinate by centering all 3 substrings together, then print them separately (different colors)! swprintf( sString, L"%d/%d", pSoldier->stats.bLife, pSoldier->stats.bLifeMax ); @@ -11328,7 +11329,7 @@ void TeamListInfoRegionBtnCallBack(MOUSE_REGION *pRegion, INT32 iReason ) fPlotForMilitia = FALSE; // if not dead or POW, select his sector - if( ( pSoldier->stats.bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ))//&& !SPY_LOCATION( pSoldier->bAssignment ) ) + if( ( pSoldier->stats.bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && ( pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND ) )//&& !SPY_LOCATION( pSoldier->bAssignment ) ) { ChangeSelectedMapSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); } @@ -11384,7 +11385,7 @@ void TeamListInfoRegionBtnCallBack(MOUSE_REGION *pRegion, INT32 iReason ) fPlotForMilitia = FALSE; // if not dead or POW, select his sector - if( ( pSoldier->stats.bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && !SPY_LOCATION( pSoldier->bAssignment ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) ) + if( ( pSoldier->stats.bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && !SPY_LOCATION( pSoldier->bAssignment ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) ) { ChangeSelectedMapSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); } @@ -11567,7 +11568,7 @@ void TeamListAssignmentRegionBtnCallBack(MOUSE_REGION *pRegion, INT32 iReason ) fShownAssignmentMenu = FALSE; // if not dead or POW, select his sector - if( ( pSoldier->stats.bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) ) + if( ( pSoldier->stats.bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) ) { ChangeSelectedMapSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ ); } @@ -15115,6 +15116,7 @@ BOOLEAN MapCharacterHasAccessibleInventory( INT8 bCharNumber ) if( ( pSoldier->bAssignment == IN_TRANSIT ) || ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || + ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) || // Kaiden: Vehicle Inventory change - Commented the following line // ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || // And added this instead: @@ -15303,7 +15305,7 @@ BOOLEAN CanChangeSleepStatusForSoldier( SOLDIERTYPE *pSoldier ) // if a vehicle, robot, in transit, or a POW if( ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) || AM_A_ROBOT( pSoldier ) || - ( pSoldier->bAssignment == IN_TRANSIT ) || ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) ) + ( pSoldier->bAssignment == IN_TRANSIT ) || ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) ) { // can't change the sleep status of such mercs return ( FALSE ); @@ -15856,7 +15858,7 @@ INT16 CalcLocationValueForChar( INT32 iCounter ) pSoldier = MercPtrs[ gCharactersList[ iCounter ].usSolID ]; // don't reveal location of POWs! - if( pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) + if( pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT && pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND ) { sLocValue = SECTOR( pSoldier->sSectorX, pSoldier->sSectorY ); // underground: add 1000 per sublevel @@ -15950,6 +15952,7 @@ BOOLEAN AnyMovableCharsInOrBetweenThisSector( INT16 sSectorX, INT16 sSectorY, IN SPY_LOCATION( pSoldier->bAssignment ) || ( pSoldier->bAssignment == ASSIGNMENT_DEAD ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || + ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) || ( pSoldier->stats.bLife == 0 ) ) { continue; @@ -16554,6 +16557,11 @@ void GetMapscreenMercLocationString( SOLDIERTYPE *pSoldier, CHAR16 sString[] ) // mini event - unknown location, use the same string as POWs swprintf( sString, L"%s", pPOWStrings[ 1 ] ); } + else if (pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + { + // on a rebel command mission + swprintf( sString, L"%s%s*", pMapVertIndex[pSoldier->sSectorY], pMapHortIndex[pSoldier->sSectorX] ); + } else if ( SPY_LOCATION( pSoldier->bAssignment ) ) { swprintf( pTempString, L"%s%s%s", @@ -16594,6 +16602,7 @@ void GetMapscreenMercDestinationString( SOLDIERTYPE *pSoldier, CHAR16 sString[] if( ( pSoldier->bAssignment == ASSIGNMENT_DEAD ) || ( pSoldier->bAssignment == ASSIGNMENT_POW ) || ( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) || + ( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) || SPY_LOCATION( pSoldier->bAssignment ) || ( pSoldier->stats.bLife == 0 ) ) { diff --git a/Strategic/strategicmap.cpp b/Strategic/strategicmap.cpp index 03a881c76..dda550d93 100644 --- a/Strategic/strategicmap.cpp +++ b/Strategic/strategicmap.cpp @@ -5229,7 +5229,7 @@ BOOLEAN CanGoToTacticalInSector( INT16 sX, INT16 sY, UINT8 ubZ ) { // ARM: now allows loading of sector with all mercs below OKLIFE as long as they're alive if( ( pSoldier->bActive && pSoldier->stats.bLife ) && !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && - ( pSoldier->bAssignment != IN_TRANSIT ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && + ( pSoldier->bAssignment != IN_TRANSIT ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) && ( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) && ( pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND ) && ( pSoldier->bAssignment != ASSIGNMENT_DEAD ) && !SoldierAboardAirborneHeli( pSoldier ) ) @@ -6402,7 +6402,7 @@ void HandleSlayDailyEvent( void ) } // valid soldier? - if ( (pSoldier->bActive == FALSE) || (pSoldier->stats.bLife == 0) || (pSoldier->bAssignment == IN_TRANSIT) || (pSoldier->bAssignment == ASSIGNMENT_POW) || (pSoldier->bAssignment == ASSIGNMENT_MINIEVENT) ) + if ( (pSoldier->bActive == FALSE) || (pSoldier->stats.bLife == 0) || (pSoldier->bAssignment == IN_TRANSIT) || (pSoldier->bAssignment == ASSIGNMENT_POW) || (pSoldier->bAssignment == ASSIGNMENT_MINIEVENT) || (pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) ) { // no return; diff --git a/Tactical/ArmsDealerInvInit.cpp b/Tactical/ArmsDealerInvInit.cpp index 9e018066a..df0c9422e 100644 --- a/Tactical/ArmsDealerInvInit.cpp +++ b/Tactical/ArmsDealerInvInit.cpp @@ -15,6 +15,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 +1009,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/Dialogue Control.cpp b/Tactical/Dialogue Control.cpp index 2d2efb528..1c95d70ec 100644 --- a/Tactical/Dialogue Control.cpp +++ b/Tactical/Dialogue Control.cpp @@ -1515,6 +1515,9 @@ BOOLEAN DelayedTacticalCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteN if( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) return( FALSE ); + if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + return( FALSE ); + return( CharacterDialogue( pSoldier->ubProfile, usQuoteNum, pSoldier->iFaceIndex, DIALOGUE_TACTICAL_UI, TRUE, TRUE ) ); } @@ -1574,6 +1577,9 @@ BOOLEAN TacticalCharacterDialogueWithSpecialEventEx( SOLDIERTYPE *pSoldier, UINT if( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) return( FALSE ); + + if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + return( FALSE ); } return( CharacterDialogueWithSpecialEventEx( pSoldier->ubProfile, usQuoteNum, pSoldier->iFaceIndex, DIALOGUE_TACTICAL_UI, TRUE, FALSE, uiFlag, uiData1, uiData2, uiData3 ) ); @@ -1625,6 +1631,9 @@ BOOLEAN TacticalCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteNum ) if( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) return( FALSE ); + if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + return( FALSE ); + // OK, let's check if this is the exact one we just played, if so, skip. if ( pSoldier->ubProfile == gTacticalStatus.ubLastQuoteProfileNUm && usQuoteNum == gTacticalStatus.ubLastQuoteSaid ) @@ -1717,6 +1726,9 @@ BOOLEAN SnitchTacticalCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteNu if( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) return( FALSE ); + if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + return( FALSE ); + // OK, let's check if this is the exact one we just played, if so, skip. //if ( pSoldier->ubProfile == gTacticalStatus.ubLastQuoteProfileNUm && // usQuoteNum == gTacticalStatus.ubLastQuoteSaid ) @@ -1774,6 +1786,9 @@ BOOLEAN AdditionalTacticalCharacterDialogue_CallsLua( SOLDIERTYPE *pSoldier, UIN if( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) return( FALSE ); + if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + return( FALSE ); + if ( AM_AN_EPC( pSoldier ) && !( gMercProfiles[pSoldier->ubProfile].ubMiscFlags & PROFILE_MISC_FLAG_FORCENPCQUOTE ) ) return( FALSE ); @@ -1802,7 +1817,7 @@ void AdditionalTacticalCharacterDialogue_AllInSector(INT16 aSectorX, INT16 aSect if ( pSoldier->stats.bLife >= OKLIFE && pSoldier->bActive && pSoldier->ubProfile != ausIgnoreProfile && pSoldier->sSectorX == aSectorX && pSoldier->sSectorY == aSectorY && pSoldier->bSectorZ == aSectorZ && - pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT && + pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT && pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND && (aAroundGridno == NOWHERE || PythSpacesAway( pSoldier->sGridNo, aAroundGridno ) <= aRadius ) && !pSoldier->flags.fBetweenSectors ) { @@ -2105,6 +2120,9 @@ BOOLEAN ExecuteCharacterDialogue( UINT8 ubCharacterNum, UINT16 usQuoteNum, INT32 if( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) return( FALSE ); + if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + return( FALSE ); + // sleeping guys don't talk.. go to standby to talk if( pSoldier->flags.fMercAsleep == TRUE ) { diff --git a/Tactical/DisplayCover.cpp b/Tactical/DisplayCover.cpp index 82a42c0de..34b67dd85 100644 --- a/Tactical/DisplayCover.cpp +++ b/Tactical/DisplayCover.cpp @@ -34,6 +34,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 +760,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 +768,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 +787,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 +796,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/DynamicDialogue.cpp b/Tactical/DynamicDialogue.cpp index 68b69b1d1..ac6e5f951 100644 --- a/Tactical/DynamicDialogue.cpp +++ b/Tactical/DynamicDialogue.cpp @@ -960,6 +960,9 @@ BOOLEAN DynamicOpinionTacticalCharacterDialogue( DynamicOpinionSpeechEvent& aEve if( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) return( FALSE ); + if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + return( FALSE ); + CHAR16 gzQuoteStr[500]; // remove old box, in case that still exists @@ -2020,7 +2023,7 @@ UINT8 GetFittingInterjectorProfile( UINT8 usEvent, UINT8 usProfileVictim, UINT8 for ( pTeamSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, pTeamSoldier++ ) { // only people that are here - if ( !pTeamSoldier->bActive || pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD || pTeamSoldier->bAssignment == ASSIGNMENT_POW || pTeamSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) + if ( !pTeamSoldier->bActive || pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD || pTeamSoldier->bAssignment == ASSIGNMENT_POW || pTeamSoldier->bAssignment == ASSIGNMENT_MINIEVENT || pTeamSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) continue; // if fSameSector is TRUE then the teammate must be in the same sector diff --git a/Tactical/Food.cpp b/Tactical/Food.cpp index 071721918..2d1e6399d 100644 --- a/Tactical/Food.cpp +++ b/Tactical/Food.cpp @@ -606,7 +606,7 @@ void HourlyFoodAutoDigestion( SOLDIERTYPE *pSoldier ) AddFoodpoints(pSoldier->bFoodLevel, powfoodadd); } // while on a minievent, assume that we can feed ourselves.. somehow - else if (pSoldier->bAssignment == ASSIGNMENT_MINIEVENT) + else if (pSoldier->bAssignment == ASSIGNMENT_MINIEVENT || pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) { const INT16 water = gGameExternalOptions.usFoodDigestionHourlyBaseDrink * gGameExternalOptions.sFoodDigestionAssignment; const INT16 foodadd = water * gGameExternalOptions.usFoodDigestionHourlyBaseFood / max(1, gGameExternalOptions.usFoodDigestionHourlyBaseDrink); diff --git a/Tactical/Inventory Choosing.cpp b/Tactical/Inventory Choosing.cpp index f552825e4..ff88ef95a 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 Ani.cpp b/Tactical/Soldier Ani.cpp index 431f08b9e..a615b8638 100644 --- a/Tactical/Soldier Ani.cpp +++ b/Tactical/Soldier Ani.cpp @@ -66,6 +66,7 @@ #include "MilitiaIndividual.h" // added by Flugente #include "Town Militia.h" // added by Flugente #include "PreBattle Interface.h" // added by Flugente +#include "Rebel Command.h" #endif // anv: for enemy taunts @@ -4198,6 +4199,9 @@ BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse ) } } + // rftr: soldier bounty payout + RebelCommand::ApplySoldierBounty(pSoldier); + // Flugente: campaign stats gCurrentIncident.AddStat( pSoldier, CAMPAIGNHISTORY_TYPE_KILL ); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 64a5a4f4c..cde0fa8d6 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -6311,7 +6311,7 @@ void SOLDIERTYPE::EVENT_SoldierGotHit( UINT16 usWeaponIndex, INT16 sDamage, INT1 // If anything other than on a squad or guard, make them guard.... if ( this->bTeam == gbPlayerNum ) { - if ( this->bAssignment >= ON_DUTY && this->bAssignment != ASSIGNMENT_POW && this->bAssignment != ASSIGNMENT_MINIEVENT ) + if ( this->bAssignment >= ON_DUTY && this->bAssignment != ASSIGNMENT_POW && this->bAssignment != ASSIGNMENT_MINIEVENT && this->bAssignment != ASSIGNMENT_REBELCOMMAND) { if ( this->flags.fMercAsleep ) { diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 192b82a08..fc5c6839c 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/Tactical/Squads.cpp b/Tactical/Squads.cpp index dc6176c9c..91ef17775 100644 --- a/Tactical/Squads.cpp +++ b/Tactical/Squads.cpp @@ -1389,6 +1389,11 @@ BOOLEAN IsSquadInSector( SOLDIERTYPE *pSoldier, UINT8 ubSquad ) return( FALSE ); } + if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) + { + return( FALSE ); + } + if( SquadIsEmpty( ubSquad ) == TRUE ) { return( TRUE ); diff --git a/Tactical/Strategic Exit GUI.cpp b/Tactical/Strategic Exit GUI.cpp index b56531d07..00274713e 100644 --- a/Tactical/Strategic Exit GUI.cpp +++ b/Tactical/Strategic Exit GUI.cpp @@ -238,7 +238,7 @@ BOOLEAN InternalInitSectorExitMenu( UINT8 ubDirection, INT32 sAdditionalData )// pSoldier->stats.bLife >= OKLIFE && ( pSoldier->bAssignment != MercPtrs[ gusSelectedSoldier ]->bAssignment || ( pSoldier->bAssignment == VEHICLE && pSoldier->iVehicleId != MercPtrs[ gusSelectedSoldier ]->iVehicleId ) ) && - pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_DEAD && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT + pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_DEAD && pSoldier->bAssignment != ASSIGNMENT_MINIEVENT && pSoldier->bAssignment != ASSIGNMENT_REBELCOMMAND && !(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE) ) { //KM: We need to determine if there are more than one squad (meaning other concious mercs in a different squad or assignment) // These conditions were done to the best of my knowledge, so if there are other situations that require modification, diff --git a/Tactical/opplist.cpp b/Tactical/opplist.cpp index 8f6db0d01..a4fb07cba 100644 --- a/Tactical/opplist.cpp +++ b/Tactical/opplist.cpp @@ -6001,7 +6001,7 @@ void ProcessNoise(UINT8 ubNoiseMaker, INT32 sGridNo, INT8 bLevel, UINT8 ubTerrTy continue; // skip } - if ( bTeam == gbPlayerNum && (pSoldier->bAssignment == ASSIGNMENT_POW || pSoldier->bAssignment == ASSIGNMENT_MINIEVENT) ) + if ( bTeam == gbPlayerNum && (pSoldier->bAssignment == ASSIGNMENT_POW || pSoldier->bAssignment == ASSIGNMENT_MINIEVENT || pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) ) { // POWs should not be processed for noise continue; diff --git a/TileEngine/Map Edgepoints.cpp b/TileEngine/Map Edgepoints.cpp index f2ef426b6..afd3c2512 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 a14b27dd4..787eba223 100644 --- a/TileEngine/Tactical Placement GUI.cpp +++ b/TileEngine/Tactical Placement GUI.cpp @@ -41,6 +41,7 @@ #include "renderworld.h"//dnl ch45 051009 #include "merc entering.h" #include "CampaignStats.h" // added by Flugente +#include "Rebel Command.h" typedef struct MERCPLACEMENT { @@ -338,6 +339,7 @@ void InitTacticalPlacementGUI() !( MercPtrs[ i ]->flags.uiStatusFlags & ( SOLDIER_VEHICLE ) ) && // ATE Ignore vehicles MercPtrs[ i ]->bAssignment != ASSIGNMENT_POW && MercPtrs[ i ]->bAssignment != ASSIGNMENT_MINIEVENT && + MercPtrs[ i ]->bAssignment != ASSIGNMENT_REBELCOMMAND && !( MercPtrs[i]->usSoldierFlagMask2 & SOLDIER_CONCEALINSERTION ) && MercPtrs[ i ]->bAssignment != IN_TRANSIT ) { @@ -355,6 +357,7 @@ void InitTacticalPlacementGUI() CurrentBattleSectorIs( MercPtrs[i]->sSectorX, MercPtrs[i]->sSectorY, MercPtrs[i]->bSectorZ ) && MercPtrs[ i ]->bAssignment != ASSIGNMENT_POW && MercPtrs[ i ]->bAssignment != ASSIGNMENT_MINIEVENT && + MercPtrs[ i ]->bAssignment != ASSIGNMENT_REBELCOMMAND && !( MercPtrs[i]->usSoldierFlagMask2 & SOLDIER_CONCEALINSERTION ) && MercPtrs[ i ]->bAssignment != IN_TRANSIT && !( MercPtrs[ i ]->flags.uiStatusFlags & ( SOLDIER_VEHICLE ) ) ) // ATE Ignore vehicles @@ -910,6 +913,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 +921,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 +929,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 +937,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 +1241,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 b5fcd1e9d..2ed7ca035 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 0a730a333..16d4f03e9 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -2480,7 +2480,8 @@ STR16 pAssignmentStrings[] = L"掩埋尸体", //L"Burial", L"管理", //L"Admin", L"探索", //L"Explore" - L"事件" //L"Event" rftr: merc is on a mini event + L"事件", //L"Event" rftr: merc is on a mini event + L"Mission", // rftr: rebel command }; @@ -4662,6 +4663,7 @@ STR16 pTransactionText[] = L"微型事件", //L"Mini event", rftr: mini events L"从反抗军司令部转移资金", //L"Funds transferred from rebel command", rftr: rebel command L"资金转移到反抗军司令部", //L"Funds transferred to rebel command", rftr: rebel command + L"Bounty payout", // rftr: rebel command soldier bounties }; STR16 pTransactionAlternateText[] = @@ -11864,12 +11866,16 @@ STR16 gLbeStatsDesc[14] = STR16 szRebelCommandText[] = { - L"Arulco反抗军司令部 - 国家总览", //L"Arulco Rebel Command - National Overview", - L"Arulco反抗军司令部 - 地区总览", //L"Arulco Rebel Command - Regional Overview", - L"点击地区总览", //L"Switch to Regional Overview", - L"点击国家总览", //L"Switch to National Overview", + L"国家总览", //L"National Overview", + L"地区总览", //L"Regional Overview", + L"Mission Overview", + L"Select View:", + L"地区总览 (2)", //L"Regional (2)", + L"国家总览 (1)", //L"National (1)", + L"Mission (3)", L"物资:", //L"Supplies:", L"后勤物资", //L"Incoming Supplies", + L"Intel:", L" /天", //L"/day", L"当前项目", //L"Current Directive", L"升级项目($%d)", //L"Improve Directive ($%d)", @@ -11927,17 +11933,68 @@ STR16 szRebelCommandText[] = L"<", //L"<", L">", //L">", L"更改此指令操作将花费$%d并重置。确认支出?", //L"Changing this Admin Action will cost $%d and reset its tier. Confirm expenditure?", + L"Insufficient supplies! Admin Actions have been DISABLED.", + L"New missions will be available every %d hours.", + L"New missions are available at the A.R.C. website.", + L"Mission preparations in progress.", + L"Mission duration: %d days", + L"Chance of success: %d%s", + L"[REDACTED]", + L"Name: %s", + L"Location: %s", + L"Assignment: %s", + L"Contract: %d days", + L"Contract: %d hours", + L"Contract: ---", + L"Agent bonus:", + L"Chance of success +%d%s (%s)", + L"Deployment range +%d (%s)", + L"ASD Income -%2.0f%s (%s)", + L"Steal fuel; send to %s (%s)", + L"Destroy reserve units (%s)", + L"Time +%2.0f%s (%s)", + L"Vision -%2.0f%s (%s)", + L"Gear quality -%d (%s)", + L"Overall stats -%d (%s)", + L"Max trainers: %d (%s)", + L"Payout +%2.0f%s (%s)", + L"Payout limit increased to $%d (%s)", + L"Bonus for officers (%s)", + L"Bonus for vehicles (%s)", + L"Duration +%d hours (%s)", + L"Agent not in town", + L"Town loyalty too low", + L"Agent unavailable", + L"Agent contract expiring", + L"Can't use rebel agent", + L"Battle in progress", + L"Prepare Mission (%d supplies)", + L"View active mission effects", + L"View available mission list", + L"You are able to prepare one of the two missions presented. Once an agent is dispatched, they will be unavailable for approximately %d hours before becoming available again. A popup will notify you when preparations are complete. If preparations succeed, the mission's effects become active.", + L"A rebel agent can be sent to prepare a mission, but your mercenaries will be far more effective. Their experience level and skills can provide additional bonuses to missions.", + L"The cost of preparing a mission increases based on the number other missions either active or being prepared.", + L"New missions will be available on Day %d at 00:00.", + L"Active missions:", + L"%s - Preparing - Ready on Day %d, %02d:%02d", + L"%s - Active - Expires on Day %d, %02d:%02d", + L"[%s (%d supplies)]", + L"%s Send a rebel agent to prepare this mission?", + L"%s Send %s to prepare this mission? He will return in approximately %d hours.", + L"%s Send %s to prepare this mission? She will return in approximately %d hours.", + L"Mission \"%s\" is now in effect.", + L"Preparations for mission \"%s\" failed.", + L"Mission \"%s\" has expired and is no longer in effect.", }; STR16 szRebelCommandHelpText[] = { L"|物|资\n \n食物、水、医疗用品、武器以及任何\n反抗军认为有用的物资。反抗军会自动收集。", //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"|后|勤|物|资\n \n反抗军每天都会自动收集物资。当你\n占领更多的城镇时,他们每天能够\n找到的物资补给量将会增加。", //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"|后|勤|物|资\n \n反抗军每天都会自动收集物资。当你\n占领更多的城镇时,他们每天能够\n找到的物资补给量将会增加。\n \n+%d (Base income)", //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"|当|前|项|目\n \n你可以选择反抗军优先进行的战略目标。\n当你选定好战略目标时,新的项目指令将生效。", //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"|指|挥|部\n \n指挥部一旦部署,就会负责处理\n该区域内的日常事务。包括支持当地人,制造\n反抗宣传,制定地区政策等等。", //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"|忠|诚|度\n \n许多行政命令的有效性取决于\n该地区的忠诚度,提高忠诚度\n能得到最大利益化。", //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"|最|高|忠|诚|度\n \n你需要说服当地人完全信任你。这可以\n通过为他们建立物资供应来实现,表明\n你打算改善他们的生活质量。", //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"|援|助|物|资\n \n将物资送到此处的反抗军手里,并允许\n他们根据需要使用。这将少量增加\n该地区的忠诚度,但是会略微增加制定\n该地区政策的成本。", //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"该管理行动只会作用到城镇区域。", //L"This Admin Action applies its bonus to town sectors only.", TODO.Translate L"该管理行动会作用到城镇区域。\n和直接相邻的区域。", //L"This Admin Action applies its bonus to town sectors, and\nsectors immediately adjacent to them.", L"该管理行动会作用到城镇区域。\n1级覆盖周边1个区域。\n2级覆盖周边2个区域。", //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 +12023,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", @@ -12030,6 +12087,32 @@ STR16 szRebelCommandDirectivesText[] = L"升级此项将会增加每天志愿者人数。", //L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + 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"Disrupt ASD", + L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", + 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"Send Supplies", + L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", + L"Soldier Bounties (Kingpin)", + L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", + 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"已经安装在机器人身上的武器不能替换。", //L"The robot's installed weapon cannot be changed.", diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index f842d6618..1afaa83e1 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -2479,7 +2479,8 @@ STR16 pAssignmentStrings[] = L"Burial", L"Admin", // TODO.Translate L"Explore", // TODO.Translate - L"Event"// rftr: merc is on a mini event // TODO: translate + L"Event",// rftr: merc is on a mini event // TODO: translate + L"Mission", // rftr: rebel command }; @@ -4665,6 +4666,7 @@ STR16 pTransactionText[] = L"Mini event", // rftr: mini events // TODO: translate L"Funds transferred from rebel command", // rftr: rebel command L"Funds transferred to rebel command", // rftr: rebel command + L"Bounty payout", // rftr: rebel command soldier bounties }; STR16 pTransactionAlternateText[] = @@ -11874,12 +11876,16 @@ STR16 gLbeStatsDesc[14] = STR16 szRebelCommandText[] = // TODO.Translate { - 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)", @@ -11937,17 +11943,68 @@ STR16 szRebelCommandText[] = // TODO.Translate L"<", L">", L"Changing this Admin Action will cost $%d and reset its tier. Confirm expenditure?", + L"Insufficient supplies! Admin Actions have been DISABLED.", + L"New missions will be available every %d hours.", + L"New missions are available at the A.R.C. website.", + L"Mission preparations in progress.", + L"Mission duration: %d days", + L"Chance of success: %d%s", + L"[REDACTED]", + L"Name: %s", + L"Location: %s", + L"Assignment: %s", + L"Contract: %d days", + L"Contract: %d hours", + L"Contract: ---", + L"Agent bonus:", + L"Chance of success +%d%s (%s)", + L"Deployment range +%d (%s)", + L"ASD Income -%2.0f%s (%s)", + L"Steal fuel; send to %s (%s)", + L"Destroy reserve units (%s)", + L"Time +%2.0f%s (%s)", + L"Vision -%2.0f%s (%s)", + L"Gear quality -%d (%s)", + L"Overall stats -%d (%s)", + L"Max trainers: %d (%s)", + L"Payout +%2.0f%s (%s)", + L"Payout limit increased to $%d (%s)", + L"Bonus for officers (%s)", + L"Bonus for vehicles (%s)", + L"Duration +%d hours (%s)", + L"Agent not in town", + L"Town loyalty too low", + L"Agent unavailable", + L"Agent contract expiring", + L"Can't use rebel agent", + L"Battle in progress", + L"Prepare Mission (%d supplies)", + L"View active mission effects", + L"View available mission list", + L"You are able to prepare one of the two missions presented. Once an agent is dispatched, they will be unavailable for approximately %d hours before becoming available again. A popup will notify you when preparations are complete. If preparations succeed, the mission's effects become active.", + L"A rebel agent can be sent to prepare a mission, but your mercenaries will be far more effective. Their experience level and skills can provide additional bonuses to missions.", + L"The cost of preparing a mission increases based on the number other missions either active or being prepared.", + L"New missions will be available on Day %d at 00:00.", + L"Active missions:", + L"%s - Preparing - Ready on Day %d, %02d:%02d", + L"%s - Active - Expires on Day %d, %02d:%02d", + L"[%s (%d supplies)]", + L"%s Send a rebel agent to prepare this mission?", + L"%s Send %s to prepare this mission? He will return in approximately %d hours.", + L"%s Send %s to prepare this mission? She will return in approximately %d hours.", + L"Mission \"%s\" is now in effect.", + L"Preparations for mission \"%s\" failed.", + L"Mission \"%s\" has expired and is no longer in effect.", }; STR16 szRebelCommandHelpText[] = // TODO.Translate { 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.", //TODO.Translate 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.", @@ -11976,7 +12033,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", @@ -12040,6 +12097,32 @@ STR16 szRebelCommandDirectivesText[] = // TODO.Translate L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + 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"Disrupt ASD", + L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", + 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"Send Supplies", + L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", + L"Soldier Bounties (Kingpin)", + L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", + 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[] = // TODO: Translate { L"The robot's installed weapon cannot be changed.", diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 9f37ce44b..7ff18a08d 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -2480,7 +2480,8 @@ STR16 pAssignmentStrings[] = L"Burial", L"Admin", L"Explore", - L"Event"// rftr: merc is on a mini event + L"Event", // rftr: merc is on a mini event + L"Mission", // rftr: rebel command }; @@ -4662,6 +4663,7 @@ STR16 pTransactionText[] = L"Mini event", // rftr: mini events L"Funds transferred from rebel command", // rftr: rebel command L"Funds transferred to rebel command", // rftr: rebel command + L"Bounty payout", // rftr: rebel command soldier bounties }; STR16 pTransactionAlternateText[] = @@ -11864,12 +11866,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)", @@ -11927,17 +11933,68 @@ STR16 szRebelCommandText[] = L"<", L">", L"Changing this Admin Action will cost $%d and reset its tier. Confirm expenditure?", + L"Insufficient supplies! Admin Actions have been DISABLED.", + L"New missions will be available every %d hours.", + L"New missions are available at the A.R.C. website.", + L"Mission preparations in progress.", + L"Mission duration: %d days", + L"Chance of success: %d%s", + L"[REDACTED]", + L"Name: %s", + L"Location: %s", + L"Assignment: %s", + L"Contract: %d days", + L"Contract: %d hours", + L"Contract: ---", + L"Agent bonus:", + L"Chance of success +%d%s (%s)", + L"Deployment range +%d (%s)", + L"ASD Income -%2.0f%s (%s)", + L"Steal fuel; send to %s (%s)", + L"Destroy reserve units (%s)", + L"Time +%2.0f%s (%s)", + L"Vision -%2.0f%s (%s)", + L"Gear quality -%d (%s)", + L"Overall stats -%d (%s)", + L"Max trainers: %d (%s)", + L"Payout +%2.0f%s (%s)", + L"Payout limit increased to $%d (%s)", + L"Bonus for officers (%s)", + L"Bonus for vehicles (%s)", + L"Duration +%d hours (%s)", + L"Agent not in town", + L"Town loyalty too low", + L"Agent unavailable", + L"Agent contract expiring", + L"Can't use rebel agent", + L"Battle in progress", + L"Prepare Mission (%d supplies)", + L"View active mission effects", + L"View available mission list", + L"You are able to prepare one of the two missions presented. Once an agent is dispatched, they will be unavailable for approximately %d hours before becoming available again. A popup will notify you when preparations are complete. If preparations succeed, the mission's effects become active.", + L"A rebel agent can be sent to prepare a mission, but your mercenaries will be far more effective. Their experience level and skills can provide additional bonuses to missions.", + L"The cost of preparing a mission increases based on the number other missions either active or being prepared.", + L"New missions will be available on Day %d at 00:00.", + L"Active missions:", + L"%s - Preparing - Ready on Day %d, %02d:%02d", + L"%s - Active - Expires on Day %d, %02d:%02d", + L"[%s (%d supplies)]", + L"%s Send a rebel agent to prepare this mission?", + L"%s Send %s to prepare this mission? He will return in approximately %d hours.", + L"%s Send %s to prepare this mission? She will return in approximately %d hours.", + L"Mission \"%s\" is now in effect.", + L"Preparations for mission \"%s\" failed.", + L"Mission \"%s\" has expired and is no longer in effect.", }; 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 +12023,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 +12087,32 @@ STR16 szRebelCommandDirectivesText[] = L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + 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"Disrupt ASD", + L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", + 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"Send Supplies", + L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", + L"Soldier Bounties (Kingpin)", + L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", + 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 b645769e7..1f264bd3f 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -2488,7 +2488,8 @@ STR16 pAssignmentStrings[] = L"Burial", L"Admin", // TODO.Translate L"Explore", // TODO.Translate - L"Event"// rftr: merc is on a mini event // TODO: translate + L"Event",// rftr: merc is on a mini event // TODO: translate + L"Mission", // rftr: rebel command }; @@ -4669,6 +4670,7 @@ STR16 pTransactionText[] = L"Mini event", // rftr: mini events // TODO: translate L"Funds transferred from rebel command", // rftr: rebel command L"Funds transferred to rebel command", // rftr: rebel command + L"Bounty payout", // rftr: rebel command soldier bounties }; STR16 pTransactionAlternateText[] = @@ -11856,12 +11858,16 @@ STR16 gLbeStatsDesc[14] = STR16 szRebelCommandText[] = // TODO.Translate { - 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)", @@ -11919,17 +11925,68 @@ STR16 szRebelCommandText[] = // TODO.Translate L"<", L">", L"Changing this Admin Action will cost $%d and reset its tier. Confirm expenditure?", + L"Insufficient supplies! Admin Actions have been DISABLED.", + L"New missions will be available every %d hours.", + L"New missions are available at the A.R.C. website.", + L"Mission preparations in progress.", + L"Mission duration: %d days", + L"Chance of success: %d%s", + L"[REDACTED]", + L"Name: %s", + L"Location: %s", + L"Assignment: %s", + L"Contract: %d days", + L"Contract: %d hours", + L"Contract: ---", + L"Agent bonus:", + L"Chance of success +%d%s (%s)", + L"Deployment range +%d (%s)", + L"ASD Income -%2.0f%s (%s)", + L"Steal fuel; send to %s (%s)", + L"Destroy reserve units (%s)", + L"Time +%2.0f%s (%s)", + L"Vision -%2.0f%s (%s)", + L"Gear quality -%d (%s)", + L"Overall stats -%d (%s)", + L"Max trainers: %d (%s)", + L"Payout +%2.0f%s (%s)", + L"Payout limit increased to $%d (%s)", + L"Bonus for officers (%s)", + L"Bonus for vehicles (%s)", + L"Duration +%d hours (%s)", + L"Agent not in town", + L"Town loyalty too low", + L"Agent unavailable", + L"Agent contract expiring", + L"Can't use rebel agent", + L"Battle in progress", + L"Prepare Mission (%d supplies)", + L"View active mission effects", + L"View available mission list", + L"You are able to prepare one of the two missions presented. Once an agent is dispatched, they will be unavailable for approximately %d hours before becoming available again. A popup will notify you when preparations are complete. If preparations succeed, the mission's effects become active.", + L"A rebel agent can be sent to prepare a mission, but your mercenaries will be far more effective. Their experience level and skills can provide additional bonuses to missions.", + L"The cost of preparing a mission increases based on the number other missions either active or being prepared.", + L"New missions will be available on Day %d at 00:00.", + L"Active missions:", + L"%s - Preparing - Ready on Day %d, %02d:%02d", + L"%s - Active - Expires on Day %d, %02d:%02d", + L"[%s (%d supplies)]", + L"%s Send a rebel agent to prepare this mission?", + L"%s Send %s to prepare this mission? He will return in approximately %d hours.", + L"%s Send %s to prepare this mission? She will return in approximately %d hours.", + L"Mission \"%s\" is now in effect.", + L"Preparations for mission \"%s\" failed.", + L"Mission \"%s\" has expired and is no longer in effect.", }; STR16 szRebelCommandHelpText[] = // TODO.Translate { 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.", //TODO.Translate 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.", @@ -11958,7 +12015,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", @@ -12022,6 +12079,32 @@ STR16 szRebelCommandDirectivesText[] = // TODO.Translate L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + 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"Disrupt ASD", + L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", + 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"Send Supplies", + L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", + L"Soldier Bounties (Kingpin)", + L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", + 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[] = // TODO: Translate { L"The robot's installed weapon cannot be changed.", diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index fe934da13..059f894f2 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -2519,7 +2519,8 @@ STR16 pAssignmentStrings[] = L"Burial", L"Admin", // TODO.Translate L"Explore", // TODO.Translate - L"Event"// rftr: merc is on a mini event // TODO: translate + L"Event",// rftr: merc is on a mini event // TODO: translate + L"Mission", // rftr: rebel command }; STR16 pMilitiaString[] = @@ -4667,6 +4668,7 @@ STR16 pTransactionText[] = L"Mini event", // rftr: mini events // TODO: translate L"Funds transferred from rebel command", // rftr: rebel command L"Funds transferred to rebel command", // rftr: rebel command + L"Bounty payout", // rftr: rebel command soldier bounties }; STR16 pTransactionAlternateText[] = @@ -11778,12 +11780,16 @@ STR16 gLbeStatsDesc[14] = STR16 szRebelCommandText[] = // TODO.Translate { - 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)", @@ -11841,17 +11847,68 @@ STR16 szRebelCommandText[] = // TODO.Translate L"<", L">", L"Changing this Admin Action will cost $%d and reset its tier. Confirm expenditure?", + L"Insufficient supplies! Admin Actions have been DISABLED.", + L"New missions will be available every %d hours.", + L"New missions are available at the A.R.C. website.", + L"Mission preparations in progress.", + L"Mission duration: %d days", + L"Chance of success: %d%s", + L"[REDACTED]", + L"Name: %s", + L"Location: %s", + L"Assignment: %s", + L"Contract: %d days", + L"Contract: %d hours", + L"Contract: ---", + L"Agent bonus:", + L"Chance of success +%d%s (%s)", + L"Deployment range +%d (%s)", + L"ASD Income -%2.0f%s (%s)", + L"Steal fuel; send to %s (%s)", + L"Destroy reserve units (%s)", + L"Time +%2.0f%s (%s)", + L"Vision -%2.0f%s (%s)", + L"Gear quality -%d (%s)", + L"Overall stats -%d (%s)", + L"Max trainers: %d (%s)", + L"Payout +%2.0f%s (%s)", + L"Payout limit increased to $%d (%s)", + L"Bonus for officers (%s)", + L"Bonus for vehicles (%s)", + L"Duration +%d hours (%s)", + L"Agent not in town", + L"Town loyalty too low", + L"Agent unavailable", + L"Agent contract expiring", + L"Can't use rebel agent", + L"Battle in progress", + L"Prepare Mission (%d supplies)", + L"View active mission effects", + L"View available mission list", + L"You are able to prepare one of the two missions presented. Once an agent is dispatched, they will be unavailable for approximately %d hours before becoming available again. A popup will notify you when preparations are complete. If preparations succeed, the mission's effects become active.", + L"A rebel agent can be sent to prepare a mission, but your mercenaries will be far more effective. Their experience level and skills can provide additional bonuses to missions.", + L"The cost of preparing a mission increases based on the number other missions either active or being prepared.", + L"New missions will be available on Day %d at 00:00.", + L"Active missions:", + L"%s - Preparing - Ready on Day %d, %02d:%02d", + L"%s - Active - Expires on Day %d, %02d:%02d", + L"[%s (%d supplies)]", + L"%s Send a rebel agent to prepare this mission?", + L"%s Send %s to prepare this mission? He will return in approximately %d hours.", + L"%s Send %s to prepare this mission? She will return in approximately %d hours.", + L"Mission \"%s\" is now in effect.", + L"Preparations for mission \"%s\" failed.", + L"Mission \"%s\" has expired and is no longer in effect.", }; STR16 szRebelCommandHelpText[] = // TODO.Translate { 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.", //TODO.Translate 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.", @@ -11880,7 +11937,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", @@ -11944,6 +12001,32 @@ STR16 szRebelCommandDirectivesText[] = // TODO.Translate L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + 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"Disrupt ASD", + L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", + 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"Send Supplies", + L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", + L"Soldier Bounties (Kingpin)", + L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", + 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[] = // TODO: Translate { L"The robot's installed weapon cannot be changed.", diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index 8dd97fc07..4acaaf5d2 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -2474,7 +2474,8 @@ STR16 pAssignmentStrings[] = L"Burial", L"Admin", // TODO.Translate L"Explore", // TODO.Translate - L"Event"// rftr: merc is on a mini event // TODO: translate + L"Event",// rftr: merc is on a mini event // TODO: translate + L"Mission", // rftr: rebel command }; @@ -4659,6 +4660,7 @@ STR16 pTransactionText[] = L"Mini event", // rftr: mini events // TODO: translate L"Funds transferred from rebel command", // rftr: rebel command L"Funds transferred to rebel command", // rftr: rebel command + L"Bounty payout", // rftr: rebel command soldier bounties }; STR16 pTransactionAlternateText[] = @@ -11865,12 +11867,16 @@ STR16 gLbeStatsDesc[14] = STR16 szRebelCommandText[] = // TODO.Translate { - 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)", @@ -11928,17 +11934,68 @@ STR16 szRebelCommandText[] = // TODO.Translate L"<", L">", L"Changing this Admin Action will cost $%d and reset its tier. Confirm expenditure?", + L"Insufficient supplies! Admin Actions have been DISABLED.", + L"New missions will be available every %d hours.", + L"New missions are available at the A.R.C. website.", + L"Mission preparations in progress.", + L"Mission duration: %d days", + L"Chance of success: %d%s", + L"[REDACTED]", + L"Name: %s", + L"Location: %s", + L"Assignment: %s", + L"Contract: %d days", + L"Contract: %d hours", + L"Contract: ---", + L"Agent bonus:", + L"Chance of success +%d%s (%s)", + L"Deployment range +%d (%s)", + L"ASD Income -%2.0f%s (%s)", + L"Steal fuel; send to %s (%s)", + L"Destroy reserve units (%s)", + L"Time +%2.0f%s (%s)", + L"Vision -%2.0f%s (%s)", + L"Gear quality -%d (%s)", + L"Overall stats -%d (%s)", + L"Max trainers: %d (%s)", + L"Payout +%2.0f%s (%s)", + L"Payout limit increased to $%d (%s)", + L"Bonus for officers (%s)", + L"Bonus for vehicles (%s)", + L"Duration +%d hours (%s)", + L"Agent not in town", + L"Town loyalty too low", + L"Agent unavailable", + L"Agent contract expiring", + L"Can't use rebel agent", + L"Battle in progress", + L"Prepare Mission (%d supplies)", + L"View active mission effects", + L"View available mission list", + L"You are able to prepare one of the two missions presented. Once an agent is dispatched, they will be unavailable for approximately %d hours before becoming available again. A popup will notify you when preparations are complete. If preparations succeed, the mission's effects become active.", + L"A rebel agent can be sent to prepare a mission, but your mercenaries will be far more effective. Their experience level and skills can provide additional bonuses to missions.", + L"The cost of preparing a mission increases based on the number other missions either active or being prepared.", + L"New missions will be available on Day %d at 00:00.", + L"Active missions:", + L"%s - Preparing - Ready on Day %d, %02d:%02d", + L"%s - Active - Expires on Day %d, %02d:%02d", + L"[%s (%d supplies)]", + L"%s Send a rebel agent to prepare this mission?", + L"%s Send %s to prepare this mission? He will return in approximately %d hours.", + L"%s Send %s to prepare this mission? She will return in approximately %d hours.", + L"Mission \"%s\" is now in effect.", + L"Preparations for mission \"%s\" failed.", + L"Mission \"%s\" has expired and is no longer in effect.", }; STR16 szRebelCommandHelpText[] = // TODO.Translate { 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.", //TODO.Translate 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.", @@ -11967,7 +12024,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", @@ -12031,6 +12088,32 @@ STR16 szRebelCommandDirectivesText[] = // TODO.Translate L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + 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"Disrupt ASD", + L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", + 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"Send Supplies", + L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", + L"Soldier Bounties (Kingpin)", + L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", + 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[] = // TODO: Translate { L"The robot's installed weapon cannot be changed.", diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index b66a93eb8..ceed1a17c 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -2486,7 +2486,8 @@ STR16 pAssignmentStrings[] = L"Burial", L"Admin", // TODO.Translate L"Explore", // TODO.Translate - L"Event"// rftr: merc is on a mini event // TODO: translate + L"Event",// rftr: merc is on a mini event // TODO: translate + L"Mission", // rftr: rebel command }; @@ -4670,6 +4671,7 @@ STR16 pTransactionText[] = L"Mini event", // rftr: mini events // TODO: translate L"Funds transferred from rebel command", // rftr: rebel command L"Funds transferred to rebel command", // rftr: rebel command + L"Bounty payout", // rftr: rebel command soldier bounties }; STR16 pTransactionAlternateText[] = @@ -11878,12 +11880,16 @@ STR16 gLbeStatsDesc[14] = STR16 szRebelCommandText[] = // TODO.Translate { - 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)", @@ -11941,17 +11947,68 @@ STR16 szRebelCommandText[] = // TODO.Translate L"<", L">", L"Changing this Admin Action will cost $%d and reset its tier. Confirm expenditure?", + L"Insufficient supplies! Admin Actions have been DISABLED.", + L"New missions will be available every %d hours.", + L"New missions are available at the A.R.C. website.", + L"Mission preparations in progress.", + L"Mission duration: %d days", + L"Chance of success: %d%s", + L"[REDACTED]", + L"Name: %s", + L"Location: %s", + L"Assignment: %s", + L"Contract: %d days", + L"Contract: %d hours", + L"Contract: ---", + L"Agent bonus:", + L"Chance of success +%d%s (%s)", + L"Deployment range +%d (%s)", + L"ASD Income -%2.0f%s (%s)", + L"Steal fuel; send to %s (%s)", + L"Destroy reserve units (%s)", + L"Time +%2.0f%s (%s)", + L"Vision -%2.0f%s (%s)", + L"Gear quality -%d (%s)", + L"Overall stats -%d (%s)", + L"Max trainers: %d (%s)", + L"Payout +%2.0f%s (%s)", + L"Payout limit increased to $%d (%s)", + L"Bonus for officers (%s)", + L"Bonus for vehicles (%s)", + L"Duration +%d hours (%s)", + L"Agent not in town", + L"Town loyalty too low", + L"Agent unavailable", + L"Agent contract expiring", + L"Can't use rebel agent", + L"Battle in progress", + L"Prepare Mission (%d supplies)", + L"View active mission effects", + L"View available mission list", + L"You are able to prepare one of the two missions presented. Once an agent is dispatched, they will be unavailable for approximately %d hours before becoming available again. A popup will notify you when preparations are complete. If preparations succeed, the mission's effects become active.", + L"A rebel agent can be sent to prepare a mission, but your mercenaries will be far more effective. Their experience level and skills can provide additional bonuses to missions.", + L"The cost of preparing a mission increases based on the number other missions either active or being prepared.", + L"New missions will be available on Day %d at 00:00.", + L"Active missions:", + L"%s - Preparing - Ready on Day %d, %02d:%02d", + L"%s - Active - Expires on Day %d, %02d:%02d", + L"[%s (%d supplies)]", + L"%s Send a rebel agent to prepare this mission?", + L"%s Send %s to prepare this mission? He will return in approximately %d hours.", + L"%s Send %s to prepare this mission? She will return in approximately %d hours.", + L"Mission \"%s\" is now in effect.", + L"Preparations for mission \"%s\" failed.", + L"Mission \"%s\" has expired and is no longer in effect.", }; STR16 szRebelCommandHelpText[] = // TODO.Translate { 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.", //TODO.Translate 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.", @@ -11980,7 +12037,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", @@ -12044,6 +12101,32 @@ STR16 szRebelCommandDirectivesText[] = // TODO.Translate L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + 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"Disrupt ASD", + L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", + 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"Send Supplies", + L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", + L"Soldier Bounties (Kingpin)", + L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", + 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[] = // TODO: Translate { L"The robot's installed weapon cannot be changed.", diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index 92a8e8abb..76308a088 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -2480,7 +2480,8 @@ STR16 pAssignmentStrings[] = L"Burial", L"Admin", // TODO.Translate L"Explore", // TODO.Translate - L"Event"// rftr: merc is on a mini event // TODO: translate + L"Event",// rftr: merc is on a mini event // TODO: translate + L"Mission", // rftr: rebel command }; @@ -4662,6 +4663,7 @@ STR16 pTransactionText[] = L"Mini event", // rftr: mini events // TODO: translate L"Funds transferred from rebel command", // rftr: rebel command L"Funds transferred to rebel command", // rftr: rebel command + L"Bounty payout", // rftr: rebel command soldier bounties }; STR16 pTransactionAlternateText[] = @@ -11859,12 +11861,16 @@ STR16 gLbeStatsDesc[14] = STR16 szRebelCommandText[] = // TODO.Translate { - 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)", @@ -11922,17 +11928,68 @@ STR16 szRebelCommandText[] = // TODO.Translate L"<", L">", L"Changing this Admin Action will cost $%d and reset its tier. Confirm expenditure?", + L"Insufficient supplies! Admin Actions have been DISABLED.", + L"New missions will be available every %d hours.", + L"New missions are available at the A.R.C. website.", + L"Mission preparations in progress.", + L"Mission duration: %d days", + L"Chance of success: %d%s", + L"[REDACTED]", + L"Name: %s", + L"Location: %s", + L"Assignment: %s", + L"Contract: %d days", + L"Contract: %d hours", + L"Contract: ---", + L"Agent bonus:", + L"Chance of success +%d%s (%s)", + L"Deployment range +%d (%s)", + L"ASD Income -%2.0f%s (%s)", + L"Steal fuel; send to %s (%s)", + L"Destroy reserve units (%s)", + L"Time +%2.0f%s (%s)", + L"Vision -%2.0f%s (%s)", + L"Gear quality -%d (%s)", + L"Overall stats -%d (%s)", + L"Max trainers: %d (%s)", + L"Payout +%2.0f%s (%s)", + L"Payout limit increased to $%d (%s)", + L"Bonus for officers (%s)", + L"Bonus for vehicles (%s)", + L"Duration +%d hours (%s)", + L"Agent not in town", + L"Town loyalty too low", + L"Agent unavailable", + L"Agent contract expiring", + L"Can't use rebel agent", + L"Battle in progress", + L"Prepare Mission (%d supplies)", + L"View active mission effects", + L"View available mission list", + L"You are able to prepare one of the two missions presented. Once an agent is dispatched, they will be unavailable for approximately %d hours before becoming available again. A popup will notify you when preparations are complete. If preparations succeed, the mission's effects become active.", + L"A rebel agent can be sent to prepare a mission, but your mercenaries will be far more effective. Their experience level and skills can provide additional bonuses to missions.", + L"The cost of preparing a mission increases based on the number other missions either active or being prepared.", + L"New missions will be available on Day %d at 00:00.", + L"Active missions:", + L"%s - Preparing - Ready on Day %d, %02d:%02d", + L"%s - Active - Expires on Day %d, %02d:%02d", + L"[%s (%d supplies)]", + L"%s Send a rebel agent to prepare this mission?", + L"%s Send %s to prepare this mission? He will return in approximately %d hours.", + L"%s Send %s to prepare this mission? She will return in approximately %d hours.", + L"Mission \"%s\" is now in effect.", + L"Preparations for mission \"%s\" failed.", + L"Mission \"%s\" has expired and is no longer in effect.", }; STR16 szRebelCommandHelpText[] = // TODO.Translate { 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.", //TODO.Translate 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.", @@ -11961,7 +12018,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", @@ -12025,6 +12082,32 @@ STR16 szRebelCommandDirectivesText[] = // TODO.Translate L"Improving this directive will increase the number of volunteers gained per day.", }; +STR16 szRebelCommandAgentMissionsText[] = +{ + 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"Disrupt ASD", + L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", + 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"Send Supplies", + L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", + L"Soldier Bounties (Kingpin)", + L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", + 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[] = // TODO: Translate { L"The robot's installed weapon cannot be changed.",