8000 Added Administrative Skill Integration for Techs by IllianiBird · Pull Request #6438 · MegaMek/mekhq · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Added Administrative Skill Integration for Techs #6438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ maintenanceTab.border="One 'Mek lance can beat one mob, large or small, any time
<br>Lecture at the New Avalon Institute of Science, 3017</i>
# createRepairTab
lblRepairTab.text=Repair Options
lblTechsUseAdministration.text=Techs Use Administration \u270E \uD83C\uDF1F
lblTechsUseAdministration.tooltip=Daily available tech minutes is increased and decreased based on the Tech's\
\ <i>Administration</i> skill. Each skill level (Green, Regular, etc.) in <i>Administration</i> increases available\
\ minutes by around 5%.
lblUseEraModsCheckBox.text=Use Era Modifiers for Repair Rolls
lblUseEraModsCheckBox.tooltip=Use faction-specific era mods for repair rolls.
lblAssignedTechFirstCheckBox.text=Place Assigned Technicians to the Top of the List
Expand Down Expand Up @@ -576,9 +580,10 @@ lblUseTougherHealing.tooltip=The healing check will be penalized for every addit
lblMaximumPatients.text=Base Beds per Doctor \u2728
lblMaximumPatients.tooltip=This is the base number of patients that can be treated per doctor. This can be modified by\
\ Administration.
lblDoctorsUseAdministration.text=Doctors Need Administration \u270E
lblDoctorsUseAdministration.text=Doctors Need Administration \u270E \uD83C\uDF1F
lblDoctorsUseAdministration.tooltip=If enabled, the <i>Administration</i> skill of each Doctor influences how many hospital\
\ beds they generate.
\ beds they generate. Each skill level in <i>Administration</i> (Green, Regular, etc.) increases the number of beds by\
\ around 20%.
# createSalariesTab
lblSalariesTab.text=Salary Options
lblDisableSecondaryRoleSalary.text=Disable Secondary Role Salaries \u270E \u2728
Expand Down
72 changes: 50 additions & 22 deletions MekHQ/src/mekhq/campaign/Campaign.java
Original file line number Diff line number Diff line change
Expand Up @@ -3107,14 +3107,35 @@ public List<Person> getTechs(final boolean noZeroMinute, final boolean eliteFirs
}

/**
* Returns a list of active technicians.
* Retrieves a list of active technicians, with options to include only those with time remaining,
* prioritize elite technicians, and expand the search to include technicians with additional roles.
*
* @param noZeroMinute If TRUE, then techs with no time remaining will be excluded from the list.
* @param eliteFirst If TRUE and sorted also TRUE, then return the list sorted from best to worst
* @param expanded If TRUE, then include techs with expanded roles (e.g. Tech/Vessel skill)
* <p>The resulting list includes {@link Person} objects who qualify as technicians ({@link Person#isTech()})
* or, if specified, as expanded technicians ({@link Person#isTechExpanded()}). If the person is part of a
* self-crewed unit (e.g., an engineer on a self-crewed vessel), they are also included in the list.</p>
*
* @return The list of active {@link Person}s who qualify as technicians ({@link Person#isTech()}), or who qualify
* as expanded technicians ({@link Person#isTechExpanded()}).
* <p>The returned list can be customized and sorted based on a variety of criteria:</p>
* <ul>
* <li>Technicians with no remaining available time can be excluded if {@code noZeroMinute} is set to {@code true}.</li>
* <li>The list can be sorted from elite (best) to least skilled if {@code eliteFirst} is set to {@code true}.</li>
* <li>When {@code expanded} is set to {@code true}, technicians with expanded roles (e.g., dual skill sets) are included
* in addition to regular technicians.</li>
* <li>The list is further sorted in the following order:
* <ol>
* <li>By skill level (default: lowest to highest, or highest to lowest if elite-first enabled).</li>
* <li>By available daily tech time (highest to lowest).</li>
* <li>By rank (lowest to highest).</li>
* </ol>
* </li>
* </ul>
*
* @param noZeroMinute If {@code true}, excludes technicians with no remaining available minutes.
* @param eliteFirst If {@code true}, sorts the list to place the most skilled technicians at the top.
* @param expanded If {@code true}, includes technicians with expanded roles (e.g., those qualifying
* under {@link Person#isTechExpanded()}).
*
* @return A list of active {@link Person} objects who qualify as technicians or expanded technicians,
* sorted by skill, available time, and rank as specified by the input parameters.
*/
public List<Person> getTechsExpanded(final boolean noZeroMinute, final boolean eliteFirst, final boolean expanded) {
final List<Person> techs = getActivePersonnel().stream()
Expand All @@ -3130,21 +3151,26 @@ public List<Person> getTechsExpanded(final boolean noZeroMinute, final boolean e
}

// Return the tech collection sorted worst to best Skill Level, or reversed if we want elites first
Comparator<Person> techSorter = Comparator.comparingInt(person -> person.getSkillLevel(this,
!person.getPrimaryRole().isTech() && person.getSecondaryRole().isTechSecondary()).ordinal());
techs.sort(Comparator.comparingInt(person -> person.getSkillLevel(this,
!person.getPrimaryRole().isTech() && person.getSecondaryRole().isTechSecondary()).ordinal()));

if (eliteFirst) {
techSorter = techSorter.reversed()
.thenComparing(Comparator.comparingInt(Person::getDailyAvailableTechTime).reversed());
} else {
techSorter = techSorter.thenComparing(Comparator.comparingInt(Person::getMinutesLeft).reversed());
Collections.reverse(techs);
}

techSorter = techSorter.thenComparing(new PersonTitleSorter());
// sort based on available minutes (highest -> lowest)
techs.sort(Comparator.comparingInt(person -> person.getDailyAvailableTechTime(false)));

if (techs.size() > 1) {
techs.subList(1, techs.size()).sort(techSorter);
}
// finally, sort based on rank (lowest -> highest)
techs.sort((person1, person2) -> {
if (person1.outRanks(person2)) {
return 1; // person1 outranks person2 -> person2 should come first
} else if (person2.outRanks(person1)) {
return -1; // person2 outranks person1 -> person1 should come first
} else {
return 0; // They are considered equal
}
});

return techs;
}
Expand Down Expand Up @@ -4107,7 +4133,8 @@ public void refit(Refit theRefit) {
theRefit.addTimeSpent(tech.getMinutesLeft());
tech.setMinutesLeft(0);
report = report + ", " + theRefit.getTimeLeft() + " minutes left. Completion ";
int daysLeft = (int) Math.ceil((double) theRefit.getTimeLeft() / (double) tech.getDailyAvailableTechTime());
int daysLeft = (int) Math.ceil((double) theRefit.getTimeLeft() /
(double) tech.getDailyAvailableTechTime(campaignOptions.isTechsUseAdministration()));
if (daysLeft == 1) {
report += " tomorrow.</b>";
} else {
Expand Down Expand Up @@ -4150,7 +4177,7 @@ public void refit(Refit theRefit) {
refit(theRefit);
report += " Completion ";
int daysLeft = (int) Math.ceil((double) theRefit.getTimeLeft() /
(double) tech.getDailyAvailableTechTime());
(double) tech.getDailyAvailableTechTime(campaignOptions.isTechsUseAdministration()));
if (daysLeft == 1) {
report += " tomorrow.</b>";
} else {
Expand Down Expand Up @@ -4286,10 +4313,11 @@ public String fixPart(IPartWork partWork, Person tech) {
report += " - <b>";
report += partWork.getTimeLeft();
report += " minutes left. Work";
if ((minutesUsed > 0) && (tech.getDailyAvailableTechTime() > 0)) {
if ((minutesUsed > 0) &&
(tech.getDailyAvailableTechTime(campaignOptions.isTechsUseAdministration()) > 0)) {
report += " will be finished ";
int daysLeft = (int) Math.ceil((double) partWork.getTimeLeft() /
(double) tech.getDailyAvailableTechTime());
(double) tech.getDailyAvailableTechTime(campaignOptions.isTechsUseAdministration()));
if (daysLeft == 1) {
report += " tomorrow.</b>";
} else {
Expand Down Expand Up @@ -4849,7 +4877,7 @@ public void processNewDayPersonnel() {
}
}

person.resetMinutesLeft();
person.resetMinutesLeft(campaignOptions.isTechsUseAdministration());
person.setAcquisition(0);

processAdvancedMedicalEvents(person);
Expand Down Expand Up @@ -5121,7 +5149,7 @@ public void processNewDayUnits() {
try {
u.resetEngineer();
if (null != u.getEngineer()) {
u.getEngineer().resetMinutesLeft();
u.getEngineer().resetMinutesLeft(campaignOptions.isTechsUseAdministration());
}

doMaintenance(u);
Expand Down
34 changes: 32 additions & 2 deletions MekHQ/src/mekhq/campaign/CampaignOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public static String getTechLevelName(final int techLevel) {
private boolean useEraMods;
private boolean assignedTechFirst;
private boolean resetToFirstTech;
private boolean techsUseAdministration;
private boolean useQuirks;
private boolean useAeroSystemHits;
private boolean destroyByMargin;
Expand Down Expand Up @@ -639,8 +640,9 @@ public CampaignOptions() {
// Repair
useEraMods = false;
assignedTechFirst = false;
resetToFirstTech = false;
useQuirks = false;
resetToFirstTech = false;
techsUseAdministration = false;
useQuirks = false;
useAeroSystemHits = false;
destroyByMargin = false;
destroyMargin = 4;
Expand Down Expand Up @@ -3630,6 +3632,31 @@ public void setResetToFirstTech(final boolean resetToFirstTech) {
this.resetToFirstTech = resetToFirstTech;
}

/**
* Checks whether administrative adjustments are applied for technician time calculations.
*
* <p>This configuration determines if technicians' daily available time should be adjusted
* using administrative multipliers in relevant calculations.</p>
*
* @return {@code true} if administrative adjustments are enabled for technicians, {@code false} otherwise.
*/
public boolean isTechsUseAdministration() {
return techsUseAdministration;
}

/**
* Sets whether administrative adjustments should be applied to technician time calculations.
*
* <p>Enabling this setting applies administrative multipliers to modify technicians' daily available time
* in relevant calculations.</p>
*
* @param techsUseAdministration {@code true} to enable administrative adjustments for technicians, {@code false} to
* disable them.
*/
public void setTechsUseAdministration(final boolean techsUseAdministration) {
this.techsUseAdministration = techsUseAdministration;
}

/**
* @return true to use the origin faction for personnel names instead of a set faction
*/
Expand Down Expand Up @@ -4752,6 +4779,7 @@ public void writeToXml(final PrintWriter pw, int indent) {
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useEraMods", useEraMods);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "assignedTechFirst", assignedTechFirst);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "resetToFirstTech", resetToFirstTech);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "techsUseAdministration", techsUseAdministration);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useQuirks", useQuirks);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "xpCostMultiplier", xpCostMultiplier);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "scenarioXP", scenarioXP);
Expand Down Expand Up @@ -5396,6 +5424,8 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve
retVal.assignedTechFirst = Boolean.parseBoolean(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("resetToFirstTech")) {
retVal.resetToFirstTech = Boolean.parseBoolean(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("techsUseAdministration")) {
retVal.techsUseAdministration = Boolean.parseBoolean(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("useQuirks")) {
retVal.useQuirks = Boolean.parseBoolean(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("xpCostMultiplier")) {
Expand Down
Loading
Loading
0