Add settings variable for mission refresh time

Fix nullptr derefence when starting a mission with a generic agent

Limit missions to one per batch

Add popup box and screenmsg when a mission activates, fails to activates, and expires
This commit is contained in:
rftrdev
2022-10-17 00:33:38 -07:00
parent 2b88fac870
commit 983aa24b1b
4 changed files with 80 additions and 46 deletions
+1
View File
@@ -4158,6 +4158,7 @@ void LoadRebelCommandSettings()
gRebelCommandSettings.iFortificationsBonus = iniReader.ReadInteger("Rebel Command Settings", "FORTIFICATIONS_BONUS", 10, 0, 100); gRebelCommandSettings.iFortificationsBonus = iniReader.ReadInteger("Rebel Command Settings", "FORTIFICATIONS_BONUS", 10, 0, 100);
// agent missions // agent missions
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.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.iDeepDeploymentSuccessChance = iniReader.ReadInteger("Rebel Command Settings", "DEEP_DEPLOYMENT_SUCCESS_CHANCE", 50, 0, 100);
+1
View File
@@ -1843,6 +1843,7 @@ typedef struct
INT16 iFortificationsBonus; INT16 iFortificationsBonus;
// agent missions // agent missions
INT8 iMissionRefreshTimeDays;
INT8 iMinLoyaltyForMission; INT8 iMinLoyaltyForMission;
INT8 iDeepDeploymentSuccessChance; INT8 iDeepDeploymentSuccessChance;
+77 -45
View File
@@ -173,6 +173,9 @@ void GetMissionInfo(RebelCommandAgentMissions mission, const MERCPROFILESTRUCT*
intModifierSkill = 0; intModifierSkill = 0;
extraBits = 0; extraBits = 0;
if (merc == nullptr)
return;
if (mission == RCAM_NONE) if (mission == RCAM_NONE)
return; return;
@@ -2120,7 +2123,8 @@ BOOLEAN SetupMissionAgentBox(UINT16 x, UINT16 y, INT8 index)
// rftr todo: handle RCAM_NONE (ie, mission prep in progress!) // rftr todo: handle RCAM_NONE (ie, mission prep in progress!)
if (rebelCommandSaveInfo.availableMissions[index] == RCAM_NONE) if (rebelCommandSaveInfo.availableMissions[index] == RCAM_NONE)
{ {
swprintf(sText, L"No mission available"); // we shouldn't even reach this point, but leaving this here for safety
swprintf(sText, L"Mission preparations in progress.");
DrawTextToScreen(sText, x, y+155, 230, FONT14ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED); DrawTextToScreen(sText, x, y+155, 230, FONT14ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED);
return FALSE; return FALSE;
} }
@@ -2492,9 +2496,23 @@ void RenderMissionOverview()
switch (missionOverviewSubview) switch (missionOverviewSubview)
{ {
case MOS_MISSION_LIST: case MOS_MISSION_LIST:
for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) if (rebelCommandSaveInfo.availableMissions[0] == RCAM_NONE)
{ {
SetupMissionAgentBox(WEBSITE_LEFT + 15 + 240 * i, WEBSITE_TOP + 65, i); UINT32 nextMissionAvailableDay = GetWorldDay();
const INT8 interval = gRebelCommandSettings.iMissionRefreshTimeDays;
nextMissionAvailableDay += (interval - (nextMissionAvailableDay % interval));
swprintf(sText, L"Mission preparations in progress.");
DrawTextToScreen(sText, WEBSITE_LEFT + 15, WEBSITE_TOP + 155, WEBSITE_WIDTH - 30, FONT14ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED);
swprintf(sText, L"New missions will be available on Day %d at 00:00.", nextMissionAvailableDay);
DrawTextToScreen(sText, WEBSITE_LEFT + 15, WEBSITE_TOP + 175, WEBSITE_WIDTH - 30, FONT14ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED);
}
else
{
for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i)
{
SetupMissionAgentBox(WEBSITE_LEFT + 15 + 240 * i, WEBSITE_TOP + 65, i);
}
} }
break; break;
@@ -2507,7 +2525,8 @@ void RenderMissionOverview()
} }
// "new missions every X hours" text // "new missions every X hours" text
DrawTextToScreen(szRebelCommandAgentMissionsText[RCAMT_NEW_MISSIONS_AVAILABLE_TIME], WEBSITE_LEFT + 22, WEBSITE_TOP + WEBSITE_HEIGHT - 14, 0, FONT10ARIAL, FONT_MCOLOR_BLACK, FONT_MCOLOR_BLACK, FALSE, 0); swprintf(sText, szRebelCommandAgentMissionsText[RCAMT_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);
} }
void StartMission(INT8 index) void StartMission(INT8 index)
@@ -2534,7 +2553,7 @@ void StartMission(INT8 index)
} }
} }
const MERCPROFILESTRUCT merc = gMercProfiles[mercs[agentIndex[index]]->ubProfile]; const MERCPROFILESTRUCT* merc = agentIndex[index] == mercs.size() ? nullptr : &gMercProfiles[mercs[agentIndex[index]]->ubProfile];
CHAR16 text[400]; CHAR16 text[400];
RebelCommandAgentMissionsText missionTitle; RebelCommandAgentMissionsText missionTitle;
INT8 missionSuccessChance; INT8 missionSuccessChance;
@@ -2547,7 +2566,7 @@ void StartMission(INT8 index)
int intSkill; int intSkill;
UINT16 extraBits; UINT16 extraBits;
MissionHelpers::GetMissionInfo(static_cast<RebelCommandAgentMissions>(rebelCommandSaveInfo.availableMissions[index]), &merc, durationBonus, floatModifier, intModifier, durSkill, floatSkill, intSkill, extraBits); MissionHelpers::GetMissionInfo(static_cast<RebelCommandAgentMissions>(rebelCommandSaveInfo.availableMissions[index]), merc, durationBonus, floatModifier, intModifier, durSkill, floatSkill, intSkill, extraBits);
switch (rebelCommandSaveInfo.availableMissions[index]) switch (rebelCommandSaveInfo.availableMissions[index])
{ {
case RCAM_DEEP_DEPLOYMENT: case RCAM_DEEP_DEPLOYMENT:
@@ -2617,7 +2636,7 @@ void StartMission(INT8 index)
default: break; default: break;
} }
missionSuccessChance += GetMissionSuccessChanceBonus(&merc); missionSuccessChance += GetMissionSuccessChanceBonus(merc);
if (Random(100) > static_cast<UINT8>(missionSuccessChance)) if (Random(100) > static_cast<UINT8>(missionSuccessChance))
{ {
@@ -2640,10 +2659,10 @@ void StartMission(INT8 index)
else else
{ {
MissionHelpers::missionParam = SerialiseMissionFirstEvent(FALSE, agentIndex[index], static_cast<RebelCommandAgentMissions>(rebelCommandSaveInfo.availableMissions[index]), missionDuration, static_cast<UINT8>(extraBits)); MissionHelpers::missionParam = SerialiseMissionFirstEvent(FALSE, agentIndex[index], static_cast<RebelCommandAgentMissions>(rebelCommandSaveInfo.availableMissions[index]), missionDuration, static_cast<UINT8>(extraBits));
if (merc.bSex == MALE) if (merc->bSex == MALE)
swprintf(text, L"%s Send %s to prepare this mission? He will return in 24 hours.", text, merc.zNickname); swprintf(text, L"%s Send %s to prepare this mission? He will return in 24 hours.", text, merc->zNickname);
else else
swprintf(text, L"%s Send %s to prepare this mission? She will return in 24 hours.", text, merc.zNickname); swprintf(text, L"%s Send %s to prepare this mission? She will return in 24 hours.", text, merc->zNickname);
} }
DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, text, LAPTOP_SCREEN, MSG_BOX_FLAG_YESNO, [](UINT8 exitValue) { DoLapTopMessageBox(MSG_BOX_LAPTOP_DEFAULT, text, LAPTOP_SCREEN, MSG_BOX_FLAG_YESNO, [](UINT8 exitValue) {
@@ -2664,13 +2683,10 @@ void StartMission(INT8 index)
ChangeSoldiersAssignment(pSoldier, ASSIGNMENT_REBELCOMMAND); ChangeSoldiersAssignment(pSoldier, ASSIGNMENT_REBELCOMMAND);
} }
// disable missions until next refresh
for (INT8 i = 0; i < NUM_ARC_AGENT_SLOTS; ++i) for (INT8 i = 0; i < NUM_ARC_AGENT_SLOTS; ++i)
{ {
if (evt.missionId == rebelCommandSaveInfo.availableMissions[i]) rebelCommandSaveInfo.availableMissions[i] = RCAM_NONE;
{
rebelCommandSaveInfo.availableMissions[i] = RCAM_NONE;
break;
}
} }
// 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 // 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
@@ -3595,41 +3611,43 @@ void DailyUpdate()
// update missions // update missions
// rftr todo: test me // rftr todo: test me
// rftr todo: don't do this every day! if (GetWorldDay() % gRebelCommandSettings.iMissionRefreshTimeDays == 0)
std::unordered_set<RebelCommandAgentMissions> validMissions;
for (int i = 0; i < RCAM_NUM_MISSIONS; ++i)
{ {
validMissions.insert(static_cast<RebelCommandAgentMissions>(i)); std::unordered_set<RebelCommandAgentMissions> validMissions;
} for (int i = 0; i < RCAM_NUM_MISSIONS; ++i)
for (const auto& pair : missionMap)
{
const RebelCommandAgentMissions mission = pair.first;
validMissions.erase(mission);
}
if (validMissions.size() >= NUM_ARC_AGENT_SLOTS)
{
for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i)
{ {
const INT8 mission = static_cast<INT8>(Random(validMissions.size())); validMissions.insert(static_cast<RebelCommandAgentMissions>(i));
rebelCommandSaveInfo.availableMissions[i] = mission;
validMissions.erase(static_cast<RebelCommandAgentMissions>(mission));
}
}
else
{
int idx = 0;
for (auto iter = validMissions.cbegin(); iter != validMissions.cend(); ++iter)
{
rebelCommandSaveInfo.availableMissions[idx] = *iter;
idx++;
} }
while (idx < NUM_ARC_AGENT_SLOTS) for (const auto& pair : missionMap)
{ {
rebelCommandSaveInfo.availableMissions[idx] = RCAM_NONE; const RebelCommandAgentMissions mission = pair.first;
idx++; validMissions.erase(mission);
}
if (validMissions.size() >= NUM_ARC_AGENT_SLOTS)
{
for (int i = 0; i < NUM_ARC_AGENT_SLOTS; ++i)
{
const INT8 mission = static_cast<INT8>(Random(validMissions.size()));
rebelCommandSaveInfo.availableMissions[i] = mission;
validMissions.erase(static_cast<RebelCommandAgentMissions>(mission));
}
}
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++;
}
} }
} }
} }
@@ -4284,6 +4302,8 @@ void HandleStrategicEvent(const UINT32 eventParam)
MissionSecondEvent evt2; MissionSecondEvent evt2;
DeserialiseMissionFirstEvent(eventParam, evt1); DeserialiseMissionFirstEvent(eventParam, evt1);
DeserialiseMissionSecondEvent(eventParam, evt2); DeserialiseMissionSecondEvent(eventParam, evt2);
CHAR16 msgBoxText[200];
CHAR16 screenMsgText[200];
if (evt1.isFirstEvent) if (evt1.isFirstEvent)
{ {
@@ -4351,6 +4371,9 @@ void HandleStrategicEvent(const UINT32 eventParam)
// rftr todo: tell the player that the mission has started. popupbox or screenmsg? // rftr todo: tell the player that the mission has started. popupbox or screenmsg?
missionMap.insert(std::make_pair(mission, activatedMissionParam)); missionMap.insert(std::make_pair(mission, activatedMissionParam));
swprintf(msgBoxText, L"Mission prep success! %s", szRebelCommandAgentMissionsText[2 + evt1.missionId * 2]);
swprintf(screenMsgText, L"Mission \"%s\" is now in effect.", szRebelCommandAgentMissionsText[2 + evt1.missionId * 2]);
ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, screenMsgText);
} }
} }
else else
@@ -4363,13 +4386,22 @@ void HandleStrategicEvent(const UINT32 eventParam)
StatChange(pSoldier, LDRAMT, 20, FROM_FAILURE); StatChange(pSoldier, LDRAMT, 20, FROM_FAILURE);
StatChange(pSoldier, WISDOMAMT, 15, FROM_FAILURE); StatChange(pSoldier, WISDOMAMT, 15, FROM_FAILURE);
} }
swprintf(msgBoxText, L"Mission prep failed... %s", szRebelCommandAgentMissionsText[2 + evt1.missionId * 2]);
swprintf(screenMsgText, L"Preparations for mission \"%s\" failed.", szRebelCommandAgentMissionsText[2 + evt1.missionId * 2]);
ScreenMsg(FONT_MCOLOR_RED, MSG_INTERFACE, screenMsgText);
} }
} }
else if (evt2.isSecondEvent) else if (evt2.isSecondEvent)
{ {
// mission duration is over. deactivate the mission // mission duration is over. deactivate the mission
missionMap.erase(static_cast<RebelCommandAgentMissions>(evt2.missionId)); missionMap.erase(static_cast<RebelCommandAgentMissions>(evt2.missionId));
swprintf(msgBoxText, L"Mission duration complete! %s", szRebelCommandAgentMissionsText[2 + evt2.missionId * 2]);
swprintf(screenMsgText, L"Mission \"%s\" has expired and is no longer in effect.", szRebelCommandAgentMissionsText[2 + evt1.missionId * 2]);
ScreenMsg(FONT_MCOLOR_RED, MSG_INTERFACE, screenMsgText);
} }
DoMessageBox(MSG_BOX_BASIC_STYLE, msgBoxText, guiCurrentScreen, MSG_BOX_FLAG_OK, NULL, NULL);
} }
BOOLEAN ShowEnemyMovementTargets() BOOLEAN ShowEnemyMovementTargets()
+1 -1
View File
@@ -12036,7 +12036,7 @@ STR16 szRebelCommandDirectivesText[] =
STR16 szRebelCommandAgentMissionsText[] = STR16 szRebelCommandAgentMissionsText[] =
{ {
L"New missions will be available every 48 hours.", L"New missions will be available every %d hours.",
L"A mission is currently active.", L"A mission is currently active.",
L"Deep Deployment", 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"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.",