Skip to content

Commit 406aee2

Browse files
authored
Merge pull request #7296 from IllianiBird/factionStandingFedComCivilWar
Improvement: Added Faction Standing Ultimatum Events for the Onset of the FedCom Civil War and ComStar Schism.
2 parents 7d43b35 + dd39893 commit 406aee2

File tree

10 files changed

+1013
-20
lines changed

10 files changed

+1013
-20
lines changed

MekHQ/resources/mekhq/resources/FactionStandingUltimatumDialog.properties

Lines changed: 276 additions & 0 deletions
Large diffs are not rendered by default.

MekHQ/resources/mekhq/resources/GUI.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ ValidationWarning.title=Validation Issue
5555
Version.text=Version
5656
Year.text=Year
5757
Yes.text=Yes
58+
AreYouSure.text=Are you sure?
5859
##### Adapter
5960
#### PersonnelTableMouseAdapter Class - TODO : unfinished, with cleanup required
6061
improved.format=%s improved %s!

MekHQ/src/mekhq/campaign/Campaign.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@
251251
import mekhq.campaign.universe.factionStanding.FactionAccoladeLevel;
252252
import mekhq.campaign.universe.factionStanding.FactionCensureEvent;
253253
import mekhq.campaign.universe.factionStanding.FactionCensureLevel;
254+
import mekhq.campaign.universe.factionStanding.FactionStandingUltimatum;
254255
import mekhq.campaign.universe.factionStanding.FactionStandingJudgmentType;
255256
import mekhq.campaign.universe.factionStanding.FactionStandingUtilities;
256257
import mekhq.campaign.universe.factionStanding.FactionStandings;
@@ -5830,6 +5831,7 @@ public boolean newDay() {
58305831
* storing a summary report. It then iterates once through all faction standings and, for each faction:</p>
58315832
*
58325833
* <ul>
5834+
* <li>Checks for new ultimatum events.</li>
58335835
* <li>Checks for new censure actions and handles the creation of related events.</li>
58345836
* <li>Evaluates for new accolade levels, creating corresponding events.</li>
58355837
* <li>Warns if any referenced faction cannot be resolved.</li>
@@ -5844,14 +5846,19 @@ public boolean newDay() {
58445846
* @since 0.50.07
58455847
*/
58465848
private void performFactionStandingChecks(boolean isFirstOfMonth, boolean isNewYear) {
5847-
if (isNewYear && faction.getShortName().equals("MERC")) {
5849+
String campaignFactionCode = faction.getShortName();
5850+
if (isNewYear && campaignFactionCode.equals(MERCENARY_FACTION_CODE)) {
58485851
checkForNewMercenaryOrganizationStartUp(false);
58495852
}
58505853

58515854
if (!campaignOptions.isTrackFactionStanding()) {
58525855
return;
58535856
}
58545857

5858+
if (FactionStandingUltimatum.checkUltimatumForDate(currentDay, campaignFactionCode)) {
5859+
new FactionStandingUltimatum(currentDay, this);
5860+
}
5861+
58555862
if (isFirstOfMonth) {
58565863
String report = factionStandings.updateClimateRegard(faction, currentDay);
58575864
addReport(report);
@@ -5870,7 +5877,6 @@ private void performFactionStandingChecks(boolean isFirstOfMonth, boolean isNewY
58705877
}
58715878

58725879
// Censure check
5873-
String campaignFactionCode = faction.getShortName();
58745880
boolean isMercenarySpecialCase = campaignFactionCode.equals(MERCENARY_FACTION_CODE) &&
58755881
relevantFaction.isMercenaryOrganization();
58765882
boolean isPirateSpecialCase = isPirateCampaign() &&
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
3+
*
4+
* This file is part of MekHQ.
5+
*
6+
* MekHQ is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License (GPL),
8+
* version 3 or (at your option) any later version,
9+
* as published by the Free Software Foundation.
10+
*
11+
* MekHQ is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty
13+
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
* See the GNU General Public License for more details.
15+
*
16+
* A copy of the GPL should have been included with this project;
17+
* if not, see <https://www.gnu.org/licenses/>.
18+
*
19+
* NOTICE: The MegaMek organization is a non-profit group of volunteers
20+
* creating free software for the BattleTech community.
21+
*
22+
* MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
23+
* of The Topps Company, Inc. All Rights Reserved.
24+
*
25+
* Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
26+
* InMediaRes Productions, LLC.
27+
*
28+
* MechWarrior Copyright Microsoft Corporation. MekHQ was created under
29+
* Microsoft's "Game Content Usage Rules"
30+
* <https://www.xbox.com/en-US/developers/rules> and it is not endorsed by or
31+
* affiliated with Microsoft.
32+
*/
33+
package mekhq.campaign.universe.factionStanding;
34+
35+
import mekhq.campaign.personnel.enums.PersonnelRole;
36+
37+
/**
38+
* Represents the core details about an agitator involved in a faction ultimatum scenario.
39+
*
40+
* @param name the name of the agitator
41+
* @param role the role of the agitator
42+
* @param factionCode the code identifier for the agitator's faction
43+
*
44+
* @author Illiani
45+
* @since 0.50.07
46+
*/
47+
public record FactionStandingAgitatorData(
48+
String name,
49+
PersonnelRole role,
50+
String factionCode
51+
) {}

MekHQ/src/mekhq/campaign/universe/factionStanding/FactionStandingLevel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public enum FactionStandingLevel {
8383
final static String FALLBACK_LABEL_SUFFIX_PERIPHERY = "periphery";
8484

8585
private final static int MINIMUM_STANDING_LEVEL = 0;
86-
private final static int MAXIMUM_STANDING_LEVEL = 8;
86+
final static int MAXIMUM_STANDING_LEVEL = 8;
8787

8888
private final int standingLevel;
8989
private final double minimumRegard;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
3+
*
4+
* This file is part of MekHQ.
5+
*
6+
* MekHQ is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License (GPL),
8+
* version 3 or (at your option) any later version,
9+
* as published by the Free Software Foundation.
10+
*
11+
* MekHQ is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty
13+
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
* See the GNU General Public License for more details.
15+
*
16+
* A copy of the GPL should have been included with this project;
17+
* if not, see <https://www.gnu.org/licenses/>.
18+
*
19+
* NOTICE: The MegaMek organization is a non-profit group of volunteers
20+
* creating free software for the BattleTech community.
21+
*
22+
* MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
23+
* of The Topps Company, Inc. All Rights Reserved.
24+
*
25+
* Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
26+
* InMediaRes Productions, LLC.
27+
*
28+
* MechWarrior Copyright Microsoft Corporation. MekHQ was created under
29+
* Microsoft's "Game Content Usage Rules"
30+
* <https://www.xbox.com/en-US/developers/rules> and it is not endorsed by or
31+
* affiliated with Microsoft.
32+
*/
33+
package mekhq.campaign.universe.factionStanding;
34+
35+
import java.time.LocalDate;
36+
import java.time.Month;
37+
import java.util.Map;
38+
39+
import megamek.common.enums.Gender;
40+
import mekhq.campaign.Campaign;
41+
import mekhq.campaign.personnel.Person;
42+
import mekhq.campaign.personnel.enums.PersonnelRole;
43+
import mekhq.gui.dialog.factionStanding.FactionStandingUltimatumDialog;
44+
45+
/**
46+
* Handles the orchestration and presentation of faction standing ultimatum events.
47+
*
48+
* <p>This class manages the association between significant historical dates and their respective Faction Standing
49+
* ultimatum scenarios, triggering appropriate in-game dialogs and transitions when such events are detected for the
50+
* campaign's current faction. It ensures scenarios such as major faction splits or leadership transitions (e.g., the
51+
* Federated Commonwealth Civil War, ComStar Schism) are detected and surfaced to the player, accompanied by unique
52+
* dialog sequences involving prominent historical personalities.</p>
53+
*
54+
* <p>Key responsibilities include:</p>
55+
* <ul>
56+
* <li>Tracking historically significant faction ultimatum dates and their context.</li>
57+
* <li>Providing static checks for whether an ultimatum event is relevant to a given date and faction.</li>
58+
* <li>Validating campaign and faction alignment before presenting a scenario dialog to the player.</li>
59+
* <li>Constructing {@link Person} representations of event participants for use in dialogs.</li>
60+
* </ul>
61+
*
62+
* <p>See also: {@link FactionStandingUltimatumData} and {@link FactionStandingAgitatorData} for structure of scenario
63+
* and participant data.</p>
64+
*
65+
* @author Illiani
66+
* @since 0.50.07
67+
*/
68+
public class FactionStandingUltimatum {
69+
/**
70+
* Represents the date marking the beginning of the Federated Commonwealth Civil War.
71+
*
72+
* <p>I opted to go with the month Katherine Steiner-Davion chose to secede from the alliance.</p>
73+
*/
74+
private static final LocalDate FED_COM_CIVIL_WAR = LocalDate.of(3057, Month.SEPTEMBER, 18);
75+
private static final FactionStandingAgitatorData KATRINA_STEINER =
76+
new FactionStandingAgitatorData("Archon Katrina Steiner", PersonnelRole.NOBLE, "LA");
77+
private static final FactionStandingAgitatorData VICTOR_STEINER_DAVION =
78+
new FactionStandingAgitatorData("Archon-Prince Victor Steiner-Davion", PersonnelRole.NOBLE, "FS");
79+
/**
80+
* Represents the date commemorating the schism within ComStar.
81+
*
82+
* <p>Primus Myndo Waterly was killed June 6th 3025, a week later (June 13th) Sharilar Mori was elected as Primus.
83+
* This caused Precentor Demona Aziz to head to Atreus (FWL) and kick off the ComStar Schism. It's a 100-day journey
84+
* from Terra to Atreus, which is how we got this date.</p>
85+
*
86+
* <p>There is an argument to be made that Precentor Aziz probably could have traveled via command circuit or
87+
* similar. I think that's very valid, however, I still went with the below date as it factors time needed to get a
88+
* meeting with Thomas Marik and then to arrange the HPG message that officially starts the schism.</p>
89+
*/
90+
private static final LocalDate COMSTAR_SCHISM = LocalDate.of(3052, Month.SEPTEMBER, 21);
91+
private static final FactionStandingAgitatorData DEMONA_AZIZ =
92+
new FactionStandingAgitatorData("Precentor Demona Aziz", PersonnelRole.RELIGIOUS_LEADER, "WOB");
93+
private static final FactionStandingAgitatorData ANASTASIUS_FOCHT =
94+
new FactionStandingAgitatorData("Precentor Martial Anastasius Focht", PersonnelRole.NOBLE, "CS");
95+
96+
/**
97+
* A mapping of specific historical dates to their corresponding faction standing ultimatum events.
98+
*
99+
* <p>This map associates significant events in faction history with their respective data regarding the faction
100+
* affected, the challenger, the incumbent leader, and whether the transition is non-violent.</p>
101+
*
102+
* <p>Each entry in the map uses a {@link LocalDate} as the key, representing the date of the faction ultimatum,
103+
* and a {@link FactionStandingUltimatumData} object as the value, capturing the relevant contextual data for the
104+
* ultimatum event.</p>
105+
*/
106+
private final static Map<LocalDate, FactionStandingUltimatumData> FACTION_STANDING_ULTIMATUMS = Map.of(
107+
FED_COM_CIVIL_WAR, new FactionStandingUltimatumData("FC", KATRINA_STEINER, VICTOR_STEINER_DAVION, false),
108+
COMSTAR_SCHISM, new FactionStandingUltimatumData("CS", DEMONA_AZIZ, ANASTASIUS_FOCHT, true)
109+
);
110+
111+
private final Campaign campaign;
112+
113+
/**
114+
* Checks whether a faction ultimatum is associated with the given date and matches the specified campaign faction
115+
* code.
116+
*
117+
* <p>This method provides a fast way to determine if a specific campaign faction has an ultimatum on a given date,
118+
* without requiring initialization of the full {@link FactionStandingUltimatum} class.</p>
119+
*
120+
* @param date the date to check for a faction ultimatum
121+
* @param campaignFactionCode the faction code to check for association with the ultimatum
122+
*
123+
* @return {@code true} if an ultimatum for the given date matches the specified faction code, {@code false}
124+
* otherwise
125+
*/
126+
public static boolean checkUltimatumForDate(final LocalDate date, final String campaignFactionCode) {
127+
FactionStandingUltimatumData ultimatum = FACTION_STANDING_ULTIMATUMS.get(date);
128+
return ultimatum != null && campaignFactionCode.equals(ultimatum.affectedFactionCode());
129+
}
130+
131+
public FactionStandingUltimatum(final LocalDate date, final Campaign campaign) {
132+
this.campaign = campaign;
133+
134+
FactionStandingUltimatumData ultimatum = FACTION_STANDING_ULTIMATUMS.get(date);
135+
136+
// We should have used 'checkUltimatumForDate' before initializing 'FactionStandingUltimatum', so we should
137+
// never return here. However, I opted to include this check as added security.
138+
if (ultimatum == null) {
139+
return;
140+
}
141+
142+
// Or here...
143+
String affectedFactionCode = FACTION_STANDING_ULTIMATUMS.get(date).affectedFactionCode();
144+
String campaignFactionCode = campaign.getFaction().getShortName();
145+
if (!campaignFactionCode.equals(affectedFactionCode)) {
146+
return;
147+
}
148+
149+
// Security checks out of the way, process the ultimatum
150+
Person challenger = createAgitator(ultimatum.challenger());
151+
Person incumbent = createAgitator(ultimatum.incumbent());
152+
new FactionStandingUltimatumDialog(campaign, challenger, incumbent, ultimatum.isViolentTransition());
153+
}
154+
155+
/**
156+
* Creates a {@link Person} entity within the campaign from the provided agitator data.
157+
*
158+
* <p>The agitator will receive the specified name, role, and faction code; surname and bloodname fields are set
159+
* to empty strings. As this information is pushed into the givenName, instead.</p>
160+
*
161+
* @param agitator the data record containing the agitator's name, role, and faction code
162+
*
163+
* @return a new {@link Person} instance initialized with the specified attributes
164+
*
165+
* @author Illiani
166+
* @since 0.50.07
167+
*/
168+
private Person createAgitator(FactionStandingAgitatorData agitator) {
169+
String name = agitator.name();
170+
PersonnelRole role = agitator.role();
171+
String factionCode = agitator.factionCode();
172+
173+
Person person = campaign.newPerson(role, factionCode, Gender.MALE); // Gender is irrelevant here
174+
175+
person.setGivenName(name);
176+
person.setSurname("");
177+
person.setBloodname("");
178+
179+
return person;
180+
}
181+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (C) 2025 The MegaMek Team. All Rights Reserved.
3+
*
4+
* This file is part of MekHQ.
5+
*
6+
* MekHQ is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License (GPL),
8+
* version 3 or (at your option) any later version,
9+
* as published by the Free Software Foundation.
10+
*
11+
* MekHQ is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty
13+
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+
* See the GNU General Public License for more details.
15+
*
16+
* A copy of the GPL should have been included with this project;
17+
* if not, see <https://www.gnu.org/licenses/>.
18+
*
19+
* NOTICE: The MegaMek organization is a non-profit group of volunteers
20+
* creating free software for the BattleTech community.
21+
*
22+
* MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks
23+
* of The Topps Company, Inc. All Rights Reserved.
24+
*
25+
* Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of
26+
* InMediaRes Productions, LLC.
27+
*
28+
* MechWarrior Copyright Microsoft Corporation. MekHQ was created under
29+
* Microsoft's "Game Content Usage Rules"
30+
* <https://www.xbox.com/en-US/developers/rules> and it is not endorsed by or
31+
* affiliated with Microsoft.
32+
*/
33+
package mekhq.campaign.universe.factionStanding;
34+
35+
/**
36+
* Holds data for Faction Standing ultimatum event.
37+
*
38+
* @param affectedFactionCode the code for the faction affected by the ultimatum
39+
* @param challenger information about initiating the challenger
40+
* @param incumbent information about the faction leader being challenged
41+
* @param isViolentTransition {@code true} if the transition is violent
42+
*
43+
* @author Illiani
44+
* @since 0.50.07
45+
*/
46+
public record FactionStandingUltimatumData(
47+
String affectedFactionCode,
48+
FactionStandingAgitatorData challenger,
49+
FactionStandingAgitatorData incumbent,
50+
boolean isViolentTransition
51+
) {}

0 commit comments

Comments
 (0)