Skip to content

Commit b712230

Browse files
authored
Merge pull request #7290 from IllianiBird/factionStandingPhase2
Improvement: Numerous Faction Standing Improvements; Added Faction Standing Events
2 parents ae3518b + 9c16019 commit b712230

File tree

48 files changed

+11469
-494
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+11469
-494
lines changed

MekHQ/resources/mekhq/resources/FactionJudgmentDialog.properties

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

MekHQ/resources/mekhq/resources/FactionJudgmentNewsArticle.properties

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

MekHQ/resources/mekhq/resources/FactionJudgmentSceneDialog.properties

Lines changed: 1762 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# suppress inspection "UnusedMessageFormatParameter" for the whole file
2+
# suppress inspection "UnusedProperty" for the whole file
3+
# Bonus
4+
FactionAccoladeDialog.credit=A generous donation.
5+
# Confirmation
6+
FactionAccoladeDialog.confirmation.inCharacter=Are you sure, {0}?
7+
FactionAccoladeDialog.confirmation.outOfCharacter=<b>WARNING:</b> each faction will only make this offer once per \
8+
campaign.
9+
FactionAccoladeDialog.confirmation.button.confirm=(Confirm) Continue as ordered.
10+
FactionAccoladeDialog.confirmation.button.cancel=(Cancel) Let me reconsider.
11+
# General
12+
FactionStandingUtilities.clan=Clan
13+
FactionStandingUtilities.the=the
14+
FactionCensureDialog.button.cancel=Cancel
15+
FactionCensureDialog.button.confirm=Confirm
16+
FactionCensureEvent.fine=Legal censure
17+
# FactionCensureGoingRogueDialog
18+
FactionCensureGoingRogueDialog.possibleFactions=Possible Factions:
19+
FactionCensureGoingRogueDialog.inCharacter=I''ve put together a list of our options.\
20+
<p>I hope you know what you''re doing, {0}...</p>
21+
FactionCensureGoingRogueDialog.outOfCharacter=This process will change your campaign faction. All other campaign \
22+
settings, including the rank system used, will be unchanged.\
23+
<p>The factions you can choose are dependent on the galactic political climate. Only the enemy of your current \
24+
faction, or their rivals, will be willing to accept you. Alternatively, the mercenary and pirate factions are \
25+
almost always available.</p>\
26+
<p>Changing faction will test the loyalties of all personnel, and it is likely some will choose to remain with your \
27+
current faction. This will cause them to leave your campaign.</p>\
28+
<p>Defecting (changing to a faction other than Mercenary or Pirate) will not be bloodless, expect casualties.</p>
29+
FactionCensureConfirmationDialog.button.confirm=(Continue) Yes
30+
FactionCensureConfirmationDialog.button.cancel=(Return) No
31+
FactionCensureConfirmationDialog.inCharacter=Are you sure, {0}?\
32+
<p>Once I send your answer, there''s no turning back.</p>

MekHQ/resources/mekhq/resources/FactionStandings.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,10 @@ factionStandingLevel.STANDING_LEVEL_3.innerSphere.label=Distrusted
291291
factionStandingLevel.STANDING_LEVEL_3.innerSphere.description=Few believe in your loyalty. Every job comes with a watchful\
292292
\ eye.
293293
factionStandingLevel.STANDING_LEVEL_3.CC.label=Watched
294-
factionStandingLevel.STANDING_LEVEL_3.CC.description=You are tolerated but not trusted. The ISF observes your every move.
294+
factionStandingLevel.STANDING_LEVEL_3.CC.description=You are tolerated but not trusted. The Maskirovka observes your every move.
295+
factionStandingLevel.STANDING_LEVEL_3.DC.label=Tolerated
296+
factionStandingLevel.STANDING_LEVEL_3.DC.description=You are permitted to operate, but only under suspicion. Your \
297+
loyalty remains uncertain.
295298
factionStandingLevel.STANDING_LEVEL_3.LA.label=A Liability
296299
factionStandingLevel.STANDING_LEVEL_3.LA.description=Association with your unit reflects poorly on Lyran officers.\
297300
\ Resources are restricted.

MekHQ/resources/mekhq/resources/PersonnelStatus.properties

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# suppress inspection "UnusedProperty" for the whole file
12
### NORMAL
23
ACTIVE.label=Active
34
ACTIVE.tooltip=They are currently a member of the force.
@@ -59,18 +60,19 @@ ENEMY_BONDSMAN.label=Enemy Bondsman
5960
ENEMY_BONDSMAN.tooltip=They were taken as a Bondsman by enemy forces.
6061
ENEMY_BONDSMAN.report=The enemy has claimed %s as a {0}<b>Bondsman</b>{1}.
6162
ENEMY_BONDSMAN.log=Taken as a Bondsman
62-
BONDSREF.label=Bondsref
63-
BONDSREF.tooltip=They have performed Bondsref
64-
BONDSREF.report=%s has preformed {0}<b>Bondsref</b>{1} instead of becoming a Bondsman.
65-
BONDSREF.log=Performed Bondsref
66-
SEPPUKU.label=Seppuku
67-
SEPPUKU.tooltip=They have performed Seppuku
68-
SEPPUKU.report=%s has performed {0}<b>Seppuku</b>{1} instead of becoming a Prisoner.
69-
SEPPUKU.log=Performed Seppuku
7063
BACKGROUND_CHARACTER.label=Background Character
7164
BACKGROUND_CHARACTER.tooltip=They are part of someone else's story
7265
BACKGROUND_CHARACTER.report=%s exited stage left.
7366
BACKGROUND_CHARACTER.log=Fell Into The Background
67+
IMPRISONED.label=Imprisoned
68+
IMPRISONED.tooltip=They have been imprisoned for their crimes
69+
IMPRISONED.report=%s has been {0}<b>Imprisoned</b>{1} for their crimes.
70+
IMPRISONED.log=Was imprisoned for their crimes
71+
DISHONORABLY_DISCHARGED.label=Dishonorably Discharged
72+
DISHONORABLY_DISCHARGED.tooltip=They have been dishonorably discharged
73+
DISHONORABLY_DISCHARGED.report=%s has been {0}<b>Dishonorably Discharged</b>{1}.
74+
DISHONORABLY_DISCHARGED.log=Was dishonorably discharged
75+
7476
### DEATH
7577
KIA.label=Killed in Action
7678
KIA.tooltip=They have been killed in action.
@@ -116,3 +118,11 @@ SUICIDE.label=Died from Suicide
116118
SUICIDE.tooltip=They took their own life.
117119
SUICIDE.report=%s has died by {0}<b>Suicide</b>{1}.
118120
SUICIDE.log=Took their own life
121+
BONDSREF.label=Bondsref
122+
BONDSREF.tooltip=They have performed Bondsref
123+
BONDSREF.report=%s has preformed {0}<b>Bondsref</b>{1} instead of becoming a Bondsman.
124+
BONDSREF.log=Performed Bondsref
125+
SEPPUKU.label=Seppuku
126+
SEPPUKU.tooltip=They have performed Seppuku
127+
SEPPUKU.report=%s has retained their honor by performing {0}<b>Seppuku</b>{1}.
128+
SEPPUKU.log=Performed Seppuku

MekHQ/src/mekhq/campaign/Campaign.java

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,10 @@
244244
import mekhq.campaign.universe.enums.HiringHallLevel;
245245
import mekhq.campaign.universe.eras.Era;
246246
import mekhq.campaign.universe.eras.Eras;
247+
import mekhq.campaign.universe.factionStanding.FactionAccoladeEvent;
248+
import mekhq.campaign.universe.factionStanding.FactionAccoladeLevel;
249+
import mekhq.campaign.universe.factionStanding.FactionCensureEvent;
250+
import mekhq.campaign.universe.factionStanding.FactionCensureLevel;
247251
import mekhq.campaign.universe.factionStanding.FactionStandingUtilities;
248252
import mekhq.campaign.universe.factionStanding.FactionStandings;
249253
import mekhq.campaign.universe.factionStanding.PerformBatchall;
@@ -5685,6 +5689,7 @@ public boolean newDay() {
56855689
// Advance the day by one
56865690
final LocalDate yesterday = currentDay;
56875691
currentDay = currentDay.plusDays(1);
5692+
boolean isMonday = currentDay.getDayOfWeek() == DayOfWeek.MONDAY;
56885693
boolean isFirstOfMonth = currentDay.getDayOfMonth() == 1;
56895694
boolean isNewYear = currentDay.getDayOfYear() == 1;
56905695

@@ -5764,7 +5769,7 @@ public boolean newDay() {
57645769

57655770
// Prisoner events can occur on Monday or the 1st of the month depending on the
57665771
// type of event
5767-
if (currentDay.getDayOfWeek() == DayOfWeek.MONDAY || isFirstOfMonth) {
5772+
if (isMonday || isFirstOfMonth) {
57685773
new PrisonerEventManager(this);
57695774
}
57705775

@@ -5794,7 +5799,7 @@ public boolean newDay() {
57945799
}
57955800
}
57965801

5797-
if (topUpWeekly && currentDay.getDayOfWeek() == DayOfWeek.MONDAY) {
5802+
if (topUpWeekly && isMonday) {
57985803
int bought = stockUpPartsInUse(getPartsInUse(ignoreMothballed, false, ignoreSparesUnderQuality));
57995804
addReport(String.format(resources.getString("weeklyStockCheck.text"), bought));
58005805
}
@@ -5805,16 +5810,85 @@ public boolean newDay() {
58055810
}
58065811

58075812
// Faction Standing
5808-
if (isFirstOfMonth && campaignOptions.isTrackFactionStanding()) {
5809-
String report = factionStandings.updateClimateRegard(faction, currentDay);
5810-
addReport(report);
5813+
if (campaignOptions.isTrackFactionStanding()) {
5814+
performFactionStandingChecks(isFirstOfMonth);
58115815
}
58125816

58135817
// This must be the last step before returning true
58145818
MekHQ.triggerEvent(new NewDayEvent(this));
58155819
return true;
58165820
}
58175821

5822+
/**
5823+
* Performs all daily and periodic standing checks for factions relevant to this campaign.
5824+
*
5825+
* <p>On the first day of the month, this method updates the climate regard for the active campaign faction,
5826+
* storing a summary report. It then iterates once through all faction standings and, for each faction:</p>
5827+
*
5828+
* <ul>
5829+
* <li>Checks for new censure actions and handles the creation of related events.</li>
5830+
* <li>Evaluates for new accolade levels, creating corresponding events.</li>
5831+
* <li>Warns if any referenced faction cannot be resolved.</li>
5832+
* </ul>
5833+
*
5834+
* <p>Finally, at the end of the checks, it processes censure degradation for all factions.</p>
5835+
*
5836+
* @param isFirstOfMonth {@code true} if called on the first day of the month.
5837+
*
5838+
* @author Illiani
5839+
* @since 0.50.07
5840+
*/
5841+
private void performFactionStandingChecks(boolean isFirstOfMonth) {
5842+
if (isFirstOfMonth) {
5843+
String report = factionStandings.updateClimateRegard(faction, currentDay);
5844+
addReport(report);
5845+
}
5846+
5847+
List<Mission> activeMissions = getActiveMissions(false);
5848+
boolean isInTransit = !location.isOnPlanet();
5849+
Factions factions = Factions.getInstance();
5850+
5851+
for (Entry<String, Double> standing : factionStandings.getAllFactionStandings().entrySet()) {
5852+
String factionCode = standing.getKey();
5853+
Faction relevantFaction = factions.getFaction(factionCode);
5854+
if (relevantFaction == null) {
5855+
logger.warn("Unable to fetch faction standing for faction: {}", factionCode);
5856+
continue;
5857+
}
5858+
5859+
// Censure check
5860+
if (relevantFaction.equals(faction)
5861+
|| (faction.getShortName().equals("MERC") && relevantFaction.isMercenaryOrganization())) {
5862+
FactionCensureLevel newCensureLevel = factionStandings.checkForCensure(
5863+
relevantFaction, currentDay, activeMissions, isInTransit);
5864+
if (newCensureLevel != null) {
5865+
new FactionCensureEvent(this, newCensureLevel, relevantFaction);
5866+
}
5867+
}
5868+
5869+
// Accolade check
5870+
boolean ignoreEmployer = relevantFaction.isMercenaryOrganization();
5871+
boolean isOnMission = FactionStandingUtilities.isIsOnMission(
5872+
!isInTransit,
5873+
getActiveAtBContracts(),
5874+
activeMissions,
5875+
relevantFaction.getShortName(),
5876+
location.getCurrentSystem(),
5877+
ignoreEmployer);
5878+
5879+
FactionAccoladeLevel newAccoladeLevel = factionStandings.checkForAccolade(
5880+
relevantFaction, currentDay, isOnMission);
5881+
5882+
if (newAccoladeLevel != null) {
5883+
new FactionAccoladeEvent(this, relevantFaction, newAccoladeLevel,
5884+
faction.equals(relevantFaction));
5885+
}
5886+
}
5887+
5888+
// Censure degradation
5889+
factionStandings.processCensureDegradation(currentDay);
5890+
}
5891+
58185892
public void refreshPersonnelMarkets() {
58195893
PersonnelMarketStyle marketStyle = campaignOptions.getPersonnelMarketStyle();
58205894
if (marketStyle == PERSONNEL_MARKET_DISABLED) {
@@ -6025,6 +6099,13 @@ private void processEducationNewDay() {
60256099
}
60266100
}
60276101

6102+
/**
6103+
* Retrieves the flagged commander from the personnel list. If no flagged commander is found returns {@code null}.
6104+
*
6105+
* <p><b>Usage:</b> consider using {@link #getCommander()} instead.</p>
6106+
*
6107+
* @return the flagged commander if present, otherwise {@code null}
6108+
*/
60286109
public @Nullable Person getFlaggedCommander() {
60296110
return getPersonnel().stream().filter(Person::isCommander).findFirst().orElse(null);
60306111
}

MekHQ/src/mekhq/campaign/mission/AtBContract.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,10 @@ private void updateEnemy(Campaign campaign, LocalDate today) {
653653

654654
if (tracksStanding) {
655655
// Whenever we dynamically change the enemy faction, we update standing accordingly
656-
factionStandings.processContractAccept(campaignFactionCode, faction, today);
656+
String report = factionStandings.processContractAccept(campaignFactionCode, faction, today);
657+
if (report != null) {
658+
campaign.addReport(report);
659+
}
657660
}
658661
}
659662
}

MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ public enum PersonnelStatus {
8888
ENEMY_BONDSMAN(NotificationSeverity.NEGATIVE, false, false),
8989
BONDSREF(NotificationSeverity.NEGATIVE, true, true),
9090
SEPPUKU(NotificationSeverity.NEGATIVE, true, true),
91-
BACKGROUND_CHARACTER(NotificationSeverity.WARNING, false, false);
91+
BACKGROUND_CHARACTER(NotificationSeverity.WARNING, false, false),
92+
IMPRISONED(NotificationSeverity.NEGATIVE, false, false),
93+
DISHONORABLY_DISCHARGED(NotificationSeverity.NEGATIVE, false, false);
9294

9395
/**
9496
* Represents the severity levels of a status.
@@ -488,6 +490,16 @@ public boolean isEnemyBondsman() {
488490
return this == ENEMY_BONDSMAN;
489491
}
490492

493+
/**
494+
* Checks if the character has the {@link #DISHONORABLY_DISCHARGED} personnel status.
495+
*
496+
* @return {@code true} if the character has the {@link #DISHONORABLY_DISCHARGED} personnel status {@code false}
497+
* otherwise.
498+
*/
499+
public boolean isDishonorablyDischarged() {
500+
return this == DISHONORABLY_DISCHARGED;
501+
}
502+
491503
/**
492504
* Checks if the character has the {@link #BONDSREF} personnel status.
493505
*
@@ -516,18 +528,25 @@ public boolean isBackground() {
516528
return this == BACKGROUND_CHARACTER;
517529
}
518530

531+
/**
532+
* Checks if the character has the {@link #IMPRISONED} personnel status.
533+
*
534+
* @return {@code true} if the character has the {@link #IMPRISONED} personnel status {@code false} otherwise.
535+
*/
536+
public boolean isImprisoned() {
537+
return this == IMPRISONED;
538+
}
539+
519540
/**
520541
* @return {@code true} if a person is currently absent from the core force, otherwise {@code false}
521542
*/
522543
public boolean isAbsent() {
523544
return isMIA() ||
524545
isPoW() ||
525-
isEnemyBondsman() ||
526546
isOnLeave() ||
527547
isOnMaternityLeave() ||
528548
isAwol() ||
529-
isStudent() ||
530-
isMissing();
549+
isStudent();
531550
}
532551

533552
/**
@@ -553,7 +572,10 @@ public boolean isDepartedUnit() {
553572
isDeserted() ||
554573
isDefected() ||
555574
isMissing() ||
556-
isLeft() || isEnemyBondsman() ||
575+
isLeft() ||
576+
isImprisoned() ||
577+
isEnemyBondsman() ||
578+
isDishonorablyDischarged() ||
557579
// We count background characters as departed, even though they technically never joined
558580
isBackground();
559581
}

MekHQ/src/mekhq/campaign/storyarc/StoryArc.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@
4040
import java.util.regex.Matcher;
4141
import java.util.regex.Pattern;
4242

43-
import org.w3c.dom.Document;
44-
import org.w3c.dom.Element;
45-
import org.w3c.dom.Node;
46-
import org.w3c.dom.NodeList;
47-
4843
import megamek.Version;
4944
import megamek.common.annotations.Nullable;
5045
import megamek.common.event.Subscribe;
@@ -64,6 +59,10 @@
6459
import mekhq.campaign.storyarc.storypoint.TravelStoryPoint;
6560
import mekhq.campaign.storyarc.storypoint.WaitStoryPoint;
6661
import mekhq.utilities.MHQXMLUtility;
62+
import org.w3c.dom.Document;
63+
import org.w3c.dom.Element;
64+
import org.w3c.dom.Node;
65+
import org.w3c.dom.NodeList;
6766

6867
/**
6968
* The Story Arc class manages a given story arc campaign.
@@ -554,7 +553,7 @@ private static void updateReplacementTokens(Campaign c) {
554553
}
555554

556555
// get commander information
557-
Person commander = c.getSeniorCommander();
556+
Person commander = c.getCommander();
558557
if (null == commander) {
559558
// shouldn't happen unless there are no personnel, but just in case
560559
replacementTokens.put("\\{commanderCallsign\\}", "callsign(?)");

0 commit comments

Comments
 (0)