Add "Send Supplies" mission

This commit is contained in:
rftrdev
2022-10-25 17:28:41 -07:00
parent a35d741403
commit c1b5d1719a
12 changed files with 90 additions and 27 deletions
+5
View File
@@ -4245,6 +4245,11 @@ void LoadRebelCommandSettings()
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);
+5
View File
@@ -1930,6 +1930,11 @@ typedef struct
UINT8 iSabotageMechanicalUnitsDuration_Bonus_Demolitions;
UINT8 iSabotageMechanicalUnitsDuration_Bonus_Heavy_Weapons;
INT8 iSendSuppliesToTownSuccessChance;
UINT8 iSendSuppliesToTownDuration;
INT32 iSendSuppliesToTownLoyaltyGain;
INT8 iSendSuppliesToTownInterval;
INT8 iTrainMilitiaAnywhereSuccessChance;
INT8 iTrainMilitiaAnywhereMaxTrainers;
INT8 iTrainMilitiaAnywhereMaxTrainers_Teaching;
+66 -2
View File
@@ -353,6 +353,7 @@ enum RebelCommandText // keep this synced with szRebelCommandText in the text fi
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,
@@ -414,6 +415,7 @@ enum RebelCommandAgentMissionsText // keep this synced with szRebelCommandAgentM
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)
};
@@ -546,6 +548,8 @@ 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();
@@ -1221,7 +1225,7 @@ void UpdateAdminActionChangeList(INT16 regionId)
BOOLEAN EnterWebsite()
{
// rftr todo: temp debugging
rebelCommandSaveInfo.availableMissions[0] = RCAM_DISRUPT_ASD;
rebelCommandSaveInfo.availableMissions[0] = RCAM_SEND_SUPPLIES_TO_TOWN;
UpdateAdminActionChangeList(iCurrentRegionId);
// make sure we have a valid directive
@@ -2229,6 +2233,7 @@ BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index)
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;
@@ -2248,6 +2253,7 @@ BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index)
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;
@@ -2270,6 +2276,7 @@ BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index)
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;
@@ -2289,6 +2296,7 @@ BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index)
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;
@@ -2503,6 +2511,15 @@ BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index)
}
break;
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;
case RCAM_SOLDIER_BOUNTIES_KINGPIN:
{
floatModifier = max(floatModifier, 1.f);
@@ -2578,7 +2595,7 @@ BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index)
const INT32 worldMin = GetWorldTotalMin();
const INT32 remaining = endTime - worldMin;
if (townId < FIRST_TOWN || townId >= NUM_TOWNS || townLoyalty < gRebelCommandSettings.iMinLoyaltyForMission)
if ((townId < FIRST_TOWN || townId >= NUM_TOWNS || townLoyalty < gRebelCommandSettings.iMinLoyaltyForMission) && rebelCommandSaveInfo.availableMissions[index] != RCAM_SEND_SUPPLIES_TO_TOWN)
{
canStartMission = FALSE;
swprintf(sText, szRebelCommandText[RCT_MISSION_CANT_START_LOW_LOYALTY]);
@@ -2593,6 +2610,11 @@ BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index)
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]);
}
if (!canStartMission)
{
@@ -2874,6 +2896,15 @@ void PrepareMission(INT8 index)
}
break;
case RCAM_SEND_SUPPLIES_TO_TOWN:
{
missionTitle = RCAMT_SEND_SUPPLIES_TO_TOWN_TITLE;
missionSuccessChance = gRebelCommandSettings.iSendSuppliesToTownSuccessChance;
missionDuration = gRebelCommandSettings.iSendSuppliesToTownDuration + SendSuppliesToTownDurationBonus(merc);
extraBits = SECTOR(merc->sSectorX, merc->sSectorY);
}
break;
case RCAM_SOLDIER_BOUNTIES_KINGPIN:
{
missionTitle = RCAMT_SOLDIER_BOUNTIES_KINGPIN_TITLE;
@@ -3909,6 +3940,9 @@ void HourlyUpdate()
{
HandleScouting();
// RCAM_SEND_SUPPLIES_TO_TOWN
SendSuppliesToTownMission();
// it's midnight! do the daily update
if (GetWorldHour() == 0)
{
@@ -4314,6 +4348,8 @@ void SetupInfo()
{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(
{
@@ -4854,6 +4890,7 @@ void HandleStrategicEvent(const UINT32 eventParam)
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:
{
@@ -4948,6 +4985,33 @@ void HandleStrategicEvent(const UINT32 eventParam)
DoMessageBox(MSG_BOX_BASIC_STYLE, msgBoxText, guiCurrentScreen, MSG_BOX_FLAG_OK, NULL, NULL);
}
void SendSuppliesToTownMission()
{
if (!gGameExternalOptions.fRebelCommandEnabled)
return;
const std::unordered_map<RebelCommandAgentMissions, UINT32>::iterator iter = missionMap.find(RCAM_SEND_SUPPLIES_TO_TOWN);
if (iter == missionMap.end())
return;
if (GetWorldHour() % gRebelCommandSettings.iSendSuppliesToTownInterval != 0)
return;
MissionSecondEvent evt;
DeserialiseMissionSecondEvent(iter->second, evt);
if (evt.isSecondEvent)
{
IncrementTownLoyalty(evt.extraBits, gRebelCommandSettings.iSendSuppliesToTownLoyaltyGain);
}
}
INT16 SendSuppliesToTownDurationBonus(const MERCPROFILESTRUCT* merc)
{
return merc ? merc->bExpLevel * 8 : 0;
}
BOOLEAN ShowEnemyMovementTargets()
{
if (!gGameExternalOptions.fRebelCommandEnabled)
+3 -17
View File
@@ -15,17 +15,6 @@ namespace RebelCommand
// applies to RegionSaveInfo.actionLevels: use the MSB as the active flag since I don't expect it to be used otherwise
constexpr UINT8 ADMIN_ACTION_ACTIVE_BIT = 1 << 7;
// for rebelCommandSaveInfo.uBitMask
// requirement breakdown
// 1 bit for notification toggle
// 5 * 4 bits for random mission selection (2off, 2def) = 32 choices each
// need some bits to track mission area (friendly/hostile? num towns?)
// remaining bits to track up/downgrades?
// some should be ASD specific?
// can we not do this and just check strategic events? GetAllStrategicEventsOfType(). could be costly. iterates over event LL on each call
// can we cache in-flight events on load? no real need to save this, right?
//constexpr UINT64 AGENT_NOTIFICATION_BIT = 1 << 0;
// the FIRST and SECOND strategic events share the same type (EVENT_REBELCOMMAND - no need to have multiple types yet, I think...)
// so the 32-bit int will have to block out information for both events
// FIRST EVENT BREAKDOWN
@@ -35,8 +24,7 @@ namespace RebelCommand
// C (8 bit) - the profile number of the merc that was sent. invalid if B == 0
// D (8 bit) - the mission ID. should match up with RebelCommandAgentMissions enum
// E (8 bit) - the mission duration, in hours. if 0, mission failed.
// the rest of the bits depend on the mission?
//
// extra bits are mission-specific
// SECOND EVENT BREAKDOWN
// A####### #######B CCCCCCCC DDDDDDDD
@@ -44,7 +32,7 @@ namespace RebelCommand
// B (1 bit) - 0 if the player sent a generic rebel agent, 1 if the player sent one of their own mercs
// C (8 bit) - the profile number of the merc that was sent. invalid if B == 0
// D (8 bit) - the mission ID. should match up with RebelCommandAgentMissions enum
// extra bits are for ???????????
// extra bits are mission-specific
enum RebelCommandDirectives
{
@@ -96,19 +84,17 @@ enum RebelCommandAgentMissions
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_SEND_SUPPLIES_TO_TOWN, // store agent location townid in extrabits
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_OBSERVE_SECTORS, // ??? competes with scouts?
RCAM_PURCHASE_SUPPLIES, // increase daily supply income, decrease daily $ income
RCAM_SABOTAGE_MINE,
RCAM_REDUCE_ENEMY_POOL, // need to make sure enemy pool is not infinite // giReinforcementPool, also gfUnlimitedTroops = zDiffSetting[gGameOptions.ubDifficultyLevel].bUnlimitedPoolOfTroops;
// militia/mercs get bonus vision (???)
// share vision with civilians?
+1 -1
View File
@@ -11961,7 +11961,7 @@ STR16 szRebelCommandText[] =
L"Bonus for officers (%s)",
L"Bonus for vehicles (%s)",
L"Duration +%d hours (%s)",
L"Agent not in loyal town",
L"Town loyalty too low",
L"Agent unavailable",
L"Agent contract expiring",
L"Battle in progress",
+1 -1
View File
@@ -11971,7 +11971,7 @@ STR16 szRebelCommandText[] = // TODO.Translate
L"Bonus for officers (%s)",
L"Bonus for vehicles (%s)",
L"Duration +%d hours (%s)",
L"Agent not in loyal town",
L"Town loyalty too low",
L"Agent unavailable",
L"Agent contract expiring",
L"Battle in progress",
+4 -1
View File
@@ -11961,9 +11961,10 @@ STR16 szRebelCommandText[] =
L"Bonus for officers (%s)",
L"Bonus for vehicles (%s)",
L"Duration +%d hours (%s)",
L"Agent not in loyal 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",
@@ -12102,6 +12103,8 @@ STR16 szRebelCommandAgentMissionsText[] =
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",
+1 -1
View File
@@ -11953,7 +11953,7 @@ STR16 szRebelCommandText[] = // TODO.Translate
L"Bonus for officers (%s)",
L"Bonus for vehicles (%s)",
L"Duration +%d hours (%s)",
L"Agent not in loyal town",
L"Town loyalty too low",
L"Agent unavailable",
L"Agent contract expiring",
L"Battle in progress",
+1 -1
View File
@@ -11875,7 +11875,7 @@ STR16 szRebelCommandText[] = // TODO.Translate
L"Bonus for officers (%s)",
L"Bonus for vehicles (%s)",
L"Duration +%d hours (%s)",
L"Agent not in loyal town",
L"Town loyalty too low",
L"Agent unavailable",
L"Agent contract expiring",
L"Battle in progress",
+1 -1
View File
@@ -11962,7 +11962,7 @@ STR16 szRebelCommandText[] = // TODO.Translate
L"Bonus for officers (%s)",
L"Bonus for vehicles (%s)",
L"Duration +%d hours (%s)",
L"Agent not in loyal town",
L"Town loyalty too low",
L"Agent unavailable",
L"Agent contract expiring",
L"Battle in progress",
+1 -1
View File
@@ -11975,7 +11975,7 @@ STR16 szRebelCommandText[] = // TODO.Translate
L"Bonus for officers (%s)",
L"Bonus for vehicles (%s)",
L"Duration +%d hours (%s)",
L"Agent not in loyal town",
L"Town loyalty too low",
L"Agent unavailable",
L"Agent contract expiring",
L"Battle in progress",
+1 -1
View File
@@ -11956,7 +11956,7 @@ STR16 szRebelCommandText[] = // TODO.Translate
L"Bonus for officers (%s)",
L"Bonus for vehicles (%s)",
L"Duration +%d hours (%s)",
L"Agent not in loyal town",
L"Town loyalty too low",
L"Agent unavailable",
L"Agent contract expiring",
L"Battle in progress",