diff --git a/MekHQ/data/universe/academies/Local Academies.xml b/MekHQ/data/universe/academies/Local Academies.xml index 180134836a..126c9e5cca 100644 --- a/MekHQ/data/universe/academies/Local Academies.xml +++ b/MekHQ/data/universe/academies/Local Academies.xml @@ -18,6 +18,7 @@ High School + 1 true A typical academic life for teenagers across the Inner Sphere true @@ -33,6 +34,7 @@ Preparatory School + 1 true A boarding school for the privileged teenagers of the Inner Sphere. true @@ -49,6 +51,7 @@ Military School + 1 true true Whether as punishment, or a matter of familial pride, an education here prepares students for a life of military discipline. @@ -93,6 +96,7 @@ Adult High School + 1 A option for those students who never graduated high school, or who never had the chance to attend. true Terra @@ -107,6 +111,7 @@ Technical College + 2 Technical Colleges are places where the technicians of the future hone their craft. true Terra @@ -155,6 +160,7 @@ University (Bachelor's) + 3 The upper crust of higher education, universities offer a wide range of courses and post-graduate options true Terra @@ -195,6 +201,7 @@ University (Master's) + 3 The upper crust of higher education, universities offer a wide range of courses and post-graduate options true Terra @@ -235,6 +242,7 @@ University (Doctorate) + 3 The upper crust of higher education, universities offer a wide range of courses and post-graduate options true Terra @@ -293,6 +301,7 @@ Boot Camp + 5 true Private After a couple of months' hard training students are ready to serve on the line. @@ -323,6 +332,7 @@ Military Academy + 4 true Corporal Local Military Academies claim to offer the finest tuition for career-minded students. However, there is no replacement for in-the-field experience. @@ -361,6 +371,7 @@ NCO Candidate Bootcamp + 6 true Corporal Here, candidates are trained locally in the skills required to lead men. @@ -379,6 +390,7 @@ Warrant Officer Candidate School + 7 true Warrant Officer In those forces that use them, Warrant Officers are essential military administrators and act as a go-between NCOs and Officers. Here they learn the skills needed to succeed. @@ -396,6 +408,7 @@ Officer Candidate School + 8 true Lieutenant Jr. Grade Only the most qualified personnel are fit to become Officers. This is where they gain essential training and education. diff --git a/MekHQ/data/universe/awards/standard.xml b/MekHQ/data/universe/awards/standard.xml index 02a7b3a06a..c7c067663d 100644 --- a/MekHQ/data/universe/awards/standard.xml +++ b/MekHQ/data/universe/awards/standard.xml @@ -53,11 +53,8 @@ Graduate of basic training. Education Awards 4-08-1-BasicTraining.png - 1 - 1 Training - Individual - Career + 5 NCO Academy Graduate @@ -65,10 +62,8 @@ Education Awards 4-07-1-NCOGraduate.png 2 - 1 Training - Individual - Career + 6 Warrant Officer Graduate @@ -76,10 +71,8 @@ Education Awards 4-06-2-WarrantGraduate.png 3 - 21 Training - Individual - Career + 7 Officer Graduate @@ -88,10 +81,8 @@ 4-06-1-OfficerGraduate.png 4 1 - 31 Training - Individual - Career + 8 Personal Awards diff --git a/MekHQ/docs/Awards Module.pdf b/MekHQ/docs/Awards Module.pdf index 74f15ed44a..0c1f720a62 100644 Binary files a/MekHQ/docs/Awards Module.pdf and b/MekHQ/docs/Awards Module.pdf differ diff --git a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties index ae8557c351..00ff93a230 100644 --- a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties +++ b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties @@ -276,6 +276,8 @@ chkEnableTheatreOfWarAwards.text=Theatre of War chkEnableTheatreOfWarAwards.toolTipText=Awards issued for partaking in galactic conflicts. chkEnableTimeAwards.text=Time chkEnableTimeAwards.toolTipText=Awards for remaining in the unit for a certain duration. +chkEnableTrainingAwards.text=Training +chkEnableTrainingAwards.toolTipText=Awards for graduating from academies (requires Education to be enabled in the Life Paths tab). chkEnableMiscAwards.text=Misc chkEnableMiscAwards.toolTipText=Miscellaneous awards (see documentation). ##end Personnel Tab diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 68127426ed..9972aacde7 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -68,10 +68,12 @@ import mekhq.campaign.parts.equipment.EquipmentPart; import mekhq.campaign.parts.equipment.MissingEquipmentPart; import mekhq.campaign.personnel.*; +import mekhq.campaign.personnel.autoAwards.AutoAwardsController; import mekhq.campaign.personnel.death.AbstractDeath; import mekhq.campaign.personnel.death.DisabledRandomDeath; import mekhq.campaign.personnel.divorce.AbstractDivorce; import mekhq.campaign.personnel.divorce.DisabledRandomDivorce; +import mekhq.campaign.personnel.education.Academy; import mekhq.campaign.personnel.education.EducationController; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.PersonnelStatus; @@ -127,6 +129,8 @@ import java.util.*; import java.util.stream.Collectors; +import static mekhq.campaign.personnel.education.EducationController.getAcademy; + /** * The main campaign class, keeps track of teams and units * @author Taharqa @@ -1818,7 +1822,7 @@ public void updatePartInUse(PartInUse piu) { if (piu.equals(newPiu)) { piu.setPlannedCount(piu.getPlannedCount() + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() - : (Part) maybePart) * maybePart.getQuantity()); + : (Part) maybePart) * maybePart.getQuantity()); } } } @@ -1853,7 +1857,7 @@ public Set getPartsInUse() { } piu.setPlannedCount(piu.getPlannedCount() + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() - : (Part) maybePart) * maybePart.getQuantity()); + : (Part) maybePart) * maybePart.getQuantity()); } return inUse.keySet(); @@ -1912,15 +1916,15 @@ public Person findBestInRole(PersonnelRole role, String primary, String secondar retVal = p; highest = p.getSkill(primary).getLevel(); } else if (secondary != null && p.getSkill(primary).getLevel() == highest && - /* - * If the skill level of the current person is the same as the previous highest, - * select the current instead under the following conditions: - */ + /* + * If the skill level of the current person is the same as the previous highest, + * select the current instead under the following conditions: + */ (retVal == null || // None has been selected yet (current has level 0) retVal.getSkill(secondary) == null || // Previous selection does not have secondary - // skill + // skill (p.getSkill(secondary) != null // Current has secondary skill and it is higher than the - // previous. + // previous. && p.getSkill(secondary).getLevel() > retVal.getSkill(secondary).getLevel()))) { retVal = p; } @@ -1999,7 +2003,7 @@ public boolean isWorkingOnRefit(Person p) { Objects.requireNonNull(p); Unit unit = getHangar().findUnit(u -> - u.isRefitting() && p.equals(u.getRefit().getTech())); + u.isRefitting() && p.equals(u.getRefit().getTech())); return unit != null; } @@ -2412,7 +2416,7 @@ public boolean canAcquireParts(@Nullable Person person) { } int maxAcquisitions = getCampaignOptions().getMaxAcquisitions(); return maxAcquisitions <= 0 - || person.getAcquisitions() < maxAcquisitions; + || person.getAcquisitions() < maxAcquisitions; } /*** @@ -2873,7 +2877,7 @@ public String fixPart(IPartWork partWork, Person tech) { getAvailableAstechs(minutesUsed, usedOvertime), false); if ((null != partWork.getUnit()) && ((partWork.getUnit().getEntity() instanceof Dropship) - || (partWork.getUnit().getEntity() instanceof Jumpship))) { + || (partWork.getUnit().getEntity() instanceof Jumpship))) { helpMod = 0; } if (partWork.getShorthandedMod() < helpMod) { @@ -2930,9 +2934,9 @@ public String fixPart(IPartWork partWork, Person tech) { if ((getCampaignOptions().isDestroyByMargin() && (getCampaignOptions().getDestroyMargin() <= (target.getValue() - roll))) || (!getCampaignOptions().isDestroyByMargin() - // if an elite, primary tech and destroy by margin is NOT on - && ((tech.getExperienceLevel(this, false) == SkillType.EXP_ELITE) - || tech.getPrimaryRole().isVehicleCrew())) // For vessel crews + // if an elite, primary tech and destroy by margin is NOT on + && ((tech.getExperienceLevel(this, false) == SkillType.EXP_ELITE) + || tech.getPrimaryRole().isVehicleCrew())) // For vessel crews && (roll < target.getValue())) { tech.changeCurrentEdge(-1); roll = tech.isRightTechTypeFor(partWork) ? Compute.d6(2) : Utilities.roll3d6(); @@ -3100,7 +3104,7 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem contract.addPlayerMinorBreach(); addReport("Failure to deploy for " + scenario.getName() - + " resulted in defeat and a minor contract breach."); + + " resulted in defeat and a minor contract breach."); } } } @@ -3519,7 +3523,7 @@ public boolean newDay() { processNewDayPersonnel(); if (campaignOptions.isUseEducationModule()) { - EducationController.processNewDay(this); + processEducationNewDay(); } resetAstechMinutes(); @@ -3537,6 +3541,40 @@ public boolean newDay() { return true; } + /** + * This method checks if any students in the academy should graduate, + * and updates their attributes and status accordingly. + * If any students do graduate, it sends the graduation information to autoAwards. + */ + private void processEducationNewDay() { + List graduatingPersonnel = new ArrayList<>(); + HashMap> academyAttributesMap = new HashMap<>(); + + + for (Person person : getStudents()) { + List individualAcademyAttributes = new ArrayList<>(); + + if (EducationController.processNewDay(this, person, false)) { + Academy academy = getAcademy(person.getEduAcademySet(), person.getEduAcademyNameInSet()); + + graduatingPersonnel.add(person.getId()); + + individualAcademyAttributes.add(academy.getEducationLevel(person)); + individualAcademyAttributes.add(academy.getType()); + individualAcademyAttributes.add(academy.getName()); + + academyAttributesMap.put(person.getId(), individualAcademyAttributes); + + person.changeStatus(this, getLocalDate(), PersonnelStatus.ACTIVE); + } + } + + if (!graduatingPersonnel.isEmpty()) { + AutoAwardsController autoAwardsController = new AutoAwardsController(); + autoAwardsController.PostGraduationController(this, graduatingPersonnel, academyAttributesMap); + } + } + public @Nullable Person getFlaggedCommander() { for (Person p : getPersonnel()) { if (p.isCommander()) { @@ -4108,7 +4146,7 @@ public void addFunds(final TransactionType type, final Money quantity, } public void removeFunds(final TransactionType type, final Money quantity, - @Nullable String description) { + @Nullable String description) { if ((description == null) || description.isEmpty()) { description = "Rich Uncle"; } @@ -5137,7 +5175,7 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, StringBuilder partAvailabilityLog = new StringBuilder(); partAvailabilityLog.append("Part Rating Level: " + partAvailability) - .append("(" + EquipmentType.ratingNames[partAvailability] + ")"); + .append("(" + EquipmentType.ratingNames[partAvailability] + ")"); /* * Even if we can acquire Clan parts, they have a minimum availability of F for @@ -5191,17 +5229,17 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition, if (((getGameYear() < 2950) || (getGameYear() > 3040)) && (acquisition instanceof Armor || acquisition instanceof MissingMekActuator - || acquisition instanceof mekhq.campaign.parts.MissingMekCockpit - || acquisition instanceof mekhq.campaign.parts.MissingMekLifeSupport - || acquisition instanceof mekhq.campaign.parts.MissingMekLocation - || acquisition instanceof mekhq.campaign.parts.MissingMekSensor)) { + || acquisition instanceof mekhq.campaign.parts.MissingMekCockpit + || acquisition instanceof mekhq.campaign.parts.MissingMekLifeSupport + || acquisition instanceof mekhq.campaign.parts.MissingMekLocation + || acquisition instanceof mekhq.campaign.parts.MissingMekSensor)) { partAvailability--; partAvailabilityLog.append("(Mek part prior to 2950 or after 3040): - 1"); } int AtBPartsAvailability = findAtBPartsAvailabilityLevel(acquisition, null); partAvailabilityLog.append("; Total part availability: " + partAvailability) - .append("; Current campaign availability: " + AtBPartsAvailability); + .append("; Current campaign availability: " + AtBPartsAvailability); if (partAvailability > AtBPartsAvailability) { return new TargetRoll(TargetRoll.IMPOSSIBLE, partAvailabilityLog.toString()); } @@ -5491,9 +5529,9 @@ public int getMedicsPerDoctor() { public int getNumberMedics() { return getMedicPool() + Math.toIntExact(getActivePersonnel().stream() - .filter(p -> (p.getPrimaryRole().isMedic() || p.getSecondaryRole().isMedic()) - && !p.isDeployed()) - .count()); + .filter(p -> (p.getPrimaryRole().isMedic() || p.getSecondaryRole().isMedic()) + && !p.isDeployed()) + .count()); } public boolean requiresAdditionalMedics() { @@ -6730,8 +6768,8 @@ public void initAtB(boolean newCampaign) { if (!newCampaign) { /* - * Switch all contracts to AtBContract's - */ + * Switch all contracts to AtBContract's + */ for (Map.Entry me : missions.entrySet()) { Mission m = me.getValue(); if (m instanceof Contract && !(m instanceof AtBContract)) { @@ -6740,9 +6778,9 @@ public void initAtB(boolean newCampaign) { } /* - * Go through all the personnel records and assume the earliest date is the date - * the unit was founded. - */ + * Go through all the personnel records and assume the earliest date is the date + * the unit was founded. + */ LocalDate founding = null; for (Person p : getPersonnel()) { for (LogEntry e : p.getPersonnelLog()) { @@ -6752,11 +6790,11 @@ public void initAtB(boolean newCampaign) { } } /* - * Go through the personnel records again and assume that any person who joined - * the unit on the founding date is one of the founding members. Also assume - * that MWs assigned to a non-Assault 'Mech on the date they joined came with - * that 'Mech (which is a less certain assumption) - */ + * Go through the personnel records again and assume that any person who joined + * the unit on the founding date is one of the founding members. Also assume + * that MWs assigned to a non-Assault 'Mech on the date they joined came with + * that 'Mech (which is a less certain assumption) + */ for (Person p : getPersonnel()) { LocalDate join = null; for (LogEntry e : p.getPersonnelLog()) { @@ -6823,8 +6861,8 @@ public boolean checkOverDueLoans() { "You have overdue loan payments totaling " + overdueAmount.toAmountAndSymbolString() + "\nYou must deal with these payments before advancing the day.\nHere are some options:\n - Sell off equipment to generate funds.\n - Pay off the collateral on the loan.\n - Default on the loan.\n - Just cheat and remove the loan via GM mode.", - "Overdue Loan Payments", - JOptionPane.WARNING_MESSAGE); + "Overdue Loan Payments", + JOptionPane.WARNING_MESSAGE); return true; } return false; @@ -6846,7 +6884,7 @@ public boolean checkYearlyRetirements() { if (!getCampaignOptions().getRandomRetirementMethod().isNone() && getCampaignOptions().isUseYearEndRandomRetirement() && (ChronoUnit.DAYS.between(getRetirementDefectionTracker().getLastRetirementRoll(), getLocalDate()) - == getRetirementDefectionTracker().getLastRetirementRoll().lengthOfYear())) { + == getRetirementDefectionTracker().getLastRetirementRoll().lengthOfYear())) { // FIXME : Localize Object[] options = { "Show Retirement Dialog", "Not Now" }; return JOptionPane.YES_OPTION == JOptionPane.showOptionDialog(null, diff --git a/MekHQ/src/mekhq/campaign/CampaignOptions.java b/MekHQ/src/mekhq/campaign/CampaignOptions.java index 8220ec1ad5..698ce98038 100644 --- a/MekHQ/src/mekhq/campaign/CampaignOptions.java +++ b/MekHQ/src/mekhq/campaign/CampaignOptions.java @@ -266,6 +266,7 @@ public static String getTransitUnitName(final int unit) { private boolean enableSkillAwards; private boolean enableTheatreOfWarAwards; private boolean enableTimeAwards; + private boolean enableTrainingAwards; private boolean enableMiscAwards; //endregion Personnel Tab @@ -335,7 +336,6 @@ public static String getTransitUnitName(final int unit) { // Education private boolean useEducationModule; - private boolean eduEnableAutoAwardsIntegration; private Integer maximumJumpCount; private boolean useTruebornTravelException; private boolean enableLocalAcademies; @@ -759,6 +759,7 @@ public CampaignOptions() { setEnableSkillAwards(true); setEnableTheatreOfWarAwards(true); setEnableTimeAwards(true); + setEnableTrainingAwards(true); setEnableMiscAwards(true); //endregion Personnel Tab @@ -845,7 +846,6 @@ public CampaignOptions() { // Education setUseEducationModule(true); - setEduEnableAutoAwardsIntegration(true); setMaximumJumpCount(5); setUseTruebornTravelException(true); setEnableLocalAcademies(true); @@ -2281,16 +2281,6 @@ public void setUseEducationModule(boolean useEducationModule) { this.useEducationModule = useEducationModule; } - - public boolean isEduEnableAutoAwardsIntegration() { - return eduEnableAutoAwardsIntegration; - } - - - public void setEduEnableAutoAwardsIntegration(boolean eduEnableAutoAwardsIntegration) { - this.eduEnableAutoAwardsIntegration = eduEnableAutoAwardsIntegration; - } - public Integer getMaximumJumpCount() { return maximumJumpCount; } @@ -2716,6 +2706,14 @@ public void setEnableTimeAwards(final boolean enableTimeAwards) { this.enableTimeAwards = enableTimeAwards; } + public boolean isEnableTrainingAwards() { + return enableTrainingAwards; + } + + public void setEnableTrainingAwards(final boolean enableTrainingAwards) { + this.enableTrainingAwards = enableTrainingAwards; + } + public boolean isEnableMiscAwards() { return enableMiscAwards; } @@ -4188,6 +4186,7 @@ public void writeToXml(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "enableSkillAwards", isEnableSkillAwards()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "enableTheatreOfWarAwards", isEnableTheatreOfWarAwards()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "enableTimeAwards", isEnableTimeAwards()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "enableTrainingAwards", isEnableTrainingAwards()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "enableMiscAwards", isEnableMiscAwards()); //endregion Awards //endregion Personnel Tab @@ -4272,7 +4271,6 @@ public void writeToXml(final PrintWriter pw, int indent) { //region Education MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useEducationModule", isUseEducationModule()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "eduEnableAutoAwardsIntegration", isEduEnableAutoAwardsIntegration()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "maximumJumpCount", getMaximumJumpCount()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useTruebornTravelException", isUseTruebornTravelException()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "enableLocalAcademies", isEnableLocalAcademies()); @@ -4487,7 +4485,7 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } else if (wn2.getNodeName().equalsIgnoreCase("logMaintenance")) { retVal.logMaintenance = Boolean.parseBoolean(wn2.getTextContent()); - //region Mass Repair / Mass Salvage + //region Mass Repair / Mass Salvage } else if (wn2.getNodeName().equalsIgnoreCase("mrmsUseRepair") || wn2.getNodeName().equalsIgnoreCase("massRepairUseRepair")) { // Legacy, 0.49.12 removal retVal.setMRMSUseRepair(Boolean.parseBoolean(wn2.getTextContent().trim())); @@ -4518,8 +4516,8 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } else if (wn2.getNodeName().equalsIgnoreCase("mrmsOptions") || wn2.getNodeName().equalsIgnoreCase("massRepairOptions")) { // Legacy, 0.49.12 removal retVal.setMRMSOptions(MRMSOption.parseListFromXML(wn2, version)); - //endregion Mass Repair / Mass Salvage - //endregion Repair and Maintenance Tab + //endregion Mass Repair / Mass Salvage + //endregion Repair and Maintenance Tab } else if (wn2.getNodeName().equalsIgnoreCase("useFactionForNames")) { retVal.setUseOriginFactionForNames(Boolean.parseBoolean(wn2.getTextContent().trim())); @@ -4678,8 +4676,8 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } else if (wn2.getNodeName().equalsIgnoreCase("maxAcquisitions")) { retVal.maxAcquisitions = Integer.parseInt(wn2.getTextContent().trim()); - //region Personnel Tab - //region General Personnel + //region Personnel Tab + //region General Personnel } else if (wn2.getNodeName().equalsIgnoreCase("useTactics")) { retVal.setUseTactics(Boolean.parseBoolean(wn2.getTextContent())); } else if (wn2.getNodeName().equalsIgnoreCase("useInitBonus") // Legacy - 0.49.1 Removal @@ -4716,9 +4714,9 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setDisplayScenarioLog(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("displayKillRecord")) { retVal.setDisplayKillRecord(Boolean.parseBoolean(wn2.getTextContent().trim())); - //endregion General Personnel + //endregion General Personnel - //region Expanded Personnel Information + //region Expanded Personnel Information } else if (wn2.getNodeName().equalsIgnoreCase("useTimeInService")) { retVal.setUseTimeInService(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("timeInServiceDisplayFormat")) { @@ -4741,9 +4739,9 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setAdminExperienceLevelIncludeNegotiation(Boolean.parseBoolean(wn2.getTextContent())); } else if (wn2.getNodeName().equalsIgnoreCase("adminExperienceLevelIncludeScrounge")) { retVal.setAdminExperienceLevelIncludeScrounge(Boolean.parseBoolean(wn2.getTextContent())); - //endregion Expanded Personnel Information + //endregion Expanded Personnel Information - //region Medical + //region Medical } else if (wn2.getNodeName().equalsIgnoreCase("useAdvancedMedical")) { retVal.setUseAdvancedMedical(Boolean.parseBoolean(wn2.getTextContent())); } else if (wn2.getNodeName().equalsIgnoreCase("healWaitingPeriod")) { @@ -4758,9 +4756,9 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setUseRandomHitsForVehicles(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("tougherHealing")) { retVal.setTougherHealing(Boolean.parseBoolean(wn2.getTextContent().trim())); - //endregion Medical + //endregion Medical - //region Prisoners + //region Prisoners } else if (wn2.getNodeName().equalsIgnoreCase("prisonerCaptureStyle")) { retVal.setPrisonerCaptureStyle(PrisonerCaptureStyle.valueOf(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("defaultPrisonerStatus")) { @@ -4780,18 +4778,18 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setUseAtBPrisonerDefection(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("useAtBPrisonerRansom")) { retVal.setUseAtBPrisonerRansom(Boolean.parseBoolean(wn2.getTextContent().trim())); - //endregion Prisoners + //endregion Prisoners - //region Dependent + //region Dependent } else if (wn2.getNodeName().equalsIgnoreCase("randomDependentMethod")) { retVal.setRandomDependentMethod(RandomDependentMethod.valueOf(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("useRandomDependentAddition")) { retVal.setUseRandomDependentAddition(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("useRandomDependentRemoval")) { retVal.setUseRandomDependentRemoval(Boolean.parseBoolean(wn2.getTextContent().trim())); - //endregion Dependent + //endregion Dependent - //region Salary + //region Salary } else if (wn2.getNodeName().equalsIgnoreCase("salaryAntiMekMultiplier")) { retVal.setSalaryAntiMekMultiplier(Double.parseDouble(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("salarySpecialistInfantryMultiplier")) { @@ -4854,12 +4852,14 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setEnableTheatreOfWarAwards(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("enableTimeAwards")) { retVal.setEnableTimeAwards(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("enableTrainingAwards")) { + retVal.setEnableTrainingAwards(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("enableMiscAwards")) { retVal.setEnableMiscAwards(Boolean.parseBoolean(wn2.getTextContent().trim())); - //endregion Awards - //endregion Personnel Tab + //endregion Awards + //endregion Personnel Tab - //region Life Paths Tab + //region Life Paths Tab //region Personnel Randomization } else if (wn2.getNodeName().equalsIgnoreCase("useDylansRandomXP")) { retVal.setUseDylansRandomXP(Boolean.parseBoolean(wn2.getTextContent().trim())); @@ -5021,8 +5021,6 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve //region Education } else if (wn2.getNodeName().equalsIgnoreCase("useEducationModule")) { retVal.setUseEducationModule(Boolean.parseBoolean(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("eduEnableAutoAwardsIntegration")) { - retVal.setEduEnableAutoAwardsIntegration(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("maximumJumpCount")) { retVal.setMaximumJumpCount(Integer.parseInt(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("useTruebornTravelException")) { @@ -5146,9 +5144,9 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } } //endregion Death - //endregion Life Paths Tab + //endregion Life Paths Tab - //region Finances Tab + //region Finances Tab } else if (wn2.getNodeName().equalsIgnoreCase("payForParts")) { retVal.payForParts = Boolean.parseBoolean(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("payForRepairs")) { @@ -5186,7 +5184,7 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } else if (wn2.getNodeName().equalsIgnoreCase("newFinancialYearFinancesToCSVExport")) { retVal.newFinancialYearFinancesToCSVExport = Boolean.parseBoolean(wn2.getTextContent().trim()); - //region Price Multipliers + //region Price Multipliers } else if (wn2.getNodeName().equalsIgnoreCase("commonPartPriceMultiplier")) { retVal.setCommonPartPriceMultiplier(Double.parseDouble(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("innerSphereUnitPriceMultiplier")) { @@ -5214,11 +5212,11 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setUnrepairablePartsValueMultiplier(Double.parseDouble(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("cancelledOrderRefundMultiplier")) { retVal.setCancelledOrderRefundMultiplier(Double.parseDouble(wn2.getTextContent().trim())); - //endregion Price Multipliers - //endregion Finances Tab + //endregion Price Multipliers + //endregion Finances Tab - //region Markets Tab - //region Personnel Market + //region Markets Tab + //region Personnel Market } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketName")) { retVal.setPersonnelMarketName(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketReportRefresh")) { @@ -5239,9 +5237,9 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketDylansWeight")) { retVal.setPersonnelMarketDylansWeight(Double.parseDouble(wn2.getTextContent().trim())); - //endregion Personnel Market + //endregion Personnel Market - //region Unit Market + //region Unit Market } else if (wn2.getNodeName().equalsIgnoreCase("unitMarketMethod")) { retVal.setUnitMarketMethod(UnitMarketMethod.valueOf(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("unitMarketRegionalMechVariations")) { @@ -5250,9 +5248,9 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setInstantUnitMarketDelivery(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("unitMarketReportRefresh")) { retVal.setUnitMarketReportRefresh(Boolean.parseBoolean(wn2.getTextContent().trim())); - //endregion Unit Market + //endregion Unit Market - //region Contract Market + //region Contract Market } else if (wn2.getNodeName().equalsIgnoreCase("contractMarketMethod")) { retVal.setContractMarketMethod(ContractMarketMethod.valueOf(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("contractSearchRadius") @@ -5264,24 +5262,24 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setContractMarketReportRefresh(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("contractMaxSalvagePercentage")) { retVal.setContractMaxSalvagePercentage(Integer.parseInt(wn2.getTextContent().trim())); - //endregion Contract Market - //endregion Markets Tab + //endregion Contract Market + //endregion Markets Tab - //region RATs Tab + //region RATs Tab } else if (wn2.getNodeName().equals("useStaticRATs")) { retVal.setUseStaticRATs(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("rats")) { retVal.setRATs(MHQXMLUtility.unEscape(wn2.getTextContent().trim()).split(",")); } else if (wn2.getNodeName().equals("ignoreRATEra")) { retVal.setIgnoreRATEra(Boolean.parseBoolean(wn2.getTextContent().trim())); - //endregion RATs Tab + //endregion RATs Tab - //region AtB Tab + //region AtB Tab } else if (wn2.getNodeName().equalsIgnoreCase("skillLevel")) { retVal.setSkillLevel(version.isLowerThan("0.49.12") ? Skills.SKILL_LEVELS[Integer.parseInt(wn2.getTextContent().trim()) + 1] : SkillLevel.valueOf(wn2.getTextContent().trim())); - //endregion AtB Tab + //endregion AtB Tab } else if (wn2.getNodeName().equalsIgnoreCase("phenotypeProbabilities")) { String[] values = wn2.getTextContent().split(","); @@ -5379,8 +5377,8 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } else if (wn2.getNodeName().equalsIgnoreCase("spaUpgradeIntensity")) { retVal.setSpaUpgradeIntensity(Integer.parseInt(wn2.getTextContent().trim())); - //region Legacy - // Removed in 0.49.* + //region Legacy + // Removed in 0.49.* } else if (wn2.getNodeName().equalsIgnoreCase("salaryXPMultiplier")) { // Legacy, 0.49.12 removal String[] values = wn2.getTextContent().split(","); for (int i = 0; i < values.length; i++) { @@ -5482,7 +5480,7 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } else if (wn2.getNodeName().equalsIgnoreCase("canceledOrderReimbursement")) { // Legacy - 0.49.3 Removal retVal.setCancelledOrderRefundMultiplier(Double.parseDouble(wn2.getTextContent().trim())); - // Removed in 0.47.* + // Removed in 0.47.* } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketType")) { // Legacy retVal.setPersonnelMarketName(PersonnelMarket.getTypeName(Integer.parseInt(wn2.getTextContent().trim()))); } else if (wn2.getNodeName().equalsIgnoreCase("useAtBCapture")) { // Legacy diff --git a/MekHQ/src/mekhq/campaign/personnel/Award.java b/MekHQ/src/mekhq/campaign/personnel/Award.java index b0ea481435..dffd63694d 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Award.java +++ b/MekHQ/src/mekhq/campaign/personnel/Award.java @@ -38,13 +38,13 @@ @XmlAccessorType(value = XmlAccessType.FIELD) public class Award implements Comparable { @XmlElement(name = "name") - private String name; + private String name = "ERROR: NO NAME"; @XmlElement(name = "description") - private String description; + private String description = ""; @XmlElement(name = "group") - private String group = "null"; + private String group = ""; @XmlElement(name = "medal") private List medals; @@ -62,16 +62,16 @@ public class Award implements Comparable { private int edge = 0; @XmlElement(name = "qty") - private int qty; + private int qty = 0; @XmlElement(name = "item") - private String item; + private String item = ""; @XmlElement(name = "size") - private String size; + private String size = ""; @XmlElement(name = "range") - private String range; + private String range = ""; @XmlElement(name = "stackable") private boolean stackable = false; diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index ff8ea11b86..2ee0ed6337 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -196,6 +196,7 @@ public class Person { private String eduAcademyFaction; private String eduAcademySystem; private int eduCourseIndex; + private int eduEducationStage; private int eduDaysOfTravelToAcademy; private int eduDaysOfEducation; private int eduDaysOfTravelFromAcademy; @@ -342,6 +343,7 @@ public Person(final String preNominal, final String givenName, final String surn eduAcademyName = null; eduAcademySystem = null; eduCourseIndex = 0; + eduEducationStage = 0; eduDaysOfTravelToAcademy = 0; eduDaysOfEducation = 0; eduDaysOfTravelFromAcademy = 0; @@ -1033,6 +1035,7 @@ public void changeStatus(final Campaign campaign, final LocalDate today, this.setEduAcademyNameInSet(null); this.setEduAcademySystem(null); this.setEduCourseIndex(0); + this.setEduEducationStage(0); this.setEduDaysOfTravelToAcademy(0); this.setEduDaysOfTravelFromAcademy(0); this.setEduDaysOfTravel(0); @@ -1364,6 +1367,14 @@ public void setEduCourseIndex(final Integer eduCourseIndex) { this.eduCourseIndex = eduCourseIndex; } + public int getEduEducationStage() { + return eduEducationStage; + } + + public void setEduEducationStage(final int eduEducationStage) { + this.eduEducationStage = eduEducationStage; + } + public String getEduAcademyName() { return eduAcademyName; } @@ -1696,6 +1707,10 @@ public void writeToXML(final PrintWriter pw, int indent, final Campaign campaign MHQXMLUtility.writeSimpleXMLTag(pw, indent, "eduCourseIndex", eduCourseIndex); } + if (eduEducationStage != 0) { + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "eduEducationStage", eduEducationStage); + } + if (eduDaysOfEducation != 0) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "eduDaysOfEducation", eduDaysOfEducation); } @@ -2018,6 +2033,8 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio retVal.eduAcademyFaction = String.valueOf(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("eduCourseIndex")) { retVal.eduCourseIndex = Integer.parseInt(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("eduEducationStage")) { + retVal.eduEducationStage = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("eduDaysOfEducation")) { retVal.eduDaysOfEducation = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("clanPersonnel") diff --git a/MekHQ/src/mekhq/campaign/personnel/autoAwards/AutoAwardsController.java b/MekHQ/src/mekhq/campaign/personnel/autoAwards/AutoAwardsController.java index a9c16071bf..239868077a 100644 --- a/MekHQ/src/mekhq/campaign/personnel/autoAwards/AutoAwardsController.java +++ b/MekHQ/src/mekhq/campaign/personnel/autoAwards/AutoAwardsController.java @@ -1,5 +1,6 @@ package mekhq.campaign.personnel.autoAwards; +import megamek.common.annotations.Nullable; import mekhq.campaign.Campaign; import mekhq.campaign.Kill; import mekhq.campaign.mission.AtBContract; @@ -45,12 +46,12 @@ public void ManualController(Campaign c) { buildAwardLists(0); - Collection personnel = getPersonnel(); + List personnel = getPersonnel(); // we have to do multiple isEmpty() checks as, at any point in the removal process, we could end up with null personnel if (!personnel.isEmpty()) { // This is the main workhorse function - ProcessAwards(personnel, false); + ProcessAwards(personnel, false, null); } else { LogManager.getLogger().info("AutoAwards found no personnel, skipping the Award Ceremony"); } @@ -73,12 +74,12 @@ public void PostMissionController(Campaign c, Mission m, Boolean missionWasSucce buildAwardLists(1); - Collection personnel = getPersonnel(); + List personnel = getPersonnel(); // we have to do multiple isEmpty() checks as, at any point in the removal process, we could end up with null personnel if (!personnel.isEmpty()) { // This is the main workhorse function - ProcessAwards(personnel, missionWasSuccessful); + ProcessAwards(personnel, missionWasSuccessful, null); } else { LogManager.getLogger().info("AutoAwards found no personnel, skipping the Award Ceremony"); } @@ -142,18 +143,41 @@ public void PostScenarioController(Campaign c, int scenarioId, HashMap personnel, HashMap> academyAttributes) { + LogManager.getLogger().info("autoAwards (Mission Conclusion) has started"); + + campaign = c; + + buildAwardLists(3); + + // we have to do multiple isEmpty() checks as, at any point in the removal process, we could end up with null personnel + if (!personnel.isEmpty()) { + // This is the main workhorse function + ProcessAwards(personnel, false, academyAttributes); + } else { + LogManager.getLogger().info("AutoAwards found no personnel, skipping the Award Ceremony"); + } + + LogManager.getLogger().info("autoAwards (Mission Conclusion) has finished"); + } + /** * Builds a list of personnel autoAwards should process */ - private Collection getPersonnel() { - Collection personnel = campaign.getActivePersonnel() + private List getPersonnel() { + List personnel = campaign.getActivePersonnel() .stream() .map(Person::getId) .collect(Collectors.toList()); // if posthumous Awards are enabled, we add the relevant dead people if (campaign.getCampaignOptions().isIssuePosthumousAwards()) { - Collection deadPeople = campaign.getPersonnel() + List deadPeople = campaign.getPersonnel() .stream().filter(person -> person.getStatus().isDead()) .map(Person::getId) .collect(Collectors.toList()); @@ -177,7 +201,7 @@ private Collection getPersonnel() { * * @param personnel personnel to process */ - private void removeDependentsAndPrisoners(Collection personnel) { + private void removeDependentsAndPrisoners(List personnel) { if (!personnel.isEmpty()) { personnel.removeIf(person -> (campaign.getPerson(person).hasRole(PersonnelRole.DEPENDENT)) || (campaign.getPerson(person).getPrisonerStatus().isCurrentPrisoner())); @@ -270,8 +294,6 @@ private void buildAwardLists(int awardListCase) { ignoredAwards.add(award); } break; - // trainingAwards are not currently supported. - // We include them here for tracking purposes case "training": trainingAwards.add(award); break; @@ -282,12 +304,12 @@ private void buildAwardLists(int awardListCase) { } // These logs help users double-check that the number of awards found matches their records LogManager.getLogger().info("autoAwards found {} Kill Awards (excluding Mission & Scenario Kill Awards)", killAwards.size()); - LogManager.getLogger().info("autoAwards found {} Misc Awards", miscAwards.size()); LogManager.getLogger().info("autoAwards found {} Rank Awards", rankAwards.size()); LogManager.getLogger().info("autoAwards found {} Scenario Awards", scenarioAwards.size()); LogManager.getLogger().info("autoAwards found {} Skill Awards", skillAwards.size()); LogManager.getLogger().info("autoAwards found {} Time Awards", timeAwards.size()); LogManager.getLogger().info("autoAwards found {} Training Awards", trainingAwards.size()); + LogManager.getLogger().info("autoAwards found {} Misc Awards", miscAwards.size()); LogManager.getLogger().info("autoAwards is ignoring {} Awards", ignoredAwards.size()); break; @@ -373,8 +395,6 @@ private void buildAwardLists(int awardListCase) { ignoredAwards.add(award); } break; - // trainingAwards are not currently supported. - // We include them here for tracking purposes case "training": trainingAwards.add(award); break; @@ -385,14 +405,13 @@ private void buildAwardLists(int awardListCase) { } // These logs help users double-check that the number of awards found matches their records LogManager.getLogger().info("autoAwards found {} Kill Awards (excluding Scenario Kill Awards)", killAwards.size()); - LogManager.getLogger().info("autoAwards found {} Misc Awards", miscAwards.size()); LogManager.getLogger().info("autoAwards found {} Contract Awards", contractAwards.size()); LogManager.getLogger().info("autoAwards found {} Rank Awards", rankAwards.size()); LogManager.getLogger().info("autoAwards found {} Scenario Awards", scenarioAwards.size()); LogManager.getLogger().info("autoAwards found {} Skill Awards", skillAwards.size()); LogManager.getLogger().info("autoAwards found {} TheatreOfWar Awards", theatreOfWarAwards.size()); LogManager.getLogger().info("autoAwards found {} Time Awards", timeAwards.size()); - LogManager.getLogger().info("autoAwards found {} Training Awards", trainingAwards.size()); + LogManager.getLogger().info("autoAwards found {} Misc Awards", miscAwards.size()); LogManager.getLogger().info("autoAwards is ignoring {} Awards", ignoredAwards.size()); break; @@ -414,6 +433,13 @@ private void buildAwardLists(int awardListCase) { injuryAwards.add(award); } break; + case "misc": + if (campaign.getCampaignOptions().isEnableMiscAwards()) { + miscAwards.add(award); + } else { + ignoredAwards.add(award); + } + break; default: ignoredAwards.add(award); } @@ -421,6 +447,29 @@ private void buildAwardLists(int awardListCase) { LogManager.getLogger().info("autoAwards found {} Scenario Kill Awards (excluding Mission & Lifetime Kill Awards)", killAwards.size()); LogManager.getLogger().info("autoAwards found {} Injury Awards", injuryAwards.size()); + LogManager.getLogger().info("autoAwards found {} Misc Awards", miscAwards.size()); + LogManager.getLogger().info("autoAwards is ignoring {} Awards", ignoredAwards.size()); + + break; + // post-graduation + case 3: + for (Award award : awards) { + switch (award.getItem().toLowerCase().replaceAll("\\s", "")) { + case "divider": + break; + case "training": + if (campaign.getCampaignOptions().isEnableTrainingAwards()) { + + trainingAwards.add(award); + } + break; + default: + ignoredAwards.add(award); + } + } + + LogManager.getLogger().info("autoAwards found {} Training Awards", trainingAwards.size()); + LogManager.getLogger().info("autoAwards is ignoring {} Awards", ignoredAwards.size()); break; default: @@ -438,10 +487,11 @@ private void buildAwardLists(int awardListCase) { /** * Process the awards for the given personnel. * - * @param personnel the collection of personnel to process awards for + * @param personnel the List of personnel to process awards for * @param missionWasSuccessful true if the mission was successful, false otherwise + * @param academyAttributes a map of academy attributes, null if not processing graduation awards */ - private void ProcessAwards(Collection personnel, Boolean missionWasSuccessful) { + private void ProcessAwards(List personnel, Boolean missionWasSuccessful, @Nullable HashMap> academyAttributes) { Map>> allAwardData = new HashMap<>(); Map> processedData; int allAwardDataKey = 0; @@ -537,6 +587,14 @@ private void ProcessAwards(Collection personnel, Boolean missionWasSuccess } } + if ((!trainingAwards.isEmpty()) && (academyAttributes != null)) { + processedData = TrainingAwardsManager(personnel, academyAttributes); + + if (processedData != null) { + allAwardData.put(allAwardDataKey, processedData); + } + } + if (!allAwardData.isEmpty()) { AutoAwardsDialog autoAwardsDialog = new AutoAwardsDialog(campaign, allAwardData, 0); autoAwardsDialog.setVisible(true); @@ -550,7 +608,7 @@ private void ProcessAwards(Collection personnel, Boolean missionWasSuccess * * @param personnel the personnel to be processed */ - private Map> ContractAwardsManager(Collection personnel) { + private Map> ContractAwardsManager(List personnel) { Map> awardData = new HashMap<>(); int awardDataKey = 0; @@ -589,7 +647,7 @@ private Map> ContractAwardsManager(Collection person * * @param personnel the personnel to be processed */ - private Map> FactionHunterAwardsManager(Collection personnel) { + private Map> FactionHunterAwardsManager(List personnel) { Map> awardData = new HashMap<>(); int awardDataKey = 0; @@ -660,7 +718,7 @@ private Map> InjuryAwardsManager(HashMap pe * * @param personnel the personnel to be processed */ - private Map> KillAwardsManager(Collection personnel) { + private Map> KillAwardsManager(List personnel) { Map> awardData = new HashMap<>(); int awardDataKey = 0; @@ -733,7 +791,7 @@ private Map> ScenarioKillAwardsManager(List personne /** * This method is the manager for processing miscellaneous awards. * - * @param personnel the collection of personnel to be processed for awards + * @param personnel the List of personnel to be processed for awards * @param missionWasSuccessful a boolean indicating if the mission was successful * @param scenarioId the scenario just completed, or -1 if autoAwards was not triggered by scenario completion * @return a map containing the award data, or null if no awards are applicable @@ -779,7 +837,7 @@ private Map> MiscAwardsManager(HashMap pers * * @param personnel the personnel to be processed */ - private Map> RankAwardsManager(Collection personnel) { + private Map> RankAwardsManager(List personnel) { Map> awardData = new HashMap<>(); int awardDataKey = 0; @@ -813,7 +871,7 @@ private Map> RankAwardsManager(Collection personnel) * * @param personnel the personnel to be processed */ - private Map> ScenarioAwardsManager(Collection personnel) { + private Map> ScenarioAwardsManager(List personnel) { Map> awardData = new HashMap<>(); int awardDataKey = 0; @@ -847,7 +905,7 @@ private Map> ScenarioAwardsManager(Collection person * * @param personnel the personnel to be processed */ - private Map> SkillAwardsManager(Collection personnel) { + private Map> SkillAwardsManager(List personnel) { Map> awardData = new HashMap<>(); int awardDataKey = 0; @@ -881,7 +939,7 @@ private Map> SkillAwardsManager(Collection personnel * * @param personnel the personnel to be processed */ - private Map> TheatreOfWarAwardsManager(Collection personnel) { + private Map> TheatreOfWarAwardsManager(List personnel) { Map> awardData = new HashMap<>(); int awardDataKey = 0; @@ -915,7 +973,7 @@ private Map> TheatreOfWarAwardsManager(Collection pe * * @param personnel the personnel to be processed */ - private Map> TimeAwardsManager(Collection personnel) { + private Map> TimeAwardsManager(List personnel) { Map> awardData = new HashMap<>(); int awardDataKey = 0; @@ -940,7 +998,41 @@ private Map> TimeAwardsManager(Collection personnel) if (!awardData.isEmpty()) { return awardData; } else { - LogManager.getLogger().info("Returning: null"); + return null; + } + } + + /** + * This is the manager for this type of award, processing eligibility and preparing awardData + * + * @param personnel the personnel to be processed + * @param academyAttributes the academy attributes mapped to the personnel being processed + */ + private Map> TrainingAwardsManager(List personnel, HashMap> academyAttributes) { + Map> awardData = new HashMap<>(); + int awardDataKey = 0; + + for (UUID person : personnel) { + Map> data; + try { + data = TrainingAwards.TrainingAwardsProcessor(campaign, person, academyAttributes.get(person), trainingAwards); + } catch (Exception e) { + data = null; + LogManager.getLogger().info("{} is not eligible for any Training Awards.", campaign.getPerson(person).getFullName()); + } + + if (data != null) { + for (Integer dataKey : data.keySet()) { + awardData.put(awardDataKey, data.get(dataKey)); + + awardDataKey++; + } + } + } + + if (!awardData.isEmpty()) { + return awardData; + } else { return null; } } diff --git a/MekHQ/src/mekhq/campaign/personnel/autoAwards/TrainingAwards.java b/MekHQ/src/mekhq/campaign/personnel/autoAwards/TrainingAwards.java new file mode 100644 index 0000000000..b9433113f2 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/personnel/autoAwards/TrainingAwards.java @@ -0,0 +1,105 @@ +package mekhq.campaign.personnel.autoAwards; + +import mekhq.campaign.Campaign; +import mekhq.campaign.personnel.Award; +import mekhq.campaign.personnel.Person; +import org.apache.logging.log4j.LogManager; + +import java.util.*; + +public class TrainingAwards { + /** + * This function loops through Training Awards, checking whether the person is eligible to receive each type of award. + * + * @param campaign the campaign to be processed + * @param person the person to check award eligibility for + * @param academyAttributes the attributes of the person's academy (education level, type, name) + * @param awards the awards to be processed (should only include awards where item == Training) + * @return a mapping of award IDs to lists of eligible awards for the given person + */ + public static Map> TrainingAwardsProcessor(Campaign campaign, UUID person, List academyAttributes, List awards) { + Person student = campaign.getPerson(person); + List eligibleAwards = new ArrayList<>(); + + int academyEducationLevel; + int academyType; + String academyName; + + // We start by prepping the data we're going to be comparing against and ensuring it's all valid + try { + academyEducationLevel = (int) academyAttributes.get(0); + } catch (ClassCastException e) { + LogManager.getLogger().warn("{} has invalid academyEducationLevel value '{}'. Aborting.", + student.getFullName(), academyAttributes.get(0).toString()); + + return AutoAwardsController.prepareAwardData(person, eligibleAwards); + } + + try { + academyType = (int) academyAttributes.get(1); + } catch (ClassCastException e) { + LogManager.getLogger().warn("{} has invalid academyType value '{}'. Aborting.", + student.getFullName(), academyAttributes.get(1).toString()); + + return AutoAwardsController.prepareAwardData(person, eligibleAwards); + } + + try { + academyName = academyAttributes.get(2).toString(); + } catch (ClassCastException e) { + LogManager.getLogger().warn("{} has invalid academyName value '{}'. Aborting.", + student.getFullName(), academyAttributes.get(2).toString()); + + return AutoAwardsController.prepareAwardData(person, eligibleAwards); + } + + // Then we process the individual awards + for (Award award : awards) { + int requiredEducationLevel; + int requiredType; + String requiredAcademyName; + + try { + requiredEducationLevel = award.getQty(); + } catch (Exception e) { + LogManager.getLogger().warn("Award {} from the {} set has an invalid qty value {}", + award.getName(), award.getSet(), award.getQty()); + continue; + } + + try { + requiredType = Integer.parseInt(award.getSize()); + } catch (Exception e) { + LogManager.getLogger().warn("Award {} from the {} set has an invalid size value {}", + award.getName(), award.getSet(), award.getSize()); + continue; + } + + try { + requiredAcademyName = award.getRange(); + } catch (Exception e) { + LogManager.getLogger().warn("Award {} from the {} set has an invalid range value {}", + award.getName(), award.getSet(), award.getRange()); + continue; + } + + if (award.canBeAwarded(campaign.getPerson(person))) { + if ((requiredEducationLevel != 0) && (requiredEducationLevel < academyEducationLevel)) { + eligibleAwards.add(award); + continue; + } + + if ((requiredType != 0) && (requiredType == academyType)) { + eligibleAwards.add(award); + continue; + } + + if (Objects.equals(requiredAcademyName, academyName)) { + eligibleAwards.add(award); + } + } + } + + return AutoAwardsController.prepareAwardData(person, eligibleAwards); + } +} diff --git a/MekHQ/src/mekhq/campaign/personnel/education/Academy.java b/MekHQ/src/mekhq/campaign/personnel/education/Academy.java index 2f8c8e9dd4..7703ff7ca8 100644 --- a/MekHQ/src/mekhq/campaign/personnel/education/Academy.java +++ b/MekHQ/src/mekhq/campaign/personnel/education/Academy.java @@ -41,6 +41,18 @@ public class Academy implements Comparable { @XmlElement(name = "name") private String name = "Error: Name Missing"; + // 0: none + // 1: high school + // 2: college + // 3: university + // 4: military academy + // 5: basic training + // 6: nco academy + // 7: warrant officer academy + // 8: officer academy + @XmlElement(name = "type") + private int type = 0; + @XmlElement(name = "isMilitary") private Boolean isMilitary = false; @@ -137,6 +149,7 @@ public Academy() { * * @param set the set name of the academy * @param name the name of the academy + * @param type the type of academy (used by autoAwards) * @param isMilitary indicates if the academy is a military academy (true) or not (false) * @param promotion indicates the promotion rank earned for completing an academic course * @param isClan indicates if the academy is a Clan Sibko or Crèche (true) or not (false) @@ -163,7 +176,7 @@ public Academy() { * @param baseAcademicSkillLevel the base skill level provided by the academy * @param id the id number of the academy, used for sorting academies in mhq */ - public Academy(String set, String name, Boolean isMilitary, String promotion, Boolean isClan, Boolean isTrueborn, + public Academy(String set, String name, int type, Boolean isMilitary, String promotion, Boolean isClan, Boolean isTrueborn, Boolean isPrepSchool, String description, Integer factionDiscount, Boolean isFactionRestricted, String faction, List locationSystems, Boolean isLocal, Integer constructionYear, Integer destructionYear, Integer closureYear, Integer tuition, Integer durationDays, @@ -172,6 +185,7 @@ public Academy(String set, String name, Boolean isMilitary, String promotion, Bo Integer baseAcademicSkillLevel, Integer id) { this.set = set; this.name = name; + this.type = type; this.isMilitary = isMilitary; this.promotion = promotion; this.isClan = isClan; @@ -237,6 +251,24 @@ public void setName(final String name) { this.name = name; } + /** + * Gets the type of academy. + * + * @return The type of academy. + */ + public int getType() { + return type; + } + + /** + * Sets the type of academy. + * + * @param type the type to be set. + */ + public void setType(final int type) { + this.type = type; + } + /** * Checks if the academy is a military academy. * @@ -802,7 +834,7 @@ public boolean isQualified(Person person) { * @param person The person whose education level needs to be determined. * @return The education level of the qualification. */ - int getEducationLevel(Person person) { + public int getEducationLevel(Person person) { int educationLevel; if ((person.getEduHighestEducation() + educationLevelMin) >= educationLevelMax) { @@ -834,7 +866,7 @@ public Boolean isFactionConflict(Campaign campaign, Person person) { if (RandomFactionGenerator.getInstance().getFactionHints().isAtWarWith(person.getOriginFaction(), Factions.getInstance().getFaction(person.getEduAcademyFaction()), campaign.getLocalDate())) { return true; - // is there a conflict between academy faction & campaign faction? + // is there a conflict between academy faction & campaign faction? } else { return RandomFactionGenerator.getInstance().getFactionHints().isAtWarWith(campaign.getFaction(), Factions.getInstance().getFaction(person.getEduAcademyFaction()), campaign.getLocalDate()); diff --git a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java index 953328e640..37ddfce44b 100644 --- a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java +++ b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java @@ -67,8 +67,8 @@ public static void beginEducation(Campaign campaign, Person person, String acade if (academy.isClan()) { campaign.addReport(String.format(resources.getString("offToSchoolClan.text"), - person.getFullName(), - person.getEduAcademyName(), + person.getFullName(), + person.getEduAcademyName(), person.getEduDaysOfTravelToAcademy())); } else { campaign.addReport(String.format(resources.getString("offToSchool.text"), @@ -95,6 +95,7 @@ public static void enrollPerson(Campaign campaign, Person person, Academy academ // change status will wipe the academic information, so must always precede the setters person.changeStatus(campaign, campaign.getLocalDate(), PersonnelStatus.STUDENT); + person.setEduEducationStage(1); person.setEduAcademySet(academy.getSet()); person.setEduAcademyNameInSet(academy.getName()); person.setEduDaysOfEducation(academy.getDurationDays()); @@ -294,38 +295,44 @@ private static String generateTypeAdult(ResourceBundle resources) { } /** - * Process a new day in the campaign, updating the education status of each student. + * Processes a new day for a person in a campaign. * - * @param campaign the campaign object containing the students + * @param campaign the campaign in which the person is participating + * @param person the person for whom the new day is being processed + * @param ageBypass a flag indicating whether graduation age restrictions should be bypassed + * @return true if the new day was successfully processed, false otherwise */ - public static void processNewDay(Campaign campaign) { + public static boolean processNewDay(Campaign campaign, Person person, boolean ageBypass) { ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Education", MekHQ.getMHQOptions().getLocale()); + Academy academy = getAcademy(person.getEduAcademySet(), person.getEduAcademyNameInSet()); - for (Person person : campaign.getStudents()) { - Academy academy = getAcademy(person.getEduAcademySet(), person.getEduAcademyNameInSet()); - - // is person in transit to the institution? - if (journeyToAcademy(campaign, person, resources)) { - continue; - } - - // is person on campus and undergoing education? - Integer daysOfEducation = ongoingEducation(campaign, person, academy, resources); + int educationStage = person.getEduEducationStage(); - if (daysOfEducation == null) { - continue; - } + // is person in transit to the institution? + if (educationStage == 1) { + journeyToAcademy(campaign, person, resources); + return false; + } - // if education has concluded and the journey home hasn't started, we begin the journey - Integer daysOfTravelFrom = beginJourneyHome(campaign, person, academy, daysOfEducation, resources); + // is person on campus and undergoing education? + if (educationStage == 2) { + return ongoingEducation(campaign, person, academy, ageBypass, resources); + } - if (daysOfTravelFrom == null) { - continue; - } + // if education has concluded and the journey home hasn't started, we begin the journey + if (educationStage == 3) { + beginJourneyHome(campaign, person, academy, resources); + return false; + } - // if we reach this point it means Person is already in transit, so we continue their journey - processJourneyHome(campaign, person, daysOfTravelFrom); + // if we reach this point it means Person is already in transit, so we continue their journey + if (educationStage == 4) { + processJourneyHome(campaign, person); + return false; } + + LogManager.getLogger().error("Unexpected education stage: {}", educationStage); + return false; } /** @@ -336,33 +343,16 @@ public static void processNewDay(Campaign campaign) { * @param resources The resource bundle containing localized strings. * @return True if the person's journey to campus was processed, false otherwise. */ - private static boolean journeyToAcademy(Campaign campaign, Person person, ResourceBundle resources) { + private static void journeyToAcademy(Campaign campaign, Person person, ResourceBundle resources) { int daysOfTravelTo = person.getEduDaysOfTravelToAcademy(); - if (daysOfTravelTo > 0) { - person.setEduDaysOfTravelToAcademy(daysOfTravelTo - 1); + person.setEduDaysOfTravelToAcademy(daysOfTravelTo - 1); - // has Person just arrived? - if ((daysOfTravelTo - 1) == 0) { - campaign.addReport(person.getHyperlinkedName() + ' ' + resources.getString("arrived.text")); - } - - return true; + // has Person just arrived? + if ((daysOfTravelTo - 1) < 1) { + campaign.addReport(person.getHyperlinkedName() + ' ' + resources.getString("arrived.text")); + person.setEduEducationStage(2); } - return false; - } - - /** - * This method completes the journey to the campaign's academy for the given person. - * - * @param campaign The current campaign. - * @param person The individual traveling to the academy. - */ - public static void completeJourneyTo(Campaign campaign, Person person) { - ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Education", MekHQ.getMHQOptions().getLocale()); - - person.setEduDaysOfTravelToAcademy(0); - campaign.addReport(person.getHyperlinkedName() + ' ' + resources.getString("arrived.text")); } /** @@ -374,35 +364,41 @@ public static void completeJourneyTo(Campaign campaign, Person person) { * @param resources The resource bundle containing localized strings. * @return The remaining days of education for the person. */ - private static Integer ongoingEducation(Campaign campaign, Person person, Academy academy, ResourceBundle resources) { + private static boolean ongoingEducation(Campaign campaign, Person person, Academy academy, boolean ageBypass, ResourceBundle resources) { int daysOfEducation = person.getEduDaysOfEducation(); + boolean graduation = false; - if ((academy.isPrepSchool()) && (person.getEduDaysOfEducation() > 0)) { - if (person.getAge(campaign.getLocalDate()) >= academy.getAgeMax()) { + if (academy.isPrepSchool()) { + if ((person.getAge(campaign.getLocalDate()) >= academy.getAgeMax()) || (ageBypass)) { graduationPicker(campaign, person, academy, resources); person.setEduDaysOfEducation(0); + + graduation = true; } if (campaign.getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { processNewWeekChecks(campaign, academy, person, daysOfEducation, resources); } - - return null; - } else if (daysOfEducation > 0) { + } else { person.setEduDaysOfEducation(daysOfEducation - 1); + if (campaign.getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { + processNewWeekChecks(campaign, academy, person, daysOfEducation, resources); + } + if ((daysOfEducation - 1) < 1) { graduationPicker(campaign, person, academy, resources); - } - if (campaign.getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { - processNewWeekChecks(campaign, academy, person, daysOfEducation, resources); + graduation = true; } - return null; } - return daysOfEducation; + if (graduation) { + person.setEduDaysOfEducation(3); + } + + return graduation; } /** @@ -433,22 +429,6 @@ private static void graduationPicker(Campaign campaign, Person person, Academy a } } - /** - * Completes the education process for a person. - * - * @param campaign the campaign in which the education is taking place - * @param person the person whose education is being completed - */ - public static void completeEducation(Campaign campaign, Person person) { - ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Education", MekHQ.getMHQOptions().getLocale()); - - Academy academy = getAcademy(person.getEduAcademySet(), person.getEduAcademyNameInSet()); - - person.setEduDaysOfEducation(0); - - graduationPicker(campaign, person, academy, resources); - } - /** * This method begins the journey home for a person after their education. * @@ -459,36 +439,31 @@ public static void completeEducation(Campaign campaign, Person person) { * @return the number of travel days from the academy, or null if the person has no education days and no * previous travel days */ - private static Integer beginJourneyHome(Campaign campaign, Person person, Academy academy, Integer daysOfEducation, ResourceBundle resources) { + private static void beginJourneyHome(Campaign campaign, Person person, Academy academy, ResourceBundle resources) { int travelTime = 0; - if ((daysOfEducation == 0) && (person.getEduDaysOfTravelFromAcademy() == 0)) { - if ((academy.isClan()) && (academy.isPrepSchool()) && (person.getAge(campaign.getLocalDate()) < 10)) { - // we do this to deliberately create an infinite loop, where the player is pestered - // daily until the student is assigned to a Sibko - person.setEduDaysOfTravelFromAcademy(travelTime); - - campaign.addReport(person.getHyperlinkedName() + ' ' + resources.getString("creche.text")); + if ((academy.isClan()) && (academy.isPrepSchool())) { + // we do this to deliberately create an infinite loop, where the player is pestered + // daily until the student is assigned to a Sibko + person.setEduDaysOfTravelFromAcademy(travelTime); - return null; - } else if ((academy.isClan()) && (!academy.isLocal())) { - try { - travelTime = Math.max(2, campaign.getSimplifiedTravelTime(campaign.getFaction().getStartingPlanet(campaign, campaign.getLocalDate()))); - } catch (Exception e) { - travelTime = Math.max(2, campaign.getSimplifiedTravelTime(campaign.getSystemById("Strana Mechty"))); - } - } else { - travelTime = Math.max(2, campaign.getSimplifiedTravelTime(campaign.getSystemById(person.getEduAcademySystem()))); + campaign.addReport(person.getHyperlinkedName() + ' ' + resources.getString("creche.text")); + } else if ((academy.isClan()) && (academy.isTrueborn())) { + try { + travelTime = Math.max(2, campaign.getSimplifiedTravelTime(campaign.getFaction().getStartingPlanet(campaign, campaign.getLocalDate()))); + } catch (Exception e) { + travelTime = Math.max(2, campaign.getSimplifiedTravelTime(campaign.getSystemById("Strana Mechty"))); } + } else { + travelTime = Math.max(2, campaign.getSimplifiedTravelTime(campaign.getSystemById(person.getEduAcademySystem()))); + } - campaign.addReport(person.getHyperlinkedName() + ' ' - + String.format(resources.getString("returningFromSchool.text"), travelTime)); - - person.setEduDaysOfTravelFromAcademy(travelTime); + campaign.addReport(person.getHyperlinkedName() + ' ' + + String.format(resources.getString("returningFromSchool.text"), travelTime)); - return null; - } - return travelTime; + person.setEduDaysOfTravelFromAcademy(travelTime); + person.setEduDaysOfTravel(0); + person.setEduEducationStage(4); } /** @@ -496,20 +471,17 @@ private static Integer beginJourneyHome(Campaign campaign, Person person, Academ * * @param campaign the campaign the person is in * @param person the person whose journey home is being processed - * @param daysOfTravelFrom the number of days it takes for the person to travel from the campaign location to the unit */ - private static void processJourneyHome(Campaign campaign, Person person, Integer daysOfTravelFrom) { - int travelTime = 0; + private static void processJourneyHome(Campaign campaign, Person person) { + int travelTime; try { travelTime = Math.max(2, campaign.getSimplifiedTravelTime(campaign.getSystemById(person.getEduAcademySystem()))); - if (travelTime != daysOfTravelFrom) { + if (travelTime != person.getEduDaysOfTravel()) { person.setEduDaysOfTravelFromAcademy(travelTime); } - - person.setEduDaysOfTravel(person.getEduDaysOfTravel() + 1); - } catch (Exception e) { + } finally { person.setEduDaysOfTravel(person.getEduDaysOfTravel() + 1); } @@ -766,7 +738,7 @@ private static boolean checkForDropout(Campaign campaign, Academy academy, Perso } else { processClanWashout(campaign, person, person.getEduCourseIndex(), resources); } - // if it isn't a Sibko, we assume it was a reeducation camp, so they flee. + // if it isn't a Sibko, we assume it was a reeducation camp, so they flee. } else if (academy.isClan()) { ServiceLogger.eduClanFlee(person, campaign.getLocalDate()); person.changeStatus(campaign, campaign.getLocalDate(), PersonnelStatus.MIA); @@ -1232,7 +1204,7 @@ private static void graduateWarriorCaste(Campaign campaign, Person person, Acade processGraduation(campaign, person, academy, 0, resources); campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("graduatedRankClan.text"), - resources.getString("graduatedRankClanWarrior.text"), resources.getString("graduatedWeightLight.text"))); + resources.getString("graduatedRankClanWarrior.text"), resources.getString("graduatedWeightLight.text"))); return; } @@ -1255,7 +1227,7 @@ private static void graduateWarriorCaste(Campaign campaign, Person person, Acade processGraduation(campaign, person, academy, 2, resources); campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("graduatedRankClan.text"), - resources.getString("graduatedRankStarCaptain.text"), resources.getString("graduatedWeightHeavy.text"))); + resources.getString("graduatedRankStarCaptain.text"), resources.getString("graduatedWeightHeavy.text"))); } /** @@ -1361,7 +1333,7 @@ private static void graduateTechnicianCaste(Campaign campaign, Person person, Ac } campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("graduatedTrialPassed.text"), - resources.getString("graduatedEffortlessly.text"), resources.getString("graduatedTechnician.text"))); + resources.getString("graduatedEffortlessly.text"), resources.getString("graduatedTechnician.text"))); ServiceLogger.eduClanPassed(person, campaign.getLocalDate(), resources.getString("graduatedEffortlessly.text"), @@ -1397,7 +1369,7 @@ private static void graduateMerchantCaste(Campaign campaign, Person person, Acad if (graduationRoll < 90) { campaign.addReport(person.getHyperlinkedName() + ' ' + resources.getString("graduatedTrialPassed.text"), - resources.getString("graduatedClanBarely.text"), resources.getString("graduatedMerchant.text")); + resources.getString("graduatedClanBarely.text"), resources.getString("graduatedMerchant.text")); ServiceLogger.eduClanPassed(person, campaign.getLocalDate(), resources.getString("graduatedClanBarely.text"), @@ -1422,7 +1394,7 @@ private static void graduateMerchantCaste(Campaign campaign, Person person, Acad } campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("graduatedTrialPassed.text"), - resources.getString("graduatedEffortlessly.text"), resources.getString("graduatedMerchant.text"))); + resources.getString("graduatedEffortlessly.text"), resources.getString("graduatedMerchant.text"))); ServiceLogger.eduClanPassed(person, campaign.getLocalDate(), resources.getString("graduatedEffortlessly.text"), @@ -1458,7 +1430,7 @@ private static void graduateScientistCaste(Campaign campaign, Person person, Aca if (graduationRoll < 90) { campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("graduatedTrialPassed.text"), - resources.getString("graduatedClanBarely.text"), + resources.getString("graduatedClanBarely.text"), resources.getString("graduatedScientist.text"))); ServiceLogger.eduClanPassed(person, campaign.getLocalDate(), @@ -1472,7 +1444,7 @@ private static void graduateScientistCaste(Campaign campaign, Person person, Aca if (graduationRoll < 99) { campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("graduatedTrialPassed.text"), - resources.getString("graduatedEasily.text"), resources.getString("graduatedScientist.text"))); + resources.getString("graduatedEasily.text"), resources.getString("graduatedScientist.text"))); ServiceLogger.eduClanPassed(person, campaign.getLocalDate(), resources.getString("graduatedEasily.text"), @@ -1484,7 +1456,7 @@ private static void graduateScientistCaste(Campaign campaign, Person person, Aca } campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("graduatedTrialPassed.text"), - resources.getString("graduatedEffortlessly.text"), resources.getString("graduatedScientist.text"))); + resources.getString("graduatedEffortlessly.text"), resources.getString("graduatedScientist.text"))); ServiceLogger.eduClanPassed(person, campaign.getLocalDate(), resources.getString("graduatedEffortlessly.text"), @@ -1609,7 +1581,7 @@ private static void addBonus(Campaign campaign, Person person, Academy academy, person.awardXP(campaign, roll); campaign.addReport(String.format(resources.getString("bonusXp.text"), - person.getFirstName(), roll)); + person.getFirstName(), roll)); } } catch (Exception e) { // if we get this, it means the 'skill' was Bonus XP diff --git a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java index df0b582123..56aa46509f 100644 --- a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java @@ -37,6 +37,7 @@ import mekhq.campaign.log.LogEntry; import mekhq.campaign.log.PersonalLogger; import mekhq.campaign.personnel.*; +import mekhq.campaign.personnel.autoAwards.AutoAwardsController; import mekhq.campaign.personnel.education.Academy; import mekhq.campaign.personnel.education.AcademyFactory; import mekhq.campaign.personnel.education.EducationController; @@ -70,6 +71,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static mekhq.campaign.personnel.education.EducationController.getAcademy; + public class PersonnelTableMouseAdapter extends JPopupMenuAdapter { //region Variable Declarations private static final String CMD_RANKSYSTEM = "RANKSYSTEM"; @@ -175,7 +178,7 @@ protected PersonnelTableMouseAdapter(CampaignGUI gui, JTable personnelTable, } public static void connect(CampaignGUI gui, JTable personnelTable, - PersonnelTableModel personnelModel, JSplitPane splitPersonnel) { + PersonnelTableModel personnelModel, JSplitPane splitPersonnel) { new PersonnelTableMouseAdapter(gui, personnelTable, personnelModel) { @Override public void mouseClicked(MouseEvent e) { @@ -304,7 +307,7 @@ public void actionPerformed(ActionEvent action) { gui.getCampaign().getProcreation().addPregnancy( gui.getCampaign(), gui.getCampaign().getLocalDate(), person); MekHQ.triggerEvent(new PersonChangedEvent(person)); - }); + }); break; } case CMD_REMOVE_PREGNANCY: { @@ -358,15 +361,33 @@ public void actionPerformed(ActionEvent action) { break; } case CMD_COMPLETE_STAGE: { + List graduatingPersonnel = new ArrayList<>(); + HashMap> academyAttributesMap = new HashMap<>(); + for (Person person : people) { - if (person.getEduDaysOfTravelToAcademy() > 0) { - EducationController.completeJourneyTo(gui.getCampaign(), person); - } else if (person.getEduDaysOfEducation() > 0) { - EducationController.completeEducation(gui.getCampaign(), person); - } else { + person.setEduDaysOfEducation(1); + + Academy academy = getAcademy(person.getEduAcademySet(), person.getEduAcademyNameInSet()); + + List individualAcademyAttributes = new ArrayList<>(); + + if (EducationController.processNewDay(gui.getCampaign(), person, true)) { + graduatingPersonnel.add(person.getId()); + + individualAcademyAttributes.add(academy.getEducationLevel(person)); + individualAcademyAttributes.add(academy.getType()); + individualAcademyAttributes.add(academy.getName()); + + academyAttributesMap.put(person.getId(), individualAcademyAttributes); + person.changeStatus(gui.getCampaign(), gui.getCampaign().getLocalDate(), PersonnelStatus.ACTIVE); } } + + if (!graduatingPersonnel.isEmpty()) { + AutoAwardsController autoAwardsController = new AutoAwardsController(); + autoAwardsController.PostGraduationController(gui.getCampaign(), graduatingPersonnel, academyAttributesMap); + } break; } case CMD_IMPROVE: { @@ -1340,6 +1361,9 @@ protected Optional createPopupMenu() { if ("group".equalsIgnoreCase(award.getItem())) { awardGroups.add(award.getName()); awardGroupDescriptions.add(award.getDescription()); + } else if ("group".equalsIgnoreCase(award.getItem())) { + awardGroups.add(award.getName()); + awardGroupDescriptions.add(award.getDescription()); } } @@ -1370,7 +1394,7 @@ protected Optional createPopupMenu() { awardGroupMenu.add(menuItem); } else if ((!awardGroups.contains(award.getGroup())) && (index == 0)) { menuItem = getAwardMenuItem(award); - awardGroupMenu.add(menuItem); + awardMenu.add(menuItem); } } } @@ -2539,7 +2563,7 @@ private void buildEducationMenus(Campaign campaign, Person person, Academy acade educationJMenuItemAdder(academy, clanMenu, militaryMenu, civilianMenu, academyOption); } - // is the applicant qualified? + // is the applicant qualified? } else if (!academy.isQualified(person)) { if ((showIneligibleAcademies) && (campaign.getCampaignOptions().isEnableShowUnqualified())) { JMenuItem academyOption = new JMenuItem("" + academy.getName() + resources.getString("eduUnqualified.text") @@ -2610,7 +2634,7 @@ private void buildEducationMenus(Campaign campaign, Person person, Academy acade JMenuItem academyOption = new JMenuItem("" + academy.getName() + resources.getString("eduFactionConflict.text")); educationJMenuItemAdder(academy, clanMenu, militaryMenu, civilianMenu, academyOption); } - // which is the nearest campus and is it in range? + // which is the nearest campus and is it in range? } else { String nearestCampus = Academy.getNearestCampus(campaign, campuses); diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java index 8d15bf0506..1d5ca7b144 100644 --- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java +++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java @@ -338,6 +338,7 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane { private JCheckBox chkEnableSkillAwards; private JCheckBox chkEnableTheatreOfWarAwards; private JCheckBox chkEnableTimeAwards; + private JCheckBox chkEnableTrainingAwards; private JCheckBox chkEnableMiscAwards; // Death @@ -358,7 +359,6 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane { // Education private JCheckBox chkUseEducationModule; - private JCheckBox chkEduEnableAutoAwardsIntegration; private JLabel lblMaximumJumpCount; private JSpinner spnMaximumJumpCount; private JCheckBox chkUseTruebornTravelException; @@ -3504,6 +3504,7 @@ public Component getListCellRendererComponent(final JList list, final Object chkEnableSkillAwards.setEnabled(isEnabled); chkEnableTheatreOfWarAwards.setEnabled(isEnabled); chkEnableTimeAwards.setEnabled(isEnabled); + chkEnableTrainingAwards.setEnabled(isEnabled); chkEnableMiscAwards.setEnabled(isEnabled); }); @@ -3524,6 +3525,7 @@ public Component getListCellRendererComponent(final JList list, final Object chkEnableSkillAwards.setEnabled(false); chkEnableTheatreOfWarAwards.setEnabled(false); chkEnableTimeAwards.setEnabled(false); + chkEnableTrainingAwards.setEnabled(false); chkEnableMiscAwards.setEnabled(false); } @@ -3612,6 +3614,10 @@ private JPanel createAutoAwardsPanel() { chkEnableTimeAwards.setToolTipText(resources.getString("chkEnableTimeAwards.toolTipText")); chkEnableTimeAwards.setName("chkEnableTimeAwards"); + chkEnableTrainingAwards = new JCheckBox(resources.getString("chkEnableTrainingAwards.text")); + chkEnableTrainingAwards.setToolTipText(resources.getString("chkEnableTrainingAwards.toolTipText")); + chkEnableTrainingAwards.setName("chkEnableTrainingAwards"); + chkEnableMiscAwards = new JCheckBox(resources.getString("chkEnableMiscAwards.text")); chkEnableMiscAwards.setToolTipText(resources.getString("chkEnableMiscAwards.toolTipText")); chkEnableMiscAwards.setName("chkEnableMiscAwards"); @@ -3641,6 +3647,7 @@ private JPanel createAutoAwardsPanel() { .addGroup(layout.createSequentialGroup() .addComponent(chkEnableTheatreOfWarAwards) .addComponent(chkEnableTimeAwards) + .addComponent(chkEnableTrainingAwards) .addComponent(chkEnableMiscAwards)) ); @@ -3659,6 +3666,7 @@ private JPanel createAutoAwardsPanel() { .addGroup(layout.createParallelGroup() .addComponent(chkEnableTheatreOfWarAwards) .addComponent(chkEnableTimeAwards) + .addComponent(chkEnableTrainingAwards) .addComponent(chkEnableMiscAwards)) ); @@ -5034,12 +5042,6 @@ public Component getListCellRendererComponent(final JList list, final Object } private JPanel createEducationPanel() { - // TODO add autoAwards integration once both modules are merged - // Enable autoAwards Integration - chkEduEnableAutoAwardsIntegration = new JCheckBox(resources.getString("chkEduEnableAutoAwardsIntegration.text")); - chkEduEnableAutoAwardsIntegration.setToolTipText(resources.getString("chkEduEnableAutoAwardsIntegration.toolTip")); - chkEduEnableAutoAwardsIntegration.setName("chkEduEnableAutoAwardsIntegration"); - // General Settings lblMaximumJumpCount = new JLabel(resources.getString("lblMaximumJumpCount.text")); lblMaximumJumpCount.setToolTipText(resources.getString("lblMaximumJumpCount.toolTip")); @@ -5089,7 +5091,6 @@ private JPanel createEducationPanel() { chkUseEducationModule.addActionListener(evt -> { final boolean isEnabled = chkUseEducationModule.isSelected(); - chkEduEnableAutoAwardsIntegration.setEnabled(isEnabled); lblMaximumJumpCount.setEnabled(isEnabled); spnMaximumJumpCount.setEnabled(isEnabled); chkUseTruebornTravelException.setEnabled(isEnabled); @@ -5145,7 +5146,6 @@ private JPanel createEducationPanel() { // this prevents a really annoying bug where disabled options don't stay disabled when // reloading Campaign Options - chkEduEnableAutoAwardsIntegration.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); lblMaximumJumpCount.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); spnMaximumJumpCount.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); chkUseTruebornTravelException.setEnabled(campaign.getCampaignOptions().isUseEducationModule()); @@ -5171,8 +5171,6 @@ private JPanel createEducationPanel() { layout.createSequentialGroup() .addComponent(chkUseEducationModule) .addGap(10) - .addComponent(chkEduEnableAutoAwardsIntegration) - .addGap(10) .addGroup(layout.createParallelGroup(Alignment.LEADING) .addComponent(lblMaximumJumpCount) .addComponent(spnMaximumJumpCount)) @@ -5190,8 +5188,6 @@ private JPanel createEducationPanel() { layout.createParallelGroup(Alignment.LEADING) .addComponent(chkUseEducationModule) .addGap(10) - .addComponent(chkEduEnableAutoAwardsIntegration) - .addGap(10) .addGroup(layout.createSequentialGroup() .addComponent(lblMaximumJumpCount) .addComponent(spnMaximumJumpCount)) @@ -5762,13 +5758,13 @@ private void createPercentageRandomProcreationPanel(final JPanel panel) { panel.setLayout(layout); layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomProcreationRelationshipChance) - .addComponent(spnPercentageRandomProcreationRelationshipChance, Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomProcreationRelationshiplessChance) - .addComponent(spnPercentageRandomProcreationRelationshiplessChance, Alignment.LEADING)) + layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblPercentageRandomProcreationRelationshipChance) + .addComponent(spnPercentageRandomProcreationRelationshipChance, Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblPercentageRandomProcreationRelationshiplessChance) + .addComponent(spnPercentageRandomProcreationRelationshiplessChance, Alignment.LEADING)) ); layout.setHorizontalGroup( @@ -7082,6 +7078,7 @@ public void setOptions(@Nullable CampaignOptions options, chkEnableSkillAwards.setSelected(options.isEnableSkillAwards()); chkEnableTheatreOfWarAwards.setSelected(options.isEnableTheatreOfWarAwards()); chkEnableTimeAwards.setSelected(options.isEnableTimeAwards()); + chkEnableTrainingAwards.setSelected(options.isEnableTimeAwards()); chkEnableMiscAwards.setSelected(options.isEnableMiscAwards()); //endregion Personnel Tab @@ -7180,7 +7177,6 @@ public void setOptions(@Nullable CampaignOptions options, // Education chkUseEducationModule.setSelected(options.isUseEducationModule()); - chkEduEnableAutoAwardsIntegration.setSelected(options.isEduEnableAutoAwardsIntegration()); spnMaximumJumpCount.setValue(options.getMaximumJumpCount()); chkUseTruebornTravelException.setSelected(options.isUseTruebornTravelException()); chkEnableLocalAcademies.setSelected(options.isEnableLocalAcademies()); @@ -7710,6 +7706,7 @@ public void updateOptions() { options.setEnableSkillAwards(chkEnableSkillAwards.isSelected()); options.setEnableTheatreOfWarAwards(chkEnableTheatreOfWarAwards.isSelected()); options.setEnableTimeAwards(chkEnableTimeAwards.isSelected()); + options.setEnableTimeAwards(chkEnableTrainingAwards.isSelected()); options.setEnableMiscAwards(chkEnableMiscAwards.isSelected()); //endregion Personnel Tab @@ -7783,7 +7780,6 @@ public void updateOptions() { // Education options.setUseEducationModule(chkUseEducationModule.isSelected()); - options.setEduEnableAutoAwardsIntegration(chkEduEnableAutoAwardsIntegration.isSelected()); options.setMaximumJumpCount((Integer) spnMaximumJumpCount.getValue()); options.setUseTruebornTravelException(chkUseTruebornTravelException.isSelected()); options.setEnableLocalAcademies(chkEnableLocalAcademies.isSelected()); diff --git a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java index cba0469e2b..1213d3855e 100644 --- a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java @@ -955,7 +955,7 @@ public void mouseClicked(MouseEvent e) { lblFormerSpouses2 = new JLabel(); lblFormerSpouses2.setName("lblFormerSpouses2"); lblFormerSpouses2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblFormerSpouses1) + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblFormerSpouses1) ); lblFormerSpouses2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); lblFormerSpouses2.setText(String.format("%s, %s, %s", @@ -993,7 +993,7 @@ public void mouseClicked(MouseEvent e) { lblChildren2 = new JLabel(); lblChildren2.setName("lblChildren2"); lblChildren2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblChildren1) + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblChildren1) ); lblChildren2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); lblChildren2.setText(String.format("%s", child.getFullName())); @@ -1031,7 +1031,7 @@ public void mouseClicked(MouseEvent e) { lblGrandchildren2 = new JLabel(); lblGrandchildren2.setName("lblGrandchildren2"); lblGrandchildren2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblGrandchildren1) + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblGrandchildren1) ); lblGrandchildren2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); lblGrandchildren2.setText(String.format("%s", grandchild.getFullName())); @@ -1097,7 +1097,7 @@ public void mouseClicked(MouseEvent e) { lblSiblings2 = new JLabel(String.format("%s", sibling.getHyperlinkedName())); lblSiblings2.setName("lblSiblings2"); lblSiblings2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblSiblings1)); + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblSiblings1)); lblSiblings2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); lblSiblings2.addMouseListener(new MouseAdapter() { @@ -1135,7 +1135,7 @@ public void mouseClicked(MouseEvent e) { grandparent.getHyperlinkedName())); lblGrandparents2.setName("lblGrandparents2"); lblGrandparents2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblGrandparents1)); + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblGrandparents1)); lblGrandparents2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); lblGrandparents2.addMouseListener(new MouseAdapter() { @Override @@ -1172,7 +1172,7 @@ public void mouseClicked(MouseEvent e) { auntOrUncle.getHyperlinkedName())); lblAuntsOrUncles2.setName("lblAuntsOrUncles2"); lblAuntsOrUncles2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblAuntsOrUncles1)); + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblAuntsOrUncles1)); lblAuntsOrUncles2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); lblAuntsOrUncles2.addMouseListener(new MouseAdapter() { @@ -1209,7 +1209,7 @@ public void mouseClicked(MouseEvent e) { lblCousins2 = new JLabel(); lblCousins2.setName("lblCousins2"); lblCousins2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblCousins1)); + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblCousins1)); lblCousins2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); lblCousins2.setText(String.format("%s", cousin.getHyperlinkedName())); lblCousins2.addMouseListener(new MouseAdapter() { @@ -1315,7 +1315,7 @@ private JPanel fillSkills() { lblAbility2.setToolTipText(option.getDescription()); lblAbility2.setName("lblAbility2"); lblAbility2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblAbility1)); + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblAbility1)); gridBagConstraints.gridy = firsty++; pnlSkills.add(lblAbility2, gridBagConstraints); } @@ -1345,7 +1345,7 @@ private JPanel fillSkills() { lblImplants2.setToolTipText(option.getDescription()); lblImplants2.setName("lblImplants2"); lblImplants2.getAccessibleContext().getAccessibleRelationSet().add( - new AccessibleRelation(AccessibleRelation.LABELED_BY, lblImplants1)); + new AccessibleRelation(AccessibleRelation.LABELED_BY, lblImplants1)); gridBagConstraints.gridy = firsty++; pnlSkills.add(lblImplants2, gridBagConstraints); } @@ -1505,7 +1505,7 @@ private JPanel fillSkills() { pnlSkills.add(lblEducationDays2, gridBagConstraints); } - if ((campaign.getCampaignOptions().isUseEducationModule()) && (person.getEduDaysOfTravelFromAcademy() > 0)) { + if ((campaign.getCampaignOptions().isUseEducationModule()) && (person.getEduDaysOfEducation() == 0) &&(person.getEduDaysOfTravelFromAcademy() > 0)) { lblEducationTravelFrom1.setName("lblEducationTravelFrom1"); lblEducationTravelFrom1.setText(resourceMap.getString("lblEducationTravelFrom1.text")); gridBagConstraints = new GridBagConstraints(); diff --git a/MekHQ/src/mekhq/utilities/MHQXMLUtility.java b/MekHQ/src/mekhq/utilities/MHQXMLUtility.java index c61b05417d..bc14346fc3 100644 --- a/MekHQ/src/mekhq/utilities/MHQXMLUtility.java +++ b/MekHQ/src/mekhq/utilities/MHQXMLUtility.java @@ -489,10 +489,6 @@ public static void writeEntityWithCrewToXML(PrintWriter pw, int indentLvl, Entit crew.append("\" " + MULParser.ATTR_TOUGH + "=\"").append(tgtEnt.getCrew().getToughness(pos)); } - if (tgtEnt.getCrew().getCrewFatigue(pos) != 0) { - crew.append("\" " + MULParser.ATTR_FATIGUE + "=\"").append(tgtEnt.getCrew().getCrewFatigue(pos)); - } - if (tgtEnt.getCrew().isDead(pos) || tgtEnt.getCrew().getHits(pos) >= Crew.DEATH) { crew.append("\" " + MULParser.ATTR_HITS + "=\"" + MULParser.VALUE_DEAD + ""); } else if (tgtEnt.getCrew().getHits(pos) > 0) { diff --git a/MekHQ/unittests/mekhq/campaign/personnel/education/AcademyTests.java b/MekHQ/unittests/mekhq/campaign/personnel/education/AcademyTests.java index 6299cb1cb5..8e9c88c10d 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/education/AcademyTests.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/education/AcademyTests.java @@ -7,10 +7,15 @@ import mekhq.campaign.universe.PlanetarySystem; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; + import java.util.Arrays; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + class AcademyTests { @Test void testSetName() { @@ -35,7 +40,7 @@ void testIsMilitary() { @Test void testAcademyCreationAllFields() { - Academy academy = new Academy("MechWarrior", "MekWarrior Academy", true, "Colonel", true, + Academy academy = new Academy("MechWarrior", "MekWarrior Academy", 0,true, "Colonel", true, true, true, "Top level MechWarrior Training", 20, true, "FWL", Arrays.asList("Sol", "Terra"), false, @@ -44,6 +49,7 @@ void testAcademyCreationAllFields() { Arrays.asList("MechWarrior", "Leadership"), Arrays.asList("Combat", "Strategy"), Arrays.asList(3050, 3055), 5, 101); assertEquals("MekWarrior Academy", academy.getName()); + assertEquals(0, academy.getType()); assertTrue(academy.isMilitary()); assertEquals("Colonel", academy.getPromotion()); assertTrue(academy.isClan());