From 771c3107183e31fa9da9b2e417a6fc0d0b673a48 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 14 Aug 2021 00:07:09 -0400 Subject: [PATCH 1/4] 274: Moving Era definitions to a file --- MekHQ/data/universe/eras.xml | 95 +++++++++++ MekHQ/src/mekhq/MekHqConstants.java | 3 +- MekHQ/src/mekhq/campaign/Campaign.java | 19 +-- .../mission/AtBDynamicScenarioFactory.java | 119 +++++++------ MekHQ/src/mekhq/campaign/universe/Era.java | 157 ------------------ .../campaign/universe/enums/EraFlag.java | 96 +++++++++++ .../src/mekhq/campaign/universe/eras/Era.java | 135 +++++++++++++++ .../mekhq/campaign/universe/eras/Eras.java | 126 ++++++++++++++ .../mekhq/gui/dialog/DataLoadingDialog.java | 50 ++---- 9 files changed, 539 insertions(+), 261 deletions(-) create mode 100644 MekHQ/data/universe/eras.xml delete mode 100644 MekHQ/src/mekhq/campaign/universe/Era.java create mode 100644 MekHQ/src/mekhq/campaign/universe/enums/EraFlag.java create mode 100644 MekHQ/src/mekhq/campaign/universe/eras/Era.java create mode 100644 MekHQ/src/mekhq/campaign/universe/eras/Eras.java diff --git a/MekHQ/data/universe/eras.xml b/MekHQ/data/universe/eras.xml new file mode 100644 index 0000000000..464e700828 --- /dev/null +++ b/MekHQ/data/universe/eras.xml @@ -0,0 +1,95 @@ + + + + + PSF + Pre-Spaceflight + 1950 + PRE_SPACEFLIGHT + + + ESF + Early Spaceflight + 2005 + EARLY_SPACEFLIGHT + + + AOW + Age of War + 2570 + AGE_OF_WAR + + + SL + Star League + 2780 + STAR_LEAGUE + + + ESW + Early Succession Wars + 2900 + EARLY_SUCCESSION_WARS + + + LSWL + Late Succession War - LosTech + 3019 + LATE_SUCCESSION_WARS_LOSTECH + + + LSWR + Late Succession War - Renaissance + 3049 + LATE_SUCCESSION_WARS_RENAISSANCE + + + CI + Clan Invasion + 3061 + CLAN_INVASION + + + CW + Civil War + 3067 + CIVIL_WAR + + + J + Jihad + 3085 + JIHAD + + + ER + Early Republic + 3100 + EARLY_REPUBLIC + + + LR + Late Republic + 3130 + LATE_REPUBLIC + + + DA + Dark Ages + 3150 + DARK_AGES + + + ILC + ilClan + ILCLAN + + \ No newline at end of file diff --git a/MekHQ/src/mekhq/MekHqConstants.java b/MekHQ/src/mekhq/MekHqConstants.java index bd43c3fc1d..e1e64cd4bb 100644 --- a/MekHQ/src/mekhq/MekHqConstants.java +++ b/MekHQ/src/mekhq/MekHqConstants.java @@ -138,7 +138,8 @@ public final class MekHqConstants { public static final String LAYERED_FORCE_ICON_FRAME_PATH = "Pieces/Frames/"; public static final String LAYERED_FORCE_ICON_LOGO_PATH = "Pieces/Logos/"; public static final String AWARDS_DIRECTORY_PATH = "data/universe/awards/"; - public static final String RATINFO_DIR = "data/universe/ratdata"; + public static final String RATINFO_DIR = "data/universe/ratdata/"; + public static final String ERAS_FILE_PATH = "data/universe/eras.xml"; public static final String FACTION_HINTS_FILE = "data/universe/factionhints.xml"; public static final String RANKS_FILE_PATH = "data/universe/ranks.xml"; public static final String USER_RANKS_FILE_PATH = "userdata/data/universe/ranks.xml"; diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 02fa77111c..6fba7f4fde 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -59,6 +59,7 @@ import mekhq.campaign.personnel.ranks.RankSystem; import mekhq.campaign.personnel.ranks.RankValidator; import mekhq.campaign.personnel.ranks.Ranks; +import mekhq.campaign.universe.eras.Eras; import mekhq.service.AutosaveService; import mekhq.service.IAutosaveService; @@ -148,7 +149,7 @@ import mekhq.campaign.universe.AbstractPlanetSelector; import mekhq.campaign.universe.DefaultFactionSelector; import mekhq.campaign.universe.DefaultPlanetSelector; -import mekhq.campaign.universe.Era; +import mekhq.campaign.universe.eras.Era; import mekhq.campaign.universe.Faction; import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.IUnitGenerator; @@ -381,18 +382,14 @@ public void setName(String s) { this.name = s; } - public String getEraName() { - return Era.getEraNameFromYear(getGameYear()); - } - - public int getEra() { - return Era.getEra(getGameYear()); + public Era getEra() { + return Eras.getInstance().getEra(getLocalDate()); } public String getTitle() { return getName() + " (" + getFactionName() + ")" + " - " + MekHQ.getMekHQOptions().getLongDisplayFormattedDate(getLocalDate()) - + " (" + getEraName() + ")"; + + " (" + getEra() + ")"; } public LocalDate getLocalDate() { @@ -3691,16 +3688,16 @@ public void removeForce(Force force) { if (null != force.getParentForce()) { force.getParentForce().removeSubForce(fid); } - + // clear out StratCon force assignments for (AtBContract contract : getActiveAtBContracts()) { if (contract.getStratconCampaignState() != null) { for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) { track.unassignForce(fid); } - } + } } - + ArrayList subs = new ArrayList<>(force.getSubForces()); for (Force sub : subs) { removeForce(sub); diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index c9ec7ca925..35579e0bec 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -79,7 +79,7 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.unit.Unit; -import mekhq.campaign.universe.Era; +import mekhq.campaign.universe.enums.EraFlag; import mekhq.campaign.universe.Faction; import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.Faction.Tag; @@ -100,8 +100,8 @@ public class AtBDynamicScenarioFactory { public static final int UNIT_WEIGHT_UNSPECIFIED = -1; // bomb types assignable to aerospace units on ground maps - private static final int[] validBotBombs = { BombType.B_HE, BombType.B_CLUSTER, BombType.B_RL, - BombType.B_INFERNO, BombType.B_THUNDER, BombType.B_FAE_SMALL, BombType.B_FAE_LARGE, + private static final int[] validBotBombs = { BombType.B_HE, BombType.B_CLUSTER, BombType.B_RL, + BombType.B_INFERNO, BombType.B_THUNDER, BombType.B_FAE_SMALL, BombType.B_FAE_LARGE, BombType.B_LG, BombType.B_ARROW, BombType.B_HOMING, BombType.B_TAG }; private static final int[] validBotAABombs = { BombType.B_RL, BombType.B_LAA, BombType.B_AAA }; @@ -163,8 +163,8 @@ public static AtBDynamicScenario initializeScenarioFromTemplate(ScenarioTemplate // apply a default "reinforcements" force template if a scenario-specific one does not already exist if (!template.getScenarioForces().containsKey(ScenarioForceTemplate.REINFORCEMENT_TEMPLATE_ID)) { ScenarioForceTemplate defaultReinforcements = ScenarioForceTemplate.getDefaultReinforcementsTemplate(); - - // the default template should not allow the user to deploy ground units as + + // the default template should not allow the user to deploy ground units as // reinforcements to aerospace battles // space battles are even more restrictive if (template.mapParameters.getMapLocation() == MapLocation.LowAtmosphere) { @@ -172,8 +172,8 @@ public static AtBDynamicScenario initializeScenarioFromTemplate(ScenarioTemplate } else if (template.mapParameters.getMapLocation() == MapLocation.Space) { defaultReinforcements.setAllowedUnitType(UnitType.AERO); } - - + + template.getScenarioForces().put(defaultReinforcements.getForceName(), defaultReinforcements); } @@ -194,7 +194,7 @@ public static void finalizeScenario(AtBDynamicScenario scenario, AtBContract con for (int x = scenario.getNumBots() - 1; x >= 0; x--) { scenario.removeBotForce(x); } - + applyScenarioModifiers(scenario, campaign, EventTiming.PreForceGeneration); // Now we can clear the other related lists @@ -228,7 +228,7 @@ public static void finalizeScenario(AtBDynamicScenario scenario, AtBContract con if (campaign.getCampaignOptions().useAbilities()) { upgradeBotCrews(scenario); } - + scenario.setFinalized(true); } @@ -428,7 +428,7 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac MapLocation mapLocation = scenario.getTemplate().mapParameters.getMapLocation(); boolean isAeroMap = (mapLocation == MapLocation.LowAtmosphere) || (mapLocation == MapLocation.Space); - + populateAeroBombs(generatedLance, campaign, !isAeroMap); } @@ -864,21 +864,21 @@ public static void setScenarioMapSize(AtBDynamicScenario scenario) { scenario.setMapSizeX(mapSizeX); scenario.setMapSizeY(mapSizeY); } - + /** - * If there are maps of the appropriate size available and we roll higher than + * If there are maps of the appropriate size available and we roll higher than * the given threshold, replace the scenario's generated map with a fixed map from data/boards */ private static void setScenarioMap(AtBDynamicScenario scenario, int mapChance) { if ((scenario.getMapSizeX() > 0) && (scenario.getMapSizeY() > 0) && (Compute.randomInt(100) <= mapChance)) { BoardClassifier bc = BoardClassifier.getInstance(); List maps = bc.getMatchingBoards(scenario.getMapSizeX(), scenario.getMapSizeY(), 5, 5, new ArrayList<>()); - + if (!maps.isEmpty()) { String mapPath = Utilities.getRandomItem(maps); MegaMekFile mapFile = new MegaMekFile(mapPath); BoardDimensions dimensions = Board.getSize(mapFile.getFile()); - + scenario.setMap(bc.getBoardPaths().get(mapPath)); scenario.setMapSizeX(dimensions.width()); scenario.setMapSizeY(dimensions.height()); @@ -886,7 +886,7 @@ private static void setScenarioMap(AtBDynamicScenario scenario, int mapChance) { return; } } - + scenario.setUsingFixedMap(false); scenario.setMapFile(); } @@ -1478,10 +1478,9 @@ private static List generateUnitTypes(int unitTypeCode, int unitCount, // logic mostly lifted from AtBScenario.java, uses campaign config to determine tank/mech mixture if (useVehicles) { // some specialized logic for clan opfors - int era = Era.getEra(campaign.getGameYear()); - // if we're in the late republic or dark ages, clans no longer have the luxury of mech only stars - boolean clanEquipmentScarcity = era == Era.E_LATE_REPUBLIC || era == Era.E_DARK_AGES; + boolean clanEquipmentScarcity = campaign.getEra() + .hasFlag(EraFlag.LATE_REPUBLIC, EraFlag.DARK_AGES, EraFlag.ILCLAN); if (faction.isClan() && !clanEquipmentScarcity) { return generateClanUnitTypes(unitCount, forceQuality, factionCode, campaign); @@ -1949,7 +1948,7 @@ public static void setDeploymentTurns(BotForce botForce, int deployRound, AtBDynamicScenario scenario) { // deployment turns don't matter for transported entities List untransportedEntities = scenario.filterUntransportedUnits(botForce.getEntityList()); - + if (deployRound == ScenarioForceTemplate.ARRIVAL_TURN_STAGGERED_BY_LANCE) { setDeploymentTurnsStaggeredByLance(untransportedEntities); } else if (deployRound == ScenarioForceTemplate.ARRIVAL_TURN_AS_REINFORCEMENTS) { @@ -2124,7 +2123,7 @@ public static void setDeploymentTurnsForReinforcements(List entityList, if (entity.getTransportId() != Entity.NONE) { continue; } - + int speed = calculateAtBSpeed(entity); // don't reduce minimum speed to 0, since dividing by zero further down is problematic @@ -2298,7 +2297,7 @@ private static void deployArtilleryOffBoard(List entityList) { } /** - * Helper function that puts the units in the given list at the given altitude. + * Helper function that puts the units in the given list at the given altitude. * Use with caution, as may lead to splattering or aerospace units starting on the ground. * @param entityList The entity list to process. * @param startingAltitude Starting altitude. @@ -2307,7 +2306,7 @@ private static void setStartingAltitude(List entityList, int startingAlt for (Entity entity : entityList) { if (entity instanceof IAero) { entity.setAltitude(startingAltitude); - + // there's a lot of stuff that happens whan an aerospace unit // "lands", so let's make sure it all happens if (startingAltitude == 0) { @@ -2316,7 +2315,7 @@ private static void setStartingAltitude(List entityList, int startingAlt } } } - + /** * This method contains various hacks intended to put "special units" * such as LAMs, VTOLs and WIGEs into a reasonable state that the bot can use @@ -2325,7 +2324,7 @@ private static void correctNonAeroFlyerBehavior(List entityList, int ter for (Entity entity : entityList) { boolean inSpace = terrainType == AtBScenario.TER_SPACE; boolean inAtmo = terrainType == AtBScenario.TER_LOW_ATMO; - + // hack for land-air mechs if (entity instanceof LandAirMech) { if (inSpace || inAtmo) { @@ -2335,9 +2334,9 @@ private static void correctNonAeroFlyerBehavior(List entityList, int ter ((LandAirMech) entity).setConversionMode(LandAirMech.CONV_MODE_MECH); } } - + // hack - set helis and WIGEs to an explicit altitude of 1 - // currently there is no support for setting elevation for "ground" units + // currently there is no support for setting elevation for "ground" units // in the scenario template editor, but it looks dumb to have choppers // start out on the ground if ((entity.getMovementMode() == EntityMovementMode.VTOL) || @@ -2380,29 +2379,29 @@ public static void populateAeroBombs(List entityList, Campaign campaign, /** * Worker function that takes an entity and an array of bomb types - * and loads it up with as many of a mostly period-appropriate random bomb type + * and loads it up with as many of a mostly period-appropriate random bomb type * as it's capable of holding */ private static void loadBombs(Entity entity, int[] validBombChoices, int year) { int[] bombChoices = new int[BombType.B_NUM]; - + // remove bomb choices if they're not era-appropriate List actualValidBombChoices = new ArrayList<>(); for (int x = 0; x < validBombChoices.length; x++) { String typeName = BombType.getBombInternalName(validBombChoices[x]); - + // hack: make rocket launcher pods available before 3055 if ((validBombChoices[x] == BombType.B_RL) || BombType.get(typeName).isAvailableIn(year)) { actualValidBombChoices.add(validBombChoices[x]); } } - + // pick out the index in the BombType array int randomBombChoiceIndex = Compute.randomInt(actualValidBombChoices.size()); int bombIndex = actualValidBombChoices.get(randomBombChoiceIndex); int weightModifier = 0; - + // hack: we only really need one "tag", so add it then pack on some more bombs if (bombIndex == BombType.B_TAG) { weightModifier = 5; @@ -2410,9 +2409,9 @@ private static void loadBombs(Entity entity, int[] validBombChoices, int year) { actualValidBombChoices.remove(randomBombChoiceIndex); bombIndex = Utilities.getRandomItem(actualValidBombChoices); } - + // # of bombs is the unit's weight / (bomb cost * 5) - int numBombs = (int) Math.floor((entity.getWeight() - weightModifier) / + int numBombs = (int) Math.floor((entity.getWeight() - weightModifier) / (BombType.getBombCost(bombIndex) * 5.0)); bombChoices[bombIndex] = numBombs; @@ -2481,7 +2480,7 @@ public static void upgradeBotCrews(AtBScenario scenario) { csu.upgradeCrew(entity); } } - + /** * Highly paranoid function that will check if the given faction is one of the * owners of the contract's location at the current date. @@ -2491,12 +2490,12 @@ private static boolean isPlanetOwner(AtBContract contract, LocalDate currentDate (contract.getSystem().getFactions(currentDate) == null)) { return false; } - + return contract.getSystem().getFactions(currentDate).contains(factionCode); } - + /** - * Given a player unit ID and a template name, if the player unit type matches + * Given a player unit ID and a template name, if the player unit type matches * the template's unit type and the template generation method is PlayerOrAllied, * take the first unit that we find in the given scenario that's a part of that * template and "put it away". @@ -2506,48 +2505,48 @@ public static void benchAllyUnit(UUID playerUnitID, String templateName, AtBDyna if (scenario.getTemplate().getScenarioForces().containsKey(templateName)) { destinationTemplate = scenario.getTemplate().getScenarioForces().get(templateName); } - - if ((destinationTemplate == null) || + + if ((destinationTemplate == null) || (destinationTemplate.getGenerationMethod() != ForceGenerationMethod.PlayerOrFixedUnitCount.ordinal())) { return; } - + // two possible situations here: - // 1 - the unit is an "attached" unit. This requires a mapping between template name and + // 1 - the unit is an "attached" unit. This requires a mapping between template name and // individual attached units. At this point, we remove the first unit matching the template // from the attached units list. The benched unit should have the player unit's ID // stored so that if the player unit is detached, the benched unit comes back. // 2 - the unit is part of a bot force. In this case, we need a mapping between template names - // and bot forces. + // and bot forces. if (destinationTemplate.getForceAlignment() == ForceAlignment.Player.ordinal()) { Entity swapTarget = null; - + // look through the "allies" player to see a unit that was put there // under a matching template for (Entity entity : scenario.getAlliesPlayer()) { UUID unitID = UUID.fromString(entity.getExternalIdAsString()); - + if (scenario.getBotUnitTemplates().get(unitID).getForceName().equals(templateName)) { swapTarget = entity; break; } } - + if (swapTarget == null) { return; } - + BenchedEntityData benchedEntity = new BenchedEntityData(); benchedEntity.entity = swapTarget; benchedEntity.templateName = ""; - + scenario.getAlliesPlayer().remove(swapTarget); scenario.getPlayerUnitSwaps().put(playerUnitID, benchedEntity); swapUnitInObjectives(playerUnitID.toString(), benchedEntity.entity.getExternalIdAsString(), "", scenario); } else { BotForce botForce = null; - + // slightly inefficient to loop through all bot forces looking for our template // but it is also difficult to create a reverse lookup, so we avoid that problem for now for (int x = 0; x < scenario.getNumBots(); x++) { @@ -2557,20 +2556,20 @@ public static void benchAllyUnit(UUID playerUnitID, String templateName, AtBDyna break; } } - + if ((botForce != null) && !botForce.getEntityList().isEmpty()) { Entity swapTarget = botForce.getEntityList().get(0); BenchedEntityData benchedEntity = new BenchedEntityData(); benchedEntity.entity = swapTarget; benchedEntity.templateName = destinationTemplate.getForceName(); - + botForce.removeEntity(0); scenario.getPlayerUnitSwaps().put(playerUnitID, benchedEntity); swapUnitInObjectives(playerUnitID.toString(), benchedEntity.entity.getExternalIdAsString(), botForce.getName(), scenario); } } } - + /** * Given a scenario and a pair of unit IDs (and a force), swap the first one for the second one. * Or, add the unit to all objectives containing the given force. @@ -2580,23 +2579,23 @@ private static void swapUnitInObjectives(String subIn, String subOut, String sub // if the sub-out unit is explicitly referenced, do a direct substitution if (objective.getAssociatedUnitIDs().contains(subOut)) { objective.removeUnit(subOut); - + // don't want to add an empty unit to the objective if (!subIn.isEmpty()) { objective.addUnit(subIn); } - + continue; } - - // if the sub-out unit is replacing a unit that's part of a force, + + // if the sub-out unit is replacing a unit that's part of a force, // just add it individually if (objective.getAssociatedForceNames().contains(subOutForceName)) { objective.addUnit(subIn); } } } - + /** * Given a player unit ID and a scenario, return a benched allied unit, if one exists * that was benched in favor of the player's unit. @@ -2606,7 +2605,7 @@ public static void unbenchAttachedAlly(UUID playerUnitID, AtBDynamicScenario sce // add it to to bot force being worked with or attached ally list if (scenario.getPlayerUnitSwaps().containsKey(playerUnitID)) { BenchedEntityData benchedEntityData = scenario.getPlayerUnitSwaps().get(playerUnitID); - + if (benchedEntityData.templateName.isEmpty()) { scenario.getAlliesPlayer().add(benchedEntityData.entity); swapUnitInObjectives(benchedEntityData.entity.getExternalIdAsString(), playerUnitID.toString(), "", scenario); @@ -2615,14 +2614,14 @@ public static void unbenchAttachedAlly(UUID playerUnitID, AtBDynamicScenario sce BotForce botForce = scenario.getBotForce(x); if (botForce.getTemplateName().equals(benchedEntityData.templateName)) { botForce.addEntity(benchedEntityData.entity); - // in this situation, the entity is being added back to a force, - // so we just want to clear out the player unit. + // in this situation, the entity is being added back to a force, + // so we just want to clear out the player unit. swapUnitInObjectives("", playerUnitID.toString(), "", scenario); break; } } } - + scenario.getPlayerUnitSwaps().remove(playerUnitID); } } diff --git a/MekHQ/src/mekhq/campaign/universe/Era.java b/MekHQ/src/mekhq/campaign/universe/Era.java deleted file mode 100644 index 1102d44961..0000000000 --- a/MekHQ/src/mekhq/campaign/universe/Era.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Era.java - * - * Copyright (c) 2009 Jay Lawson . All rights reserved. - * - * This file is part of MekHQ. - * - * MekHQ is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * MekHQ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with MekHQ. If not, see . - */ - -package mekhq.campaign.universe; - -import megamek.common.EquipmentType; - -/** - * - * @author Jay Lawson - * September 2017 - Update Eras to - * match MUL, and added reference to 4th era code - */ -public class Era { - - public static final int E_AOW = 0; - public static final int E_SL = 1; - public static final int E_ESW = 2; - public static final int E_LSW_LOSTECH = 3; - public static final int E_LSW_RENAISSANCE = 4; - public static final int E_CLAN_INVASION = 5; - public static final int E_CIVIL_WAR = 6; - public static final int E_JIHAD = 7; - public static final int E_EARLY_REPUBLIC = 8; - public static final int E_LATE_REPUBLIC = 9; - public static final int E_DARK_AGES = 10; - public static final int E_NUM = 11; - - public static int getEra(int year) { - if(year < 2570) { - return E_AOW; - } - else if(year < 2780) { - return E_SL; - } - else if(year < 2900) { - return E_ESW; - } - else if(year < 3019) { - return E_LSW_LOSTECH; - } - else if(year < 3049) { - return E_LSW_RENAISSANCE; - } - else if(year < 3061) { - return E_CLAN_INVASION; - } - else if(year < 3067) { - return E_CIVIL_WAR; - } - else if(year < 3085) { - return E_JIHAD; - } - else if(year <3100) { - return E_EARLY_REPUBLIC; - } - else if(year <3130) { - return E_LATE_REPUBLIC; - } - else { - return E_DARK_AGES; - } - } - - public static String getEraName(int era) { - switch(era) { - case E_AOW: - return "Age of War"; - - case E_SL: - return "Star League"; - - case E_ESW: - return "Early Succession War"; - - case E_LSW_LOSTECH: - return "Late Succession War - LosTech"; - - case E_LSW_RENAISSANCE: - return "Late Succession War - Renaissance"; - - case E_CLAN_INVASION: - return "Clan Invasion"; - - case E_CIVIL_WAR: - return "Civil War"; - - case E_JIHAD: - return "Jihad"; - - case E_EARLY_REPUBLIC: - return "Early Republic"; - - case E_LATE_REPUBLIC: - return "Late Republic"; - - case E_DARK_AGES: - return "Dark Ages"; - - default: - return "Unknown"; - } - } - - public static String getEraNameFromYear(int year) { - return getEraName(getEra(year)); - } - - /** - * Convert the eras used in Strategic Ops to the availability-based eras - * used in the TechManual. - * Updated for 4th Era Code. - * @param era - * @return - */ - - public static int convertEra(int era) { - switch(era) { - case E_AOW: - case E_SL: - return EquipmentType.ERA_SL; - case E_ESW: - case E_LSW_LOSTECH: - case E_LSW_RENAISSANCE: - return EquipmentType.ERA_SW; - case E_CLAN_INVASION: - case E_CIVIL_WAR: - case E_JIHAD: - case E_EARLY_REPUBLIC: - case E_LATE_REPUBLIC: - return EquipmentType.ERA_CLAN; - case E_DARK_AGES: - return EquipmentType.ERA_DA; - default: - return -1; - } - } - -} diff --git a/MekHQ/src/mekhq/campaign/universe/enums/EraFlag.java b/MekHQ/src/mekhq/campaign/universe/enums/EraFlag.java new file mode 100644 index 0000000000..1e78d49cf2 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/universe/enums/EraFlag.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MekHQ. + * + * MekHQ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MekHQ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MekHQ. If not, see . + */ +package mekhq.campaign.universe.enums; + +public enum EraFlag { + //region Enum Declarations + PRE_SPACEFLIGHT, + EARLY_SPACEFLIGHT, + AGE_OF_WAR, + STAR_LEAGUE, + EARLY_SUCCESSION_WARS, + LATE_SUCCESSION_WARS_LOSTECH, + LATE_SUCCESSION_WARS_RENAISSANCE, + CLAN_INVASION, + CIVIL_WAR, + JIHAD, + EARLY_REPUBLIC, + LATE_REPUBLIC, + DARK_AGES, + ILCLAN; + //endregion Enum Declarations + + //region Boolean Comparison Methods + public boolean isPreSpaceflight() { + return this == PRE_SPACEFLIGHT; + } + + public boolean isEarlySpaceflight() { + return this == EARLY_SPACEFLIGHT; + } + + public boolean isAgeOfWar() { + return this == AGE_OF_WAR; + } + + public boolean isStarLeague() { + return this == STAR_LEAGUE; + } + + public boolean isEarlySuccessionWars() { + return this == EARLY_SUCCESSION_WARS; + } + + public boolean isLateSuccessionWarsLosTech() { + return this == LATE_SUCCESSION_WARS_LOSTECH; + } + + public boolean isLateSuccessionWarsRenaissance() { + return this == LATE_SUCCESSION_WARS_RENAISSANCE; + } + + public boolean isClanInvasion() { + return this == CLAN_INVASION; + } + + public boolean isCivilWar() { + return this == CIVIL_WAR; + } + + public boolean isJihad() { + return this == JIHAD; + } + + public boolean isEarlyRepublic() { + return this == EARLY_REPUBLIC; + } + + public boolean isLateRepublic() { + return this == LATE_REPUBLIC; + } + + public boolean isDarkAges() { + return this == DARK_AGES; + } + + public boolean isIlClan() { + return this == ILCLAN; + } + //endregion Boolean Comparison Methods +} diff --git a/MekHQ/src/mekhq/campaign/universe/eras/Era.java b/MekHQ/src/mekhq/campaign/universe/eras/Era.java new file mode 100644 index 0000000000..89ea0b6474 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/universe/eras/Era.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MekHQ. + * + * MekHQ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MekHQ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MekHQ. If not, see . + */ +package mekhq.campaign.universe.eras; + +import megamek.common.annotations.Nullable; +import mekhq.MekHQ; +import mekhq.MekHqXmlUtil; +import mekhq.campaign.universe.enums.EraFlag; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.time.LocalDate; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +public class Era { + //region Variable Declarations + private String code; + private String name; + private LocalDate end; + private Set flags; + //endregion Variable Declarations + + //region Constructors + public Era() { + setCode("???"); + setName(""); + setEnd(LocalDate.ofYearDay(9999, 1)); + setFlags(new HashSet<>()); + } + //endregion Constructors + + //region Getters/Setters + public String getCode() { + return code; + } + + public void setCode(final String code) { + this.code = code; + } + + public void setName(final String name) { + this.name = name; + } + + public LocalDate getEnd() { + return end; + } + + public void setEnd(final LocalDate end) { + this.end = end; + } + + public Set getFlags() { + return flags; + } + + public void setFlags(final Set flags) { + this.flags = flags; + } + //endregion Getters/Setters + + public boolean hasFlag(final EraFlag... flags) { + return Stream.of(flags).anyMatch(getFlags()::contains); + } + + //region File I/O + public static @Nullable Era generateInstanceFromXML(final NodeList nl) { + final Era era = new Era(); + + for (int x = 0; x < nl.getLength(); x++) { + try { + final Node wn = nl.item(x); + switch (wn.getNodeName()) { + case "code": + era.setCode(MekHqXmlUtil.unEscape(wn.getTextContent().trim())); + break; + case "name": + era.setName(MekHqXmlUtil.unEscape(wn.getTextContent().trim())); + break; + case "end": + era.setEnd(MekHqXmlUtil.parseDate(wn.getTextContent().trim())); + break; + case "flag": + era.getFlags().add(EraFlag.valueOf(wn.getTextContent().trim())); + break; + } + } catch (Exception e) { + MekHQ.getLogger().error(e); + return null; + } + } + + return era.getCode().equals("???") ? null : era; + } + //endregion File I/O + + @Override + public String toString() { + return name; + } + + @Override + public boolean equals(final @Nullable Object object) { + if (this == object) { + return true; + } else if (!(object instanceof Era)) { + return false; + } else { + return getCode().equals(((Era) object).getCode()); + } + } + + @Override + public int hashCode() { + return getCode().hashCode(); + } +} diff --git a/MekHQ/src/mekhq/campaign/universe/eras/Eras.java b/MekHQ/src/mekhq/campaign/universe/eras/Eras.java new file mode 100644 index 0000000000..a30c1dba5d --- /dev/null +++ b/MekHQ/src/mekhq/campaign/universe/eras/Eras.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MekHQ. + * + * MekHQ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MekHQ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MekHQ. If not, see . + */ +package mekhq.campaign.universe.eras; + +import megamek.common.annotations.Nullable; +import megamek.common.util.fileUtils.MegaMekFile; +import mekhq.MekHQ; +import mekhq.MekHqConstants; +import mekhq.MekHqXmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.LocalDate; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +public class Eras { + //region Variable Declarations + private static Eras instance; + + private SortedMap eras; + //endregion Variable Declarations + + //region Constructors + private Eras() { + setEras(new TreeMap<>(LocalDate::compareTo)); + } + //endregion Constructors + + //region Getters/Setters + public static Eras getInstance() { + if (instance == null) { + setInstance(new Eras()); + } + + return instance; + } + + public static void setInstance(final @Nullable Eras instance) { + Eras.instance = instance; + } + + public SortedMap getEras() { + return eras; + } + + private void setEras(final TreeMap eras) { + this.eras = eras; + } + + public Era getEra(final LocalDate today) { + for (final Map.Entry entry : getEras().entrySet()) { + if (today.isBefore(entry.getKey())) { + return entry.getValue(); + } + } + + MekHQ.getLogger().error("Failed to determine a valid era for the date " + today); + return getEras().values().stream().findFirst().orElse(new Era()); + } + //endregion Getters/Setters + + //region File I/O + public static void initializeEras() throws Exception { + final Eras eras = new Eras(); + + final File file = new MegaMekFile(MekHqConstants.ERAS_FILE_PATH).getFile(); + if ((file == null) || !file.exists()) { + throw new IOException("The eras file does not exist."); + } + + final Document xmlDoc; + + try (InputStream is = new FileInputStream(file)) { + xmlDoc = MekHqXmlUtil.newSafeDocumentBuilder().parse(is); + } + + final Element element = xmlDoc.getDocumentElement(); + element.normalize(); + final NodeList nl = element.getChildNodes(); + for (int x = 0; x < nl.getLength(); x++) { + final Node wn = nl.item(x); + + if (!wn.getParentNode().equals(element) || (wn.getNodeType() != Node.ELEMENT_NODE)) { + continue; + } + + if (wn.getNodeName().equalsIgnoreCase("era") && wn.hasChildNodes()) { + final Era era = Era.generateInstanceFromXML(wn.getChildNodes()); + if (era != null) { + eras.getEras().put(era.getEnd(), era); + } + } + } + + if (eras.getEras().isEmpty()) { + throw new IOException("Failed to parse any eras"); + } + + setInstance(eras); + } + //endregion File I/O +} diff --git a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java index ab205e984f..187535db74 100644 --- a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java @@ -19,33 +19,10 @@ */ package mekhq.gui.dialog; -import java.awt.BorderLayout; -import java.awt.Image; -import java.awt.MediaTracker; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.text.ParseException; -import java.util.List; -import java.util.ResourceBundle; -import java.util.concurrent.ExecutionException; - -import javax.swing.ImageIcon; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JProgressBar; -import javax.swing.SwingWorker; -import javax.xml.parsers.ParserConfigurationException; - -import org.w3c.dom.DOMException; -import org.xml.sax.SAXException; - -import megamek.client.generator.RandomNameGenerator; import megamek.client.generator.RandomCallsignGenerator; +import megamek.client.generator.RandomNameGenerator; +import megamek.client.ui.preferences.JWindowPreference; +import megamek.client.ui.preferences.PreferencesNode; import megamek.common.MechSummaryCache; import megamek.common.QuirksHandler; import megamek.common.util.EncodeControl; @@ -57,15 +34,23 @@ import mekhq.campaign.GamePreset; import mekhq.campaign.event.OptionsChangedEvent; import mekhq.campaign.finances.CurrencyManager; -import mekhq.campaign.io.CampaignXmlParseException; import mekhq.campaign.mod.am.InjuryTypes; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.ranks.Ranks; import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.RATManager; -import megamek.client.ui.preferences.JWindowPreference; -import megamek.client.ui.preferences.PreferencesNode; import mekhq.campaign.universe.Systems; +import mekhq.campaign.universe.eras.Eras; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.FileInputStream; +import java.util.List; +import java.util.ResourceBundle; +import java.util.concurrent.ExecutionException; public class DataLoadingDialog extends JDialog implements PropertyChangeListener { private static final long serialVersionUID = -3454307876761238915L; @@ -134,12 +119,13 @@ class Task extends SwingWorker { private boolean cancelled = false; @Override - public Campaign doInBackground() throws IOException, CampaignXmlParseException, NullEntityException, - DOMException, ParseException, SAXException, ParserConfigurationException { + public Campaign doInBackground() throws Exception { //region Progress 0 //Initialize progress property. setProgress(0); + Eras.initializeEras(); + Factions.setInstance(Factions.loadDefault()); CurrencyManager.getInstance().loadCurrencies(); @@ -178,7 +164,7 @@ public Campaign doInBackground() throws IOException, CampaignXmlParseException, //region Progress 3 setProgress(3); - + Campaign campaign; boolean newCampaign = false; if (fileCampaign == null) { From fe79783f370ab8559f3e31fa928afdbac15d683e Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 14 Aug 2021 00:11:56 -0400 Subject: [PATCH 2/4] Applying changes from personal review --- MekHQ/data/universe/eras.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MekHQ/data/universe/eras.xml b/MekHQ/data/universe/eras.xml index 464e700828..39a2962947 100644 --- a/MekHQ/data/universe/eras.xml +++ b/MekHQ/data/universe/eras.xml @@ -3,7 +3,7 @@ Each Era consists of the following fields: Code: The unique String code for the era. Name: The display name for the era. -End: The end date for the era. This can be in the formats "yyyy-MM-dd", "yyyy-MM" (assuming first day of the month), and "yyyy" (assuming first day of the year) +End: The end date for the era. This can be in the formats "yyyy-MM-dd", "yyyy-MM" (assuming first day of the month), and "yyyy" (assuming first day of the year). One era should have this value missing, which will put it as the final era. Flag: The internal flags for the base eras. You can have multiple flag fields (multiple lines each with a single flag) per custom era, which isn't used in base MekHQ, but allows one to customize things. Note that internal use of this setup is minimal at best, at least as of 0.49.3. @@ -92,4 +92,4 @@ Note that internal use of this setup is minimal at best, at least as of 0.49.3. ilClan ILCLAN - \ No newline at end of file + From e7172dd95fc00e651f038e733af89b16df0e73b7 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 14 Aug 2021 07:03:04 -0400 Subject: [PATCH 3/4] 274: Adjusting dates to follow the MUL properly --- MekHQ/data/universe/eras.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/MekHQ/data/universe/eras.xml b/MekHQ/data/universe/eras.xml index 39a2962947..74d1bbe881 100644 --- a/MekHQ/data/universe/eras.xml +++ b/MekHQ/data/universe/eras.xml @@ -3,7 +3,7 @@ Each Era consists of the following fields: Code: The unique String code for the era. Name: The display name for the era. -End: The end date for the era. This can be in the formats "yyyy-MM-dd", "yyyy-MM" (assuming first day of the month), and "yyyy" (assuming first day of the year). One era should have this value missing, which will put it as the final era. +End: The end date for the era. This can be in the formats "yyyy-MM-dd", "yyyy-MM" (assuming first day of the month), and "yyyy" (assuming first day of the year). One era should have this value missing, which will put it as the final era. For the canon dates, these will be one year after their end date in the MUL, as that will swapover on the first day of the new era. Flag: The internal flags for the base eras. You can have multiple flag fields (multiple lines each with a single flag) per custom era, which isn't used in base MekHQ, but allows one to customize things. Note that internal use of this setup is minimal at best, at least as of 0.49.3. @@ -12,7 +12,7 @@ Note that internal use of this setup is minimal at best, at least as of 0.49.3. PSF Pre-Spaceflight - 1950 + 1951 PRE_SPACEFLIGHT @@ -24,67 +24,67 @@ Note that internal use of this setup is minimal at best, at least as of 0.49.3. AOW Age of War - 2570 + 2571 AGE_OF_WAR SL Star League - 2780 + 2781 STAR_LEAGUE ESW Early Succession Wars - 2900 + 2901 EARLY_SUCCESSION_WARS LSWL Late Succession War - LosTech - 3019 + 3020 LATE_SUCCESSION_WARS_LOSTECH LSWR Late Succession War - Renaissance - 3049 + 3050 LATE_SUCCESSION_WARS_RENAISSANCE CI Clan Invasion - 3061 + 3062 CLAN_INVASION CW Civil War - 3067 + 3068 CIVIL_WAR J Jihad - 3085 + 3081 JIHAD ER Early Republic - 3100 + 3101 EARLY_REPUBLIC LR Late Republic - 3130 + 3131 LATE_REPUBLIC DA Dark Ages - 3150 + 3151 DARK_AGES From 26a78def9f0e57b75cce0c4a905810b6bb01ba19 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 Aug 2021 21:47:13 -0400 Subject: [PATCH 4/4] Applying changes from review --- MekHQ/data/universe/eras.xml | 4 ++-- MekHQ/src/mekhq/campaign/universe/eras/Eras.java | 13 +++---------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/MekHQ/data/universe/eras.xml b/MekHQ/data/universe/eras.xml index 74d1bbe881..16d4db9c1c 100644 --- a/MekHQ/data/universe/eras.xml +++ b/MekHQ/data/universe/eras.xml @@ -41,13 +41,13 @@ Note that internal use of this setup is minimal at best, at least as of 0.49.3. LSWL - Late Succession War - LosTech + Late Succession Wars - LosTech 3020 LATE_SUCCESSION_WARS_LOSTECH LSWR - Late Succession War - Renaissance + Late Succession Wars - Renaissance 3050 LATE_SUCCESSION_WARS_RENAISSANCE diff --git a/MekHQ/src/mekhq/campaign/universe/eras/Eras.java b/MekHQ/src/mekhq/campaign/universe/eras/Eras.java index a30c1dba5d..f1b27cf571 100644 --- a/MekHQ/src/mekhq/campaign/universe/eras/Eras.java +++ b/MekHQ/src/mekhq/campaign/universe/eras/Eras.java @@ -41,7 +41,7 @@ public class Eras { //region Variable Declarations private static Eras instance; - private SortedMap eras; + private TreeMap eras; //endregion Variable Declarations //region Constructors @@ -63,7 +63,7 @@ public static void setInstance(final @Nullable Eras instance) { Eras.instance = instance; } - public SortedMap getEras() { + public TreeMap getEras() { return eras; } @@ -72,14 +72,7 @@ private void setEras(final TreeMap eras) { } public Era getEra(final LocalDate today) { - for (final Map.Entry entry : getEras().entrySet()) { - if (today.isBefore(entry.getKey())) { - return entry.getValue(); - } - } - - MekHQ.getLogger().error("Failed to determine a valid era for the date " + today); - return getEras().values().stream().findFirst().orElse(new Era()); + return getEras().ceilingEntry(today).getValue(); } //endregion Getters/Setters