Skip to content

Commit 3387d3b

Browse files
authored
Merge pull request #8535 from IllianiBird/successor
Improvement: Added Ability to Designate Second-in-Command
2 parents 0e0dc27 + 480b87c commit 3387d3b

6 files changed

Lines changed: 251 additions & 16 deletions

File tree

MekHQ/resources/mekhq/resources/GUI.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ edge.text=Edge
116116
bareHands.text=Bare Hands
117117
removedCommander.format=%s has been removed as the overall unit commander.
118118
setAsCommander.format=%s has been set as the overall unit commander.
119+
removedSecondInCommand.format={0} has been removed as the unit's second in command.
120+
setAsSecondInCommand.format={0} has been set as the unit's second in command.
119121
enterNewCallsign.text=Enter new callsign
120122
editCallsign.text=Edit Callsign
121123
changeSalary.text=Change Salary (-1 to remove custom salary)
@@ -313,6 +315,9 @@ miClanPersonnel.text=Clan Personnel
313315
miClanPersonnel.toolTipText=If this is selected, then the person is from Clan origins.
314316
miCommander.text=Commander
315317
miCommander.toolTipText=If this is selected, then the person is the unit's commander. The current commander, if any, will have the flag removed before this is assigned.
318+
miSecondInCommand.text=Second in Command
319+
miSecondInCommand.toolTipText=If this is selected, then the person is the unit's second in command. The current \
320+
second in command, if any, will have the flag removed before this is assigned.
316321
miDivorceable.text=Divorceable
317322
miDivorceable.toolTipText=If this is selected, then the person will be included as a potential person for processing divorce \n(standard checks still apply, so unmarried personnel will not be included even if this flag is selected)
318323
miFounder.text=Founder

MekHQ/src/mekhq/campaign/Campaign.java

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3989,30 +3989,66 @@ public int getPatientsFor(Person doctor) {
39893989
private Person[] findTopCommanders() {
39903990
Person flaggedCommander = getFlaggedCommander();
39913991
Person commander = flaggedCommander;
3992-
Person secondInCommand = null;
3992+
3993+
Person flaggedSecondInCommand = getFlaggedSecondInCommand();
3994+
Person secondInCommand = flaggedSecondInCommand;
3995+
3996+
if (flaggedCommander != null && flaggedSecondInCommand != null) {
3997+
return new Person[] { commander, secondInCommand };
3998+
}
39933999

39944000
for (Person person : getActivePersonnel(false, false)) {
3995-
// If we have a flagged commander, skip them
3996-
if (flaggedCommander != null) {
3997-
if (person.equals(flaggedCommander)) {
3998-
continue;
3999-
}
4000-
// Second in command is best among non-flagged
4001-
if (secondInCommand == null || person.outRanksUsingSkillTiebreaker(this, secondInCommand)) {
4002-
secondInCommand = person;
4003-
}
4004-
} else {
4001+
if (person == null) {
4002+
continue;
4003+
}
4004+
4005+
if (person.equals(flaggedCommander) || person.equals(flaggedSecondInCommand)) {
4006+
continue;
4007+
}
4008+
4009+
// Commander selection (if not locked)
4010+
if (flaggedCommander == null) {
40054011
if (commander == null) {
40064012
commander = person;
4007-
} else if (person.outRanksUsingSkillTiebreaker(this, commander)) {
4008-
secondInCommand = commander;
4013+
continue;
4014+
}
4015+
4016+
if (!person.equals(commander) && person.outRanksUsingSkillTiebreaker(this, commander)) {
4017+
Person previousCommander = commander;
40094018
commander = person;
4010-
} else if (secondInCommand == null || person.outRanksUsingSkillTiebreaker(this, secondInCommand)) {
4011-
if (!person.equals(commander)) {
4012-
secondInCommand = person;
4019+
4020+
// Previous commander becomes a candidate for second-in-command (if not locked)
4021+
if (flaggedSecondInCommand == null && !previousCommander.equals(commander)) {
4022+
if (secondInCommand == null) {
4023+
secondInCommand = previousCommander;
4024+
} else if (!previousCommander.equals(secondInCommand)
4025+
&& previousCommander.outRanksUsingSkillTiebreaker(this, secondInCommand)) {
4026+
secondInCommand = previousCommander;
4027+
}
40134028
}
4029+
continue;
40144030
}
40154031
}
4032+
4033+
// Second-in-command selection (if not locked), excluding commander
4034+
if (flaggedSecondInCommand == null) {
4035+
if (person.equals(commander)) {
4036+
continue;
4037+
}
4038+
4039+
if (secondInCommand == null) {
4040+
secondInCommand = person;
4041+
continue;
4042+
}
4043+
4044+
if (!person.equals(secondInCommand) && person.outRanksUsingSkillTiebreaker(this, secondInCommand)) {
4045+
secondInCommand = person;
4046+
}
4047+
}
4048+
}
4049+
4050+
if (commander != null && commander.equals(secondInCommand)) {
4051+
secondInCommand = null;
40164052
}
40174053

40184054
return new Person[] { commander, secondInCommand };
@@ -5474,6 +5510,17 @@ public void setInitiativeMaxBonus(int bonus) {
54745510
return getPersonnel().stream().filter(Person::isCommander).findFirst().orElse(null);
54755511
}
54765512

5513+
/**
5514+
* Retrieves the flagged second-in-command from the personnel list. If no flagged second-in-command is found returns {@code null}.
5515+
*
5516+
* <p><b>Usage:</b> consider using {@link #getSecondInCommand()} instead.</p>
5517+
*
5518+
* @return the flagged second-in-command if present, otherwise {@code null}
5519+
*/
5520+
public @Nullable Person getFlaggedSecondInCommand() {
5521+
return getPersonnel().stream().filter(Person::isSecondInCommand).findFirst().orElse(null);
5522+
}
5523+
54775524
/**
54785525
* Use {@link #getCommander()} instead
54795526
*/

MekHQ/src/mekhq/campaign/personnel/Person.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import static mekhq.campaign.personnel.skills.SkillType.*;
6666
import static mekhq.campaign.randomEvents.personalities.PersonalityController.generateReasoning;
6767
import static mekhq.campaign.randomEvents.personalities.PersonalityController.getTraitIndex;
68+
import static mekhq.utilities.MHQInternationalization.getFormattedText;
6869
import static mekhq.utilities.MHQInternationalization.getFormattedTextAt;
6970
import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG;
7071
import static mekhq.utilities.ReportingUtilities.getNegativeColor;
@@ -396,6 +397,7 @@ public class Person {
396397
// region Flags
397398
private boolean clanPersonnel;
398399
private boolean commander;
400+
private boolean secondInCommand;
399401
private boolean divorceable;
400402
private boolean founder; // +1 share if using shares system
401403
private boolean immortal;
@@ -636,6 +638,7 @@ public Person(final String preNominal, final String givenName, final String surn
636638
// region Flags
637639
setClanPersonnel(originFaction.isClan());
638640
setCommander(false);
641+
setSecondInCommand(false);
639642
setDivorceable(true);
640643
setFounder(false);
641644
setImmortal(false);
@@ -1620,6 +1623,28 @@ public void changeStatus(final Campaign campaign, final LocalDate today, final P
16201623
}
16211624

16221625
setCommander(false);
1626+
1627+
// promote second in command
1628+
Person secondInCommand = campaign.getSecondInCommand();
1629+
if (secondInCommand != null) {
1630+
secondInCommand.setSecondInCommand(false);
1631+
secondInCommand.setCommander(true);
1632+
1633+
String secondInCommandHyperlink = secondInCommand.getHyperlinkedFullTitle();
1634+
campaign.addReport(PERSONNEL, getFormattedText("removedSecondInCommand.format",
1635+
secondInCommandHyperlink));
1636+
campaign.addReport(PERSONNEL, String.format(resources.getString("setAsCommander.format"),
1637+
secondInCommandHyperlink));
1638+
1639+
campaign.personUpdated(secondInCommand);
1640+
}
1641+
}
1642+
1643+
// release the second-in-command flag.
1644+
if (isSecondInCommand() && status.isDepartedUnit()) {
1645+
setSecondInCommand(false);
1646+
campaign.addReport(PERSONNEL, getFormattedText("removedSecondInCommand.format",
1647+
getHyperlinkedFullTitle()));
16231648
}
16241649

16251650
// clean up the save entry
@@ -3003,6 +3028,14 @@ public void setCommander(final boolean commander) {
30033028
this.commander = commander;
30043029
}
30053030

3031+
public boolean isSecondInCommand() {
3032+
return secondInCommand;
3033+
}
3034+
3035+
public void setSecondInCommand(final boolean secondInCommand) {
3036+
this.secondInCommand = secondInCommand;
3037+
}
3038+
30063039
public boolean isDivorceable() {
30073040
return divorceable;
30083041
}
@@ -3654,6 +3687,7 @@ public int writeToXMLHeadless(PrintWriter pw, int indent, Campaign campaign) {
36543687
// region Flags
36553688
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "clanPersonnel", isClanPersonnel());
36563689
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "commander", commander);
3690+
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "secondInCommand", secondInCommand);
36573691
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "divorceable", divorceable);
36583692
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "founder", founder);
36593693
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "immortal", immortal);
@@ -4264,6 +4298,8 @@ public static Person generateInstanceFromXML(Node wn, Campaign campaign, Version
42644298
person.setClanPersonnel(Boolean.parseBoolean(wn2.getTextContent().trim()));
42654299
} else if (nodeName.equalsIgnoreCase("commander")) {
42664300
person.setCommander(Boolean.parseBoolean(wn2.getTextContent().trim()));
4301+
} else if (nodeName.equalsIgnoreCase("secondInCommand")) {
4302+
person.setSecondInCommand(Boolean.parseBoolean(wn2.getTextContent().trim()));
42674303
} else if (nodeName.equalsIgnoreCase("divorceable")) {
42684304
person.setDivorceable(Boolean.parseBoolean(wn2.getTextContent().trim()));
42694305
} else if (nodeName.equalsIgnoreCase("founder")) {

MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,12 @@ private List<CompanyGenerationPersonTracker> generateCombatPersonnel(final Campa
396396
generateOfficers(sortedTrackers);
397397
generateStandardMekWarriors(campaign, sortedTrackers);
398398

399+
// Dynamically fetch the second-in-command
400+
Person secondInCommand = campaign.getFlaggedSecondInCommand();
401+
if (secondInCommand != null) {
402+
secondInCommand.setSecondInCommand(true);
403+
}
404+
399405
return sortedTrackers;
400406
}
401407

MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
import static mekhq.campaign.randomEvents.personalities.PersonalityController.writePersonalityDescription;
6767
import static mekhq.campaign.randomEvents.prisoners.PrisonerEventManager.checkForIntelBreachEvent;
6868
import static mekhq.campaign.randomEvents.prisoners.PrisonerEventManager.processAdHocExecution;
69+
import static mekhq.utilities.MHQInternationalization.getFormattedText;
70+
import static mekhq.utilities.MHQInternationalization.getText;
6971
import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG;
7072
import static mekhq.utilities.ReportingUtilities.getAmazingColor;
7173
import static mekhq.utilities.ReportingUtilities.getPositiveColor;
@@ -308,6 +310,7 @@ public class PersonnelTableMouseAdapter extends JPopupMenuAdapter {
308310
private final JTable personnelTable;
309311
private final PersonnelTableModel personnelModel;
310312

313+
@Deprecated(since = "0.50.11", forRemoval = true)
311314
private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.GUI",
312315
MekHQ.getMHQOptions().getLocale());
313316
// endregion Variable Declarations
@@ -3820,12 +3823,34 @@ protected Optional<JPopupMenu> createPopupMenu() {
38203823
});
38213824
if (miCommander.isSelected()) {
38223825
person.setCommander(true);
3826+
person.setSecondInCommand(false);
38233827
getCampaign().addReport(PERSONNEL, String.format(resources.getString("setAsCommander.format"),
38243828
person.getHyperlinkedFullTitle()));
38253829
getCampaign().personUpdated(person);
38263830
}
38273831
});
38283832
menu.add(miCommander);
3833+
3834+
final JCheckBoxMenuItem miSecondInCommand = new JCheckBoxMenuItem(getText("miSecondInCommand.text"));
3835+
miSecondInCommand.setToolTipText(getText("miSecondInCommand.toolTipText"));
3836+
miSecondInCommand.setName("miSecondInCommand");
3837+
miSecondInCommand.setSelected(person.isSecondInCommand());
3838+
miSecondInCommand.addActionListener(evt -> {
3839+
getCampaign().getPersonnel().stream().filter(Person::isSecondInCommand).forEach(secondInCommand -> {
3840+
secondInCommand.setSecondInCommand(false);
3841+
getCampaign().addReport(PERSONNEL, getFormattedText("removedSecondInCommand.format",
3842+
secondInCommand.getHyperlinkedFullTitle()));
3843+
getCampaign().personUpdated(secondInCommand);
3844+
});
3845+
if (miSecondInCommand.isSelected()) {
3846+
person.setSecondInCommand(true);
3847+
person.setCommander(false);
3848+
getCampaign().addReport(PERSONNEL, getFormattedText("setAsSecondInCommand.format",
3849+
person.getHyperlinkedFullTitle()));
3850+
getCampaign().personUpdated(person);
3851+
}
3852+
});
3853+
menu.add(miSecondInCommand);
38293854
}
38303855

38313856
cbMenuItem = new JCheckBoxMenuItem(resources.getString("miDivorceable.text"));

0 commit comments

Comments
 (0)