diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index eb381982f23..0c2f9f5fb5a 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -138,6 +138,7 @@ import mekhq.campaign.universe.DefaultPlanetSelector; import mekhq.campaign.universe.Era; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.IUnitGenerator; import mekhq.campaign.universe.News; import mekhq.campaign.universe.NewsItem; @@ -3901,7 +3902,7 @@ public void setGMMode(boolean b) { } public Faction getFaction() { - return Faction.getFaction(factionCode); + return Factions.getInstance().getFaction(factionCode); } public String getFactionName() { diff --git a/MekHQ/src/mekhq/campaign/againstTheBot/AtBConfiguration.java b/MekHQ/src/mekhq/campaign/againstTheBot/AtBConfiguration.java index 10b6c36bcd9..5a8abbe3fe8 100644 --- a/MekHQ/src/mekhq/campaign/againstTheBot/AtBConfiguration.java +++ b/MekHQ/src/mekhq/campaign/againstTheBot/AtBConfiguration.java @@ -54,6 +54,7 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; /** * @author Neoancient @@ -291,7 +292,7 @@ public static int decodeWeightStr(String s, int i) { public static String getParentFactionType(String factionCode) { String org = AtBConfiguration.ORG_IS; - Faction faction = Faction.getFaction(factionCode); + Faction faction = Factions.getInstance().getFaction(factionCode); if (faction.isComStar()) { org = AtBConfiguration.ORG_CS; diff --git a/MekHQ/src/mekhq/campaign/finances/CurrencyManager.java b/MekHQ/src/mekhq/campaign/finances/CurrencyManager.java index 5a6c1cb7d60..659e8d850e6 100644 --- a/MekHQ/src/mekhq/campaign/finances/CurrencyManager.java +++ b/MekHQ/src/mekhq/campaign/finances/CurrencyManager.java @@ -26,6 +26,7 @@ import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.Contract; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.PlanetarySystem; import org.joda.money.CurrencyUnitDataProvider; @@ -170,7 +171,7 @@ synchronized Currency getDefaultCurrency() { for (Contract contract : this.campaign.getActiveContracts()) { if (contract instanceof AtBContract) { Currency currency = possibleCurrencies.getOrDefault( - Faction.getFaction(((AtBContract)contract).getEmployerCode()).getCurrencyCode(), + Factions.getInstance().getFaction(((AtBContract)contract).getEmployerCode()).getCurrencyCode(), null); if (currency != null) { @@ -253,7 +254,7 @@ protected void registerCurrencies() { // Adjust the currency start and end dates if needed by the // start/end dates of the factions that use it - for (Faction faction : Faction.getFactions()) { + for (Faction faction : Factions.getInstance().getFactions()) { if (faction.getCurrencyCode().equals(code)) { if (faction.getStartYear() < startYear) { startYear = faction.getStartYear(); diff --git a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java index 5f487e4139b..2dc1fd6f669 100644 --- a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java +++ b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java @@ -1510,8 +1510,6 @@ private static boolean isLegacyMissingMASC(Part p) { } private static void updatePlanetaryEventsFromXML(Node wn) { - Systems.reload(true); - List events; Map> eventsMap = new HashMap<>(); diff --git a/MekHQ/src/mekhq/campaign/market/ContractMarket.java b/MekHQ/src/mekhq/campaign/market/ContractMarket.java index c31bd8f29a7..2896720293e 100644 --- a/MekHQ/src/mekhq/campaign/market/ContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/ContractMarket.java @@ -47,6 +47,7 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.PlanetarySystem; import mekhq.campaign.universe.RandomFactionGenerator; import mekhq.campaign.universe.Systems; @@ -356,7 +357,7 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u contract.setEmployerCode(employer, campaign.getGameYear()); contract.setMissionType(findAtBMissionType(unitRatingMod, RandomFactionGenerator.getInstance().getFactionHints() - .isISMajorPower(Faction.getFaction(contract.getEmployerCode())))); + .isISMajorPower(Factions.getInstance().getFaction(contract.getEmployerCode())))); if (contract.getMissionType() == AtBContract.MT_PIRATEHUNTING) { contract.setEnemyCode("PIR"); @@ -374,9 +375,9 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u * (ComStar, Mercs not under contract) are more likely to have garrison-type * contracts and less likely to have battle-type contracts unless at war. */ - if (RandomFactionGenerator.getInstance().getFactionHints().isNeutral(Faction.getFaction(employer)) && - !RandomFactionGenerator.getInstance().getFactionHints().isAtWarWith(Faction.getFaction(employer), - Faction.getFaction(contract.getEnemyCode()), campaign.getLocalDate())) { + if (RandomFactionGenerator.getInstance().getFactionHints().isNeutral(Factions.getInstance().getFaction(employer)) && + !RandomFactionGenerator.getInstance().getFactionHints().isAtWarWith(Factions.getInstance().getFaction(employer), + Factions.getInstance().getFaction(contract.getEnemyCode()), campaign.getLocalDate())) { if (contract.getMissionType() == AtBContract.MT_PLANETARYASSAULT) { contract.setMissionType(AtBContract.MT_GARRISONDUTY); } else if (contract.getMissionType() == AtBContract.MT_RELIEFDUTY) { @@ -430,9 +431,9 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u contract.calculateContract(campaign); //Ralgith had a version of this then the PR got added. Commenting this Out. -/* contract.setName(Faction.getFaction(employer).getShortName() + "-" + String.format("%1$tY%1$tm", contract.getStartDate()) +/* contract.setName(Factions.getInstance().getFaction(employer).getShortName() + "-" + String.format("%1$tY%1$tm", contract.getStartDate()) + "-" + AtBContract.missionTypeNames[contract.getMissionType()] - + "-" + Faction.getFaction(contract.getEnemyCode()).getShortName() + + "-" + Factions.getInstance().getFaction(contract.getEnemyCode()).getShortName() + "-" + contract.getLength());*/ return contract; @@ -444,7 +445,7 @@ protected AtBContract generateAtBSubcontract(Campaign campaign, contract.setEmployerCode(parent.getEmployerCode(), campaign.getGameYear()); contract.setMissionType(findAtBMissionType(unitRatingMod, RandomFactionGenerator.getInstance().getFactionHints() - .isISMajorPower(Faction.getFaction(contract.getEmployerCode())))); + .isISMajorPower(Factions.getInstance().getFaction(contract.getEmployerCode())))); if (contract.getMissionType() == AtBContract.MT_PIRATEHUNTING) contract.setEnemyCode("PIR"); @@ -612,13 +613,13 @@ public void setAllyRating(AtBContract contract, boolean isAttacker, int year) { if (contract.getMissionType() == AtBContract.MT_PLANETARYASSAULT) { mod += 1; } - if (Faction.getFaction(contract.getEmployerCode()).isClan() && !isAttacker) { + if (Factions.getInstance().getFaction(contract.getEmployerCode()).isClan() && !isAttacker) { //facing front-line units mod += 1; } contract.setAllySkill(getSkillRating(Compute.d6(2) + mod)); if (year > 2950 && year < 3039 && - !Faction.getFaction(contract.getEmployerCode()).isClan()) { + !Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) { mod -= 1; } contract.setAllyQuality(getQualityRating(Compute.d6(2) + mod)); @@ -639,12 +640,12 @@ public void setEnemyRating(AtBContract contract, boolean isAttacker, int year) { if (AtBContract.isMinorPower(contract.getEmployerCode())) { mod -= 1; } - if (Faction.getFaction(contract.getEmployerCode()).isClan()) { + if (Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) { mod += isAttacker?2:4; } contract.setEnemySkill(getSkillRating(Compute.d6(2) + mod)); if (year > 2950 && year < 3039 && - !Faction.getFaction(contract.getEnemyCode()).isClan()) { + !Factions.getInstance().getFaction(contract.getEnemyCode()).isClan()) { mod -= 1; } contract.setEnemyQuality(getQualityRating(Compute.d6(2) + mod)); @@ -718,8 +719,8 @@ protected void setAtBContractClauses(AtBContract contract, int unitRatingMod, Ca mods.mods[Compute.randomInt(4)] -= 1; } - if (Faction.getFaction(contract.getEnemyCode()).isClan() && - !Faction.getFaction(contract.getEmployerCode()).isClan()) { + if (Factions.getInstance().getFaction(contract.getEnemyCode()).isClan() && + !Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) { for (int i = 0; i < 4; i++) if (i == CLAUSE_SALVAGE) mods.mods[i] -= 2; else mods.mods[i] += 1; @@ -740,7 +741,7 @@ protected void setAtBContractClauses(AtBContract contract, int unitRatingMod, Ca } if (RandomFactionGenerator.getInstance().getFactionHints() - .isISMajorPower(Faction.getFaction(contract.getEmployerCode()))) { + .isISMajorPower(Factions.getInstance().getFaction(contract.getEmployerCode()))) { mods.mods[CLAUSE_SALVAGE] += -1; mods.mods[CLAUSE_TRANSPORT] += 1; } diff --git a/MekHQ/src/mekhq/campaign/market/UnitMarket.java b/MekHQ/src/mekhq/campaign/market/UnitMarket.java index 235c49dafda..6dd4627a47e 100644 --- a/MekHQ/src/mekhq/campaign/market/UnitMarket.java +++ b/MekHQ/src/mekhq/campaign/market/UnitMarket.java @@ -46,6 +46,7 @@ import mekhq.campaign.mission.Mission; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.IUnitGenerator; import mekhq.campaign.universe.RandomFactionGenerator; import mekhq.campaign.universe.UnitGeneratorParameters; @@ -136,7 +137,7 @@ public void generateUnitOffers(Campaign campaign) { if (campaign.getUnitRatingMod() >= IUnitRating.DRAGOON_B) { Set factions = campaign.getCurrentSystem().getFactionSet(campaign.getLocalDate()); String faction = Utilities.getRandomItem(factions).getShortName(); - if (campaign.getFaction().isClan() || !Faction.getFaction(faction).isClan()) { + if (campaign.getFaction().isClan() || !Factions.getInstance().getFaction(faction).isClan()) { addOffers(campaign, Compute.d6() - 3, UnitMarketType.FACTORY, UnitType.MEK, faction, IUnitRating.DRAGOON_A, 6); addOffers(campaign, Compute.d6() - 2, UnitMarketType.FACTORY, UnitType.TANK, diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index cbe3391f1ce..1b089df6bed 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -53,6 +53,7 @@ import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.RandomFactionGenerator; /** @@ -340,7 +341,7 @@ public static int getEffectiveNumUnits(Campaign campaign) { } public static boolean isMinorPower(String fName) { - Faction faction = Faction.getFaction(fName); + Faction faction = Factions.getInstance().getFaction(fName); if (null != faction) { return !RandomFactionGenerator.getInstance().getFactionHints().isISMajorPower(faction) && !faction.isClan(); @@ -397,7 +398,7 @@ public void calculatePaymentMultiplier(Campaign campaign) { break; } - Faction employer = Faction.getFaction(employerCode); + Faction employer = Factions.getInstance().getFaction(employerCode); if ((null != employer) && (RandomFactionGenerator.getInstance().getFactionHints().isISMajorPower(employer) || employer.isClan())) { @@ -494,7 +495,7 @@ public void checkMorale(LocalDate today, int dragoonRating) { isMinorPower(enemyCode) || enemyCode.equals("MERC")) { mod -= 1; - } else if (Faction.getFaction(enemyCode).isClan()) { + } else if (Factions.getInstance().getFaction(enemyCode).isClan()) { mod += 2; } @@ -1021,8 +1022,8 @@ public int findBigBattleType() { public boolean contractExtended (Campaign campaign) { if ((getMissionType() != MT_PIRATEHUNTING) && (getMissionType() != MT_RIOTDUTY)) { String warName = RandomFactionGenerator.getInstance() - .getFactionHints().getCurrentWar(Faction.getFaction(getEmployerCode()), - Faction.getFaction(getEnemyCode()), campaign.getLocalDate()); + .getFactionHints().getCurrentWar(Factions.getInstance().getFaction(getEmployerCode()), + Factions.getInstance().getFaction(getEnemyCode()), campaign.getLocalDate()); if (null != warName) { int extension = 0; int roll = Compute.d6(); @@ -1313,9 +1314,9 @@ public void setEmployerCode(String code, int year) { public String getEmployerName(int year) { if (mercSubcontract) { return "Mercenary (" + - Faction.getFaction(employerCode).getFullName(year) + ")"; + Factions.getInstance().getFaction(employerCode).getFullName(year) + ")"; } - return Faction.getFaction(employerCode).getFullName(year); + return Factions.getInstance().getFaction(employerCode).getFullName(year); } public String getEnemyCode() { @@ -1323,7 +1324,7 @@ public String getEnemyCode() { } public String getEnemyName(int year) { - return Faction.getFaction(enemyCode).getFullName(year); + return Factions.getInstance().getFaction(enemyCode).getFullName(year); } public void setEnemyCode(String enemyCode) { @@ -1566,7 +1567,7 @@ public AtBContract(Contract c, Campaign campaign) { missionType = MT_PLANETARYASSAULT; } } - Faction f = Faction.getFactionFromFullNameAndYear(c.getEmployer(), campaign.getGameYear()); + Faction f = Factions.getInstance().getFactionFromFullNameAndYear(c.getEmployer(), campaign.getGameYear()); if (null == f) { employerCode = "IND"; } else { diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index 12234c086cd..a1562cd85fa 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -79,6 +79,7 @@ import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Era; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.Faction.Tag; import mekhq.campaign.universe.IUnitGenerator; import mekhq.campaign.universe.Planet; @@ -1164,7 +1165,7 @@ public static List generateBAForNova(AtBScenario scenario, List // if yes, then pick the fastest mech and load it up, adding the generated BA to the transport relationships. // non-clan forces and units that aren't stars don't become novas - if (!Faction.getFaction(factionCode).isClan() && (starUnits.size() != 5)) { + if (!Factions.getInstance().getFaction(factionCode).isClan() && (starUnits.size() != 5)) { return transportedUnits; } @@ -1290,7 +1291,7 @@ public static Entity createEntityWithCrew(String factionCode, int skill, Campaig en.setOwner(campaign.getPlayer()); en.setGame(campaign.getGame()); - Faction faction = Faction.getFaction(factionCode); + Faction faction = Factions.getInstance().getFaction(factionCode); RandomNameGenerator rng = RandomNameGenerator.getInstance(); rng.setChosenFaction(faction.getNameGenerator()); @@ -1446,7 +1447,7 @@ private static List generateUnitTypes(int unitTypeCode, int unitCount, int actualUnitType = unitTypeCode; if (unitTypeCode == ScenarioForceTemplate.SPECIAL_UNIT_TYPE_ATB_MIX) { - Faction faction = Faction.getFaction(factionCode); + Faction faction = Factions.getInstance().getFaction(factionCode); // "AtB Mix" will skip vehicles if the "use vehicles" checkbox is turned off // or if the faction is clan and "clan opfors use vehicles" is turned off @@ -1544,7 +1545,7 @@ private static List generateClanUnitTypes(int unitCount, int forceQuali * @return Unit weight string. */ private static String generateUnitWeights(List unitTypes, String faction, int weightClass, int maxWeight, int minWeight, Campaign campaign) { - Faction genFaction = Faction.getFaction(faction); + Faction genFaction = Factions.getInstance().getFaction(faction); String factionWeightString = AtBConfiguration.ORG_IS; if (genFaction.isClan() || faction.equals("MH")) { factionWeightString = AtBConfiguration.ORG_CLAN; @@ -2219,7 +2220,7 @@ private static void setScenarioRerolls(AtBDynamicScenario scenario, Campaign cam * @return "Lance" size. */ public static int getLanceSize(String factionCode) { - Faction faction = Faction.getFaction(factionCode); + Faction faction = Factions.getInstance().getFaction(factionCode); if (faction != null) { // clans and marian hegemony use a fundamental unit size of 5. if (faction.isClan() || factionCode.equals("MH")) { @@ -2344,7 +2345,7 @@ private static String getPlanetOwnerFaction(AtBContract contract, LocalDate curr List planetFactions = contract.getSystem().getFactions(currentDate); if (planetFactions != null && !planetFactions.isEmpty()) { factionCode = planetFactions.get(0); - Faction ownerFaction = Faction.getFaction(factionCode); + Faction ownerFaction = Factions.getInstance().getFaction(factionCode); if (ownerFaction.is(Tag.ABANDONED)) { factionCode = "MERC"; diff --git a/MekHQ/src/mekhq/campaign/mission/AtBScenario.java b/MekHQ/src/mekhq/campaign/mission/AtBScenario.java index 3efb209e648..fa643e9d76d 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBScenario.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBScenario.java @@ -59,7 +59,7 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.unit.Unit; -import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.Planet; import mekhq.campaign.universe.PlanetarySystem; import mekhq.campaign.universe.Systems; @@ -1038,7 +1038,7 @@ protected void addLance(List list, String faction, */ private void addLance(List list, String faction, int skill, int quality, int weightClass, int maxWeight, Campaign campaign, int arrivalTurn) { - if (Faction.getFaction(faction).isClan()) { + if (Factions.getInstance().getFaction(faction).isClan()) { addStar(list, faction, skill, quality, weightClass, maxWeight, campaign, arrivalTurn); return; } diff --git a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java index 9680dd72b51..cb53eb10849 100644 --- a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java +++ b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioModifierApplicator.java @@ -42,7 +42,7 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.unit.Unit; -import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; public class AtBScenarioModifierApplicator { @@ -191,7 +191,7 @@ public static void adjustSkill(AtBDynamicScenario scenario, Campaign campaign, rsg.setMethod(RandomSkillsGenerator.M_TAHARQA); rsg.setLevel(adjustedSkill); - if (Faction.getFaction(scenario.getContract(campaign).getEnemyCode()).isClan()) { + if (Factions.getInstance().getFaction(scenario.getContract(campaign).getEnemyCode()).isClan()) { rsg.setType(RandomSkillsGenerator.T_CLAN); } diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index 2e6ced31ffc..43b5cfde6e8 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -63,6 +63,7 @@ import mekhq.campaign.unit.Unit; import mekhq.campaign.work.IPartWork; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.Planet; /** @@ -343,7 +344,7 @@ public Person(String givenName, String surname, String honorific, Campaign campa secondaryDesignator = ROMDesignation.NONE; commander = false; dependent = false; - originFaction = Faction.getFaction(factionCode); + originFaction = Factions.getInstance().getFaction(factionCode); originPlanet = null; clan = originFaction.isClan(); phenotype = Phenotype.NONE; @@ -1927,7 +1928,7 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio } else if (wn2.getNodeName().equalsIgnoreCase("dependent")) { retVal.dependent = Boolean.parseBoolean(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("faction")) { - retVal.originFaction = Faction.getFaction(wn2.getTextContent().trim()); + retVal.originFaction = Factions.getInstance().getFaction(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("planetId")) { String systemId = wn2.getAttributes().getNamedItem("systemId").getTextContent().trim(); String planetId = wn2.getTextContent().trim(); diff --git a/MekHQ/src/mekhq/campaign/universe/DefaultFactionSelector.java b/MekHQ/src/mekhq/campaign/universe/DefaultFactionSelector.java index 9e1ae5ad00b..b2274a9c491 100644 --- a/MekHQ/src/mekhq/campaign/universe/DefaultFactionSelector.java +++ b/MekHQ/src/mekhq/campaign/universe/DefaultFactionSelector.java @@ -45,7 +45,7 @@ public DefaultFactionSelector(String factionCode) { @Override public Faction selectFaction(Campaign campaign) { if (factionCode != null) { - return Faction.getFaction(factionCode); + return Factions.getInstance().getFaction(factionCode); } else { return campaign.getFaction(); } diff --git a/MekHQ/src/mekhq/campaign/universe/Faction.java b/MekHQ/src/mekhq/campaign/universe/Faction.java index eb99b6c77cf..e40d8bed50f 100644 --- a/MekHQ/src/mekhq/campaign/universe/Faction.java +++ b/MekHQ/src/mekhq/campaign/universe/Faction.java @@ -22,14 +22,12 @@ package mekhq.campaign.universe; import java.awt.Color; -import java.io.FileInputStream; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -37,16 +35,10 @@ import java.util.Set; import java.util.TreeMap; -import javax.xml.parsers.DocumentBuilder; - import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import megamek.client.ratgenerator.FactionRecord; -import megamek.client.ratgenerator.RATGenerator; import megamek.common.EquipmentType; import mekhq.MekHQ; import mekhq.MekHqXmlUtil; @@ -59,10 +51,6 @@ * @author Jay Lawson */ public class Faction { - private static Map factions; - private static Map factionIdMap; - private static List choosableFactionCodes = Collections.singletonList("MERC"); - private String shortName; private String fullName; private NavigableMap nameChanges = new TreeMap<>(); @@ -95,16 +83,6 @@ public Faction(String shortName, String fullName) { end = 9999; } - public static List getChoosableFactionCodes() { - return choosableFactionCodes; - } - - public static void setChoosableFactionCodes(String... choosableFactionCodes) { - if (choosableFactionCodes.length > 0) { - Faction.choosableFactionCodes = Arrays.asList(choosableFactionCodes); - } - } - public String getShortName() { return shortName; } @@ -265,75 +243,7 @@ public boolean hasName(String name) { return false; } - public static Collection getFactions() { - return factions.values(); - } - - public static Collection getFactionList() { - return new ArrayList<>(factions.keySet()); - } - - public static Faction getFaction(String sname) { - Faction defaultFaction = new Faction(); - if (factions == null) { - return defaultFaction; - } else { - return factions.getOrDefault(sname, defaultFaction); - } - } - - public static Faction getFactionFromFullName(String fname, int year) { - return getFactionFromFullNameAndYear(fname, year); - } - - public static Faction getFactionFromFullNameAndYear(String fname, int year) { - Faction faction = null; - for (Faction f : factions.values()) { - if (f.getFullName(year).equals(fname)) { - faction = f; - break; - } - } - return faction; - } - - /** - * Helper function that gets the faction record for the specified faction, or a fallback general faction record. - * Useful for RAT generator activity. - * @param faction The faction whose MegaMek faction record to retrieve. - * @return Found faction record or null. - */ - public static FactionRecord getFactionRecordOrFallback(String faction) { - FactionRecord fRec = RATGenerator.getInstance().getFaction(faction); - if (fRec == null) { - Faction f = Faction.getFaction(faction); - if (f != null) { - if (f.isPeriphery()) { - fRec = RATGenerator.getInstance().getFaction("Periphery"); - } else if (f.isClan()) { - fRec = RATGenerator.getInstance().getFaction("CLAN"); - } else { - fRec = RATGenerator.getInstance().getFaction("IS"); - } - } - - if (fRec == null) { - MekHQ.getLogger().error(RATGenerator.class, "getFactionRecordOrFallback", - "Could not locate faction record for " + faction); - } - } - - return fRec; - } - - public static String getFactionCode(int faction) { - Faction f = factionIdMap.get(faction); - return (null != f) ? f.getShortName() : "IND"; - } - public static Faction getFactionFromXML(Node wn) throws DOMException { - final String METHOD_NAME = "getFactionFromXML(Node)"; - Faction retVal = new Faction(); NodeList nl = wn.getChildNodes(); @@ -397,86 +307,12 @@ public static Faction getFactionFromXML(Node wn) throws DOMException { } if ((retVal.eraMods != null) && (retVal.eraMods.length < 9)) { - MekHQ.getLogger().warning(Faction.class, METHOD_NAME, - retVal.fullName + " faction did not have a long enough eraMods vector"); + MekHQ.getLogger().warning(retVal.fullName + " faction did not have a long enough eraMods vector"); } return retVal; } - public static void generateFactions() throws DOMException { - final String METHOD_NAME = "generateFactions()"; - - MekHQ.getLogger().info(Faction.class, METHOD_NAME, "Starting load of faction data from XML..."); - // Initialize variables. - factions = new HashMap<>(); - factionIdMap = new HashMap<>(); - - Document xmlDoc; - - try (FileInputStream fis = new FileInputStream("data/universe/factions.xml")) { - // Using factory get an instance of document builder - DocumentBuilder db = MekHqXmlUtil.newSafeDocumentBuilder(); - - // Parse using builder to get DOM representation of the XML file - xmlDoc = db.parse(fis); - } catch (Exception ex) { - MekHQ.getLogger().error(Faction.class, METHOD_NAME, ex); - return; - } - - Element factionEle = xmlDoc.getDocumentElement(); - NodeList nl = factionEle.getChildNodes(); - - // Get rid of empty text nodes and adjacent text nodes... - // Stupid weird parsing of XML. At least this cleans it up. - factionEle.normalize(); - - // Okay, lets iterate through the children, eh? - for (int x = 0; x < nl.getLength(); x++) { - Node wn = nl.item(x); - - if (!wn.getParentNode().equals(factionEle)) { - continue; - } - - int xc = wn.getNodeType(); - - if (xc == Node.ELEMENT_NODE) { - // This is what we really care about. - // All the meat of our document is in this node type, at this - // level. - // Okay, so what element is it? - String xn = wn.getNodeName(); - - if (xn.equalsIgnoreCase("faction")) { - Faction f = getFactionFromXML(wn); - if (!factions.containsKey(f.getShortName())) { - factions.put(f.getShortName(), f); - if (null != f.getId()) { - if (!factionIdMap.containsKey(f.getId())) { - factionIdMap.put(f.getId(), f); - } else { - MekHQ.getLogger().error(Faction.class, METHOD_NAME, - String.format("Faction id \"%d\" already used for faction %s, can't re-use it for %s", - f.getId(), factionIdMap.get(f.getId()).getFullName(0), - f.getFullName(0))); - } - } - } else { - MekHQ.getLogger().error(Faction.class, METHOD_NAME, - String.format("Faction code \"%s\" already used for faction %s, can't re-use it for %s", - f.getShortName(), factions.get(f.getShortName()).getFullName(0), f.getFullName(0))); - } - } else if (xn.equalsIgnoreCase("choosableFactionCodes")) { - setChoosableFactionCodes(wn.getTextContent().split(",")); - } - } - } - MekHQ.getLogger().info(Faction.class, METHOD_NAME, - "Loaded a total of " + factions.size() + " factions"); - } - /** @return Sorted list of faction names as one string */ public static String getFactionNames(Collection factions, int year) { if (null == factions) { diff --git a/MekHQ/src/mekhq/campaign/universe/FactionBorderTracker.java b/MekHQ/src/mekhq/campaign/universe/FactionBorderTracker.java index 416661e9dc5..5ae473d8bc0 100644 --- a/MekHQ/src/mekhq/campaign/universe/FactionBorderTracker.java +++ b/MekHQ/src/mekhq/campaign/universe/FactionBorderTracker.java @@ -249,7 +249,7 @@ public synchronized Set getFactionsInRegion() { Thread.currentThread().interrupt(); } } - Faction f = Faction.getFaction(fKey); + Faction f = Factions.getInstance().getFaction(fKey); if (null != f) { return borders.get(f); } diff --git a/MekHQ/src/mekhq/campaign/universe/FactionHints.java b/MekHQ/src/mekhq/campaign/universe/FactionHints.java index 48009b79abd..f204017974d 100644 --- a/MekHQ/src/mekhq/campaign/universe/FactionHints.java +++ b/MekHQ/src/mekhq/campaign/universe/FactionHints.java @@ -478,7 +478,7 @@ private void loadFactionHints() throws DOMException, ParseException { if (nodeName.equals("neutral")) { String fKey = wn.getAttributes().getNamedItem("faction").getTextContent().trim(); - Faction f = Faction.getFaction(fKey); + Faction f = Factions.getInstance().getFaction(fKey); if (null != f) { neutralFactions.add(f); addNeutralExceptions(f, wn); @@ -488,7 +488,7 @@ private void loadFactionHints() throws DOMException, ParseException { } } else if (nodeName.equals("deepPeriphery")) { for (String fKey : wn.getTextContent().trim().split(",")) { - Faction f = Faction.getFaction(fKey); + Faction f = Factions.getInstance().getFaction(fKey); if (null != f) { deepPeriphery.add(f); } else { @@ -498,7 +498,7 @@ private void loadFactionHints() throws DOMException, ParseException { } } else if (nodeName.equals("majorPowers")) { for (String fKey : wn.getTextContent().trim().split(",")) { - Faction f = Faction.getFaction(fKey); + Faction f = Factions.getInstance().getFaction(fKey); if (null != f) { majorPowers.add(f); } else { @@ -528,15 +528,15 @@ private void loadFactionHints() throws DOMException, ParseException { for (int j = 0; j < wn.getChildNodes().getLength(); j++) { Node wn2 = wn.getChildNodes().item(j); if (wn2.getNodeName().equals("outer")) { - outer = Faction.getFaction(wn2.getTextContent().trim()); + outer = Factions.getInstance().getFaction(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equals("inner")) { - inner = Faction.getFaction(wn2.getTextContent().trim()); + inner = Factions.getInstance().getFaction(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equals("fraction")) { fraction = Double.parseDouble(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equals("opponents")) { opponents = new ArrayList<>(); for (String fKey : wn2.getTextContent().trim().split(",")) { - Faction f = Faction.getFaction(fKey); + Faction f = Factions.getInstance().getFaction(fKey); if (null != f) { opponents.add(f); } @@ -584,7 +584,7 @@ private void setFactionHint(Map>> hint, String[] factionKeys = wn.getTextContent().trim().split(","); Faction[] parties = new Faction[factionKeys.length]; for (int i = 0; i < factionKeys.length; i++) { - parties[i] = Faction.getFaction(factionKeys[i]); + parties[i] = Factions.getInstance().getFaction(factionKeys[i]); if (null == parties[i]) { MekHQ.getLogger().error(getClass(), METHOD_NAME, "Invalid faction code in factionhints.xml: " + parties[i]); //$NON-NLS-1$ @@ -617,7 +617,7 @@ private void addNeutralExceptions(Faction faction, Node node) throws DOMExceptio String[] parties = wn.getTextContent().trim().split(","); for (String party : parties) { - final Faction f = Faction.getFaction(party); + final Faction f = Factions.getInstance().getFaction(party); if (null == f) { MekHQ.getLogger().error(getClass(), METHOD_NAME, "Invalid faction code in factionhints.xml: " + party); //$NON-NLS-1$ diff --git a/MekHQ/src/mekhq/campaign/universe/Factions.java b/MekHQ/src/mekhq/campaign/universe/Factions.java new file mode 100644 index 00000000000..8ae284d5ae2 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/universe/Factions.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2020 - 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; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import megamek.client.ratgenerator.FactionRecord; +import megamek.client.ratgenerator.RATGenerator; +import megamek.common.annotations.Nullable; +import mekhq.MekHQ; +import mekhq.MekHqXmlUtil; + +public class Factions { + private static Factions instance; + + private Map factions = new HashMap<>(); + private Map factionIdMap = new HashMap<>(); + private List choosableFactionCodes = Collections.singletonList("MERC"); + + private RATGenerator ratGenerator; + + public Factions() { + this(RATGenerator.getInstance()); + } + + public Factions(RATGenerator ratGenerator) { + this.ratGenerator = Objects.requireNonNull(ratGenerator); + } + + public static Factions getInstance() { + if (instance == null) { + instance = new Factions(); + } + + return instance; + } + + public static void setInstance(@Nullable Factions instance) { + Factions.instance = instance; + } + + public RATGenerator getRATGenerator() { + return ratGenerator; + } + + public void setRATGenerator(RATGenerator ratGenerator) { + this.ratGenerator = Objects.requireNonNull(ratGenerator); + } + + public List getChoosableFactionCodes() { + return choosableFactionCodes; + } + + public void setChoosableFactionCodes(String... choosableFactionCodes) { + if (choosableFactionCodes.length > 0) { + this.choosableFactionCodes = Arrays.asList(choosableFactionCodes); + } + } + + public Collection getFactions() { + return factions.values(); + } + + public Collection getFactionList() { + return new ArrayList<>(factions.keySet()); + } + + public Faction getFaction(String sname) { + Faction defaultFaction = new Faction(); + if (factions == null) { + return defaultFaction; + } else { + return factions.getOrDefault(sname, defaultFaction); + } + } + + public Faction getFactionFromFullName(String fname, int year) { + return getFactionFromFullNameAndYear(fname, year); + } + + public Faction getFactionFromFullNameAndYear(String fname, int year) { + Faction faction = null; + for (Faction f : factions.values()) { + if (f.getFullName(year).equals(fname)) { + faction = f; + break; + } + } + return faction; + } + + /** + * Helper function that gets the faction record for the specified faction, or a + * fallback general faction record. Useful for RAT generator activity. + * + * @param faction The faction whose MegaMek faction record to retrieve. + * @return Found faction record or null. + */ + public FactionRecord getFactionRecordOrFallback(String faction) { + FactionRecord fRec = ratGenerator.getFaction(faction); + if (fRec == null) { + Faction f = getFaction(faction); + if (f != null) { + if (f.isPeriphery()) { + fRec = ratGenerator.getFaction("Periphery"); + } else if (f.isClan()) { + fRec = ratGenerator.getFaction("CLAN"); + } else { + fRec = ratGenerator.getFaction("IS"); + } + } + + if (fRec == null) { + MekHQ.getLogger().error("Could not locate faction record for " + faction); + } + } + + return fRec; + } + + public String getFactionCode(int faction) { + Faction f = factionIdMap.get(faction); + return (null != f) ? f.getShortName() : "IND"; + } + + /** + * Loads the default Factions data. + * + * @throws DOMException + * @throws ParserConfigurationException + * @throws IOException + * @throws SAXException + */ + public static Factions loadDefault() + throws DOMException, SAXException, IOException, ParserConfigurationException { + MekHQ.getLogger().info("Starting load of faction data from XML..."); + + Factions factions = load("data/universe/factions.xml"); + + MekHQ.getLogger().info("Loaded a total of " + factions.factions.size() + " factions"); + + return factions; + } + + /** + * Loads Factions data from a file. + * + * @param factionsPath The path to the XML file containing Factions data. + * + * @throws DOMException + * @throws ParserConfigurationException + * @throws IOException + * @throws SAXException + */ + public static Factions load(String factionsPath) + throws DOMException, SAXException, IOException, ParserConfigurationException { + Factions retVal = new Factions(); + + Document xmlDoc; + + try (FileInputStream fis = new FileInputStream(factionsPath)) { + // Using factory get an instance of document builder + DocumentBuilder db = MekHqXmlUtil.newSafeDocumentBuilder(); + + // Parse using builder to get DOM representation of the XML file + xmlDoc = db.parse(fis); + } + + Element factionEle = xmlDoc.getDocumentElement(); + NodeList nl = factionEle.getChildNodes(); + + // Get rid of empty text nodes and adjacent text nodes... + // Stupid weird parsing of XML. At least this cleans it up. + factionEle.normalize(); + + // Okay, lets iterate through the children, eh? + for (int x = 0; x < nl.getLength(); x++) { + Node wn = nl.item(x); + + if (!wn.getParentNode().equals(factionEle)) { + continue; + } + + int xc = wn.getNodeType(); + + if (xc == Node.ELEMENT_NODE) { + // This is what we really care about. + // All the meat of our document is in this node type, at this + // level. + // Okay, so what element is it? + String xn = wn.getNodeName(); + + if (xn.equalsIgnoreCase("faction")) { + Faction f = Faction.getFactionFromXML(wn); + if (!retVal.factions.containsKey(f.getShortName())) { + retVal.factions.put(f.getShortName(), f); + if (null != f.getId()) { + if (!retVal.factionIdMap.containsKey(f.getId())) { + retVal.factionIdMap.put(f.getId(), f); + } else { + MekHQ.getLogger().error( + String.format("Faction id \"%d\" already used for faction %s, can't re-use it for %s", + f.getId(), retVal.factionIdMap.get(f.getId()).getFullName(0), + f.getFullName(0))); + } + } + } else { + MekHQ.getLogger().error( + String.format("Faction code \"%s\" already used for faction %s, can't re-use it for %s", + f.getShortName(), retVal.factions.get(f.getShortName()).getFullName(0), f.getFullName(0))); + } + } else if (xn.equalsIgnoreCase("choosableFactionCodes")) { + retVal.setChoosableFactionCodes(wn.getTextContent().split(",")); + } + } + } + + return retVal; + } +} diff --git a/MekHQ/src/mekhq/campaign/universe/Planet.java b/MekHQ/src/mekhq/campaign/universe/Planet.java index a1905e40610..b52e48f4c08 100644 --- a/MekHQ/src/mekhq/campaign/universe/Planet.java +++ b/MekHQ/src/mekhq/campaign/universe/Planet.java @@ -254,7 +254,7 @@ public Planet(String tsvData, List years) throws Exception { // there are a few situations where all this stuff with parens is for naught, which is // PlanetName (FactionCode) or if the PlanetName (AltName) is already in our planets "database" - if ((null == Faction.getFaction(altName)) && (null == Systems.getInstance().getSystemById(primaryName))) { + if ((null == Factions.getInstance().getFaction(altName)) && (null == Systems.getInstance().getSystemById(primaryName))) { primaryName = nameString.substring(0, parenIndex - 1); PlanetaryEvent nameChangeEvent = getOrCreateEvent(nameChangeYearDate); @@ -705,7 +705,7 @@ private static Set getFactionsFrom(Collection codes) { } Set factions = new HashSet<>(codes.size()); for (String code : codes) { - factions.add(Faction.getFaction(code)); + factions.add(Factions.getInstance().getFaction(code)); } return factions; } @@ -950,7 +950,7 @@ public String updateFromTSVPlanet(final Planet tsvPlanet, boolean dryRun) { if ((event.faction != null) && (event.faction.size() > 0)) { // the purpose of this code is to evaluate whether the current "other planet" event is // a faction change to an active, valid faction. - Faction eventFaction = Faction.getFaction(event.faction.get(0)); + Faction eventFaction = Factions.getInstance().getFaction(event.faction.get(0)); boolean eventHasActualFaction = eventFaction != null && (!eventFaction.is(Tag.INACTIVE) && !eventFaction.is(Tag.ABANDONED)); if (eventHasActualFaction) { @@ -959,8 +959,8 @@ public String updateFromTSVPlanet(final Planet tsvPlanet, boolean dryRun) { // if this planet has an "inactive and abandoned" current faction... // we also want to catch the situation where the next faction change isn't to the same exact faction if ((currentFactions.size() == 1) - && Faction.getFaction(currentFactions.get(0)).is(Tag.INACTIVE) - && Faction.getFaction(currentFactions.get(0)).is(Tag.ABANDONED)) { + && Factions.getInstance().getFaction(currentFactions.get(0)).is(Tag.INACTIVE) + && Factions.getInstance().getFaction(currentFactions.get(0)).is(Tag.ABANDONED)) { // now we travel into the future, to the next "other" event, and if this planet has acquired a faction // before the next "other" event, then we @@ -977,8 +977,8 @@ public String updateFromTSVPlanet(final Planet tsvPlanet, boolean dryRun) { List nextFactions = this.getFactions(nextEventDate); boolean factionBeforeNextEvent = !(nextFactions.size() == 1 && - Faction.getFaction(nextFactions.get(0)).is(Tag.INACTIVE) && - Faction.getFaction(nextFactions.get(0)).is(Tag.ABANDONED)); + Factions.getInstance().getFaction(nextFactions.get(0)).is(Tag.INACTIVE) && + Factions.getInstance().getFaction(nextFactions.get(0)).is(Tag.ABANDONED)); if (!factionBeforeNextEvent) { sb.append("Adding faction change in ").append(event.date.getYear()) @@ -1138,7 +1138,7 @@ private Set updateFactions(Set current, List codes, Li if (codes != otherCodes) { current = new HashSet<>(codes.size()); for (String code : codes) { - current.add(Faction.getFaction(code)); + current.add(Factions.getInstance().getFaction(code)); } } return current; diff --git a/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java b/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java index 024d296de3d..d31be97148b 100644 --- a/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java +++ b/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java @@ -205,7 +205,7 @@ public Set getFactionSet(LocalDate when) { //ignore cases where abandoned (ABN) is given in addition //to real factions if (factions.size() > 1) { - factions.remove(Faction.getFaction("ABN")); + factions.remove(Factions.getInstance().getFaction("ABN")); } return factions; } diff --git a/MekHQ/src/mekhq/campaign/universe/RATGeneratorConnector.java b/MekHQ/src/mekhq/campaign/universe/RATGeneratorConnector.java index 27e485b7a62..604d98fed28 100644 --- a/MekHQ/src/mekhq/campaign/universe/RATGeneratorConnector.java +++ b/MekHQ/src/mekhq/campaign/universe/RATGeneratorConnector.java @@ -47,14 +47,14 @@ public RATGeneratorConnector(int year) { try { Thread.sleep(50); } catch (InterruptedException e) { - MekHQ.getLogger().error(getClass(), "RATGeneratorConnector", e); + MekHQ.getLogger().error(e); } } RATGenerator.getInstance().loadYear(year); } private UnitTable findTable(String faction, int unitType, int weightClass, int year, int quality, Collection movementModes) { - FactionRecord fRec = Faction.getFactionRecordOrFallback(faction); + FactionRecord fRec = Factions.getInstance().getFactionRecordOrFallback(faction); if(fRec == null) { return null; } diff --git a/MekHQ/src/mekhq/campaign/universe/RATManager.java b/MekHQ/src/mekhq/campaign/universe/RATManager.java index b489f00e3da..95e365af0d7 100644 --- a/MekHQ/src/mekhq/campaign/universe/RATManager.java +++ b/MekHQ/src/mekhq/campaign/universe/RATManager.java @@ -378,7 +378,7 @@ private List factionTree(String faction) { alts.remove(index); } } - Faction f = Faction.getFaction(faction); + Faction f = Factions.getInstance().getFaction(faction); if (f.isPeriphery()) { retVal.add("Periphery"); } diff --git a/MekHQ/src/mekhq/campaign/universe/RandomFactionGenerator.java b/MekHQ/src/mekhq/campaign/universe/RandomFactionGenerator.java index 5a8c4597c1c..987e9adc8dc 100644 --- a/MekHQ/src/mekhq/campaign/universe/RandomFactionGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/RandomFactionGenerator.java @@ -90,6 +90,10 @@ public static RandomFactionGenerator getInstance() { return rfg; } + public static void setInstance(RandomFactionGenerator instance) { + rfg = instance; + } + public void startup(Campaign c) { borderTracker.setDate(c.getLocalDate()); final PlanetarySystem location = c.getLocation().getCurrentSystem(); @@ -97,7 +101,7 @@ public void startup(Campaign c) { borderTracker.setRegionRadius(c.getCampaignOptions().getSearchRadius()); MekHQ.registerHandler(borderTracker); MekHQ.registerHandler(this); - for (Faction f : Faction.getFactions()) { + for (Faction f : Factions.getInstance().getFactions()) { if (factionHints.isDeepPeriphery(f)) { borderTracker.setBorderSize(f, BORDER_RANGE_DEEP_PERIPHERY); } @@ -223,12 +227,9 @@ public String getEmployer() { * @return The shortName of the faction to use as the opfor. */ public String getEnemy(String employer, boolean useRebels) { - final String METHOD_NAME = "getEnemy(String,boolean)"; //$NON-NLS-1$ - - Faction employerFaction = Faction.getFaction(employer); + Faction employerFaction = Factions.getInstance().getFaction(employer); if (null == employerFaction) { - MekHQ.getLogger().error(getClass(), METHOD_NAME, - "Could not find enemy for " + employer); //$NON-NLS-1$ + MekHQ.getLogger().error("Could not find enemy for " + employer); //$NON-NLS-1$ return "PIR"; } else { return getEnemy(employerFaction, useRebels); @@ -246,8 +247,6 @@ public String getEnemy(String employer, boolean useRebels) { * @return The faction to use as the opfor. */ public String getEnemy(Faction employer, boolean useRebels) { - final String METHOD_NAME = "getEnemy(Faction,boolean)"; //$NON-NLS-1$ - String employerName = employer != null ? employer.getShortName() : "no employer supplied or faction does not exist"; /* Rebels occur on a 1-4 (d20) on nearly every enemy chart */ @@ -268,8 +267,7 @@ public String getEnemy(Faction employer, boolean useRebels) { return enemy.getShortName(); } - MekHQ.getLogger().error(getClass(), METHOD_NAME, - "Could not find enemy for " + employerName); //$NON-NLS-1$ + MekHQ.getLogger().error("Could not find enemy for " + employerName); //$NON-NLS-1$ // Fallback; there are always pirates. return "PIR"; @@ -343,13 +341,12 @@ public Set getEmployerSet() { * @return A list of faction that share a border */ public List getEnemyList(String employerName) { - Faction employer = Faction.getFaction(employerName); + Faction employer = Factions.getInstance().getFaction(employerName); if (null == employer) { - MekHQ.getLogger().warning(getClass(), "getEnemyList(String)", //$NON-NLS-1$ - "Unknown faction key: " + employerName); //$NON-NLS-1$ + MekHQ.getLogger().warning("Unknown faction key: " + employerName); //$NON-NLS-1$ return Collections.emptyList(); } - return getEnemyList(Faction.getFaction(employerName)); + return getEnemyList(Factions.getInstance().getFaction(employerName)); } /** @@ -446,17 +443,14 @@ protected double adjustBorderWeight(double count, Faction f, Faction enemy, Loca * @return The planetId of the chosen planet, or null if there are no target candidates */ @Nullable public String getMissionTarget(String attacker, String defender) { - final String METHOD_NAME = "getMissionTarget(String, String)"; // $NON-NLS-1$ - Faction f1 = Faction.getFaction(attacker); - Faction f2 = Faction.getFaction(defender); + Faction f1 = Factions.getInstance().getFaction(attacker); + Faction f2 = Factions.getInstance().getFaction(defender); if (null == f1) { - MekHQ.getLogger().error(getClass(), METHOD_NAME, - "Non-existent faction key: " + attacker); // $NON-NLS-1$ + MekHQ.getLogger().error("Non-existent faction key: " + attacker); // $NON-NLS-1$ return null; } if (null == f2) { - MekHQ.getLogger().error(getClass(), METHOD_NAME, - "Non-existent faction key: " + attacker); // $NON-NLS-1$ + MekHQ.getLogger().error("Non-existent faction key: " + attacker); // $NON-NLS-1$ return null; } List planetList = getMissionTargetList(f1, f2); @@ -475,16 +469,13 @@ protected double adjustBorderWeight(double count, Faction f, Faction enemy, Loca * @return A list of potential mission targets */ public List getMissionTargetList(String attackerKey, String defenderKey) { - final String METHOD_NAME = "getMissionTargetList(String, String)"; //$NON-NLS-1$ - Faction attacker = Faction.getFaction(attackerKey); - Faction defender = Faction.getFaction(defenderKey); + Faction attacker = Factions.getInstance().getFaction(attackerKey); + Faction defender = Factions.getInstance().getFaction(defenderKey); if (null == attacker) { - MekHQ.getLogger().error(getClass(), METHOD_NAME, - "Non-existent faction key: " + attackerKey); //$NON-NLS-1$ + MekHQ.getLogger().error("Non-existent faction key: " + attackerKey); //$NON-NLS-1$ } if (null == defender) { - MekHQ.getLogger().error(getClass(), METHOD_NAME, - "Non-existent faction key: " + defenderKey); //$NON-NLS-1$ + MekHQ.getLogger().error("Non-existent faction key: " + defenderKey); //$NON-NLS-1$ } if ((null != attacker) && (null != defender)) { return getMissionTargetList(attacker, defender); diff --git a/MekHQ/src/mekhq/campaign/universe/RangedFactionSelector.java b/MekHQ/src/mekhq/campaign/universe/RangedFactionSelector.java index c51b08f51e2..3a7360ab06f 100644 --- a/MekHQ/src/mekhq/campaign/universe/RangedFactionSelector.java +++ b/MekHQ/src/mekhq/campaign/universe/RangedFactionSelector.java @@ -160,7 +160,7 @@ private void createLookupMap(Campaign campaign) { } }); - Faction mercenaries = Faction.getFaction("MERC"); + Faction mercenaries = Factions.getInstance().getFaction("MERC"); TreeMap factions = new TreeMap<>(); if (weights.isEmpty()) { // If we have no valid factions, we can always @@ -218,7 +218,7 @@ private Set getEnemies(Campaign campaign) { Set enemies = new HashSet<>(); for (Contract contract : campaign.getActiveContracts()) { if (contract instanceof AtBContract) { - enemies.add(Faction.getFaction(((AtBContract)contract).getEnemyCode())); + enemies.add(Factions.getInstance().getFaction(((AtBContract)contract).getEnemyCode())); } } return enemies; diff --git a/MekHQ/src/mekhq/campaign/universe/Systems.java b/MekHQ/src/mekhq/campaign/universe/Systems.java index d6b5b3d808f..8b54b25162e 100644 --- a/MekHQ/src/mekhq/campaign/universe/Systems.java +++ b/MekHQ/src/mekhq/campaign/universe/Systems.java @@ -19,6 +19,7 @@ package mekhq.campaign.universe; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; @@ -63,12 +64,9 @@ */ public class Systems { - private final static Object LOADING_LOCK = new Object[0]; private static Systems systems; - //private static ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.Planets", new EncodeControl()); //$NON-NLS-1$ - - // Marshaller / unmarshaller instances + // Marshaller / unmarshaller instances private static Marshaller marshaller; private static Unmarshaller unmarshaller; static { @@ -81,7 +79,7 @@ public class Systems { // For debugging only! // unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler()); } catch (JAXBException e) { - MekHQ.getLogger().error(Systems.class, "", e); //$NON-NLS-1$ + MekHQ.getLogger().error(e); } } @@ -96,7 +94,7 @@ public class Systems { planetMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); planetUnmarshaller = jContext.createUnmarshaller(); } catch (Exception e) { - MekHQ.getLogger().error(Systems.class, "Systems", e); + MekHQ.getLogger().error(e); } } @@ -104,29 +102,26 @@ public static Systems getInstance() { if (systems == null) { systems = new Systems(); } - if (!systems.initialized && !systems.initializing) { - systems.initializing = true; - systems.loader = new Thread(() -> systems.initialize(), "Planet Loader"); - systems.loader.setPriority(Thread.NORM_PRIORITY - 1); - systems.loader.start(); - } return systems; } + public static void setInstance(Systems instance) { + systems = instance; + } + private ConcurrentMap systemList = new ConcurrentHashMap<>(); - /* organizes systems into a grid of 30lyx30ly squares so we can find - * nearby systems without iterating through the entire planet list. */ + /** + * Organizes systems into a grid of 30lyx30ly squares so we can find nearby + * systems without iterating through the entire planet list. + */ private HashMap>> systemGrid = new HashMap<>(); // HPG Network cache (to not recalculate all the damn time) private Collection hpgNetworkCache = null; private LocalDate hpgNetworkCacheDate = null; - private Thread loader; - private boolean initialized = false; - private boolean initializing = false; - - private Systems() {} + private Systems() { + } private Set getSystemGrid(int x, int y) { if (!systemGrid.containsKey(x)) { @@ -153,12 +148,12 @@ public PlanetarySystem getSystemByName(String name, LocalDate when) { } public List getNearbySystems(final double centerX, final double centerY, int distance) { - List neighbors = new ArrayList<>(); + List neighbors = new ArrayList<>(); - visitNearbySystems(centerX, centerY, distance, neighbors::add); + visitNearbySystems(centerX, centerY, distance, neighbors::add); - neighbors.sort(Comparator.comparingDouble(o -> o.getDistanceTo(centerX, centerY))); - return neighbors; + neighbors.sort(Comparator.comparingDouble(o -> o.getDistanceTo(centerX, centerY))); + return neighbors; } public List getNearbySystems(final PlanetarySystem system, int distance) { @@ -258,27 +253,61 @@ public Collection getHPGNetwork(LocalDate when) { return result; } -// Data loading methods + // Data loading methods - private void initialize() { - try { - generateSystems(); - } catch (ParseException e) { - MekHQ.getLogger().error(getClass(), "initialize()", e); //$NON-NLS-1$ - } - } + /** + * Loads the default Systems data. + * + * @throws DOMException + * @throws IOException + * @throws FileNotFoundException + * @throws ParseException + */ + public static Systems loadDefault() + throws DOMException, FileNotFoundException, IOException, ParseException { + MekHQ.getLogger().info("Starting load of system data from XML..."); + long currentTime = System.currentTimeMillis(); + + Systems systems = load("data/universe/planetary_systems", "data/universe/systems.xml"); + + MekHQ.getLogger().info(String.format(Locale.ROOT, "Loaded a total of %d systems in %.3fs.", + systems.systemList.size(), (System.currentTimeMillis() - currentTime) / 1000.0)); + + systems.logVeryCloseSystems(); - private void done() { - initialized = true; - initializing = false; + return systems; } - public boolean isInitialized() { - return initialized; + /** + * Loads Systems data from files. + * + * @param planetsPath The path to the folder containing planetary XML files. + * @param defaultFilePath The path to the file with default systems data. + * + * @throws DOMException + * @throws IOException + * @throws FileNotFoundException + * @throws ParseException + */ + public static Systems load(String planetsPath, String defaultFilePath) + throws DOMException, IOException, ParseException, FileNotFoundException { + Systems systems = new Systems(); + + // Step 1: Read the default file + try (FileInputStream fis = new FileInputStream(defaultFilePath)) { + systems.updateSystems(fis); + } + + // Step 2: Load all the xml files within the planets subdirectory, if it exists + Utilities.parseXMLFiles(planetsPath, systems::updateSystems); + + // Step 3: Cleanup any systems that have issues + systems.cleanupSystems(); + + return systems; } private void updateSystems(FileInputStream source) { - final String METHOD_NAME = "updateSystems(FileInputStream)"; //$NON-NLS-1$ // JAXB unmarshaller closes the stream it doesn't own. Bad JAXB. BAD. try (InputStream is = new FilterInputStream(source) { @Override @@ -309,86 +338,45 @@ public void close() { } } } } catch (JAXBException e) { - MekHQ.getLogger().error(getClass(), METHOD_NAME, e); + MekHQ.getLogger().error(e); } catch (IOException e) { - MekHQ.getLogger().error(getClass(), METHOD_NAME, e); + MekHQ.getLogger().error(e); } } - private void generateSystems() throws DOMException, ParseException { - generateSystems("data/universe/planetary_systems", "data/universe/systems.xml"); - } - - private void generateSystems(String planetsPath, String defaultFilePath) throws DOMException, ParseException { - - MekHQ.getLogger().info(this, "Starting load of system data from XML..."); - long currentTime = System.currentTimeMillis(); - synchronized (LOADING_LOCK) { - // Step 1: Initialize variables. - if (null == systemList) { - systemList = new ConcurrentHashMap<>(); - } - systemList.clear(); - if (null == systemGrid) { - systemGrid = new HashMap<>(); - } - // Be nice to the garbage collector - for (Map.Entry>> systemGridColumn : systemGrid.entrySet()) { - for (Map.Entry> systemGridElement : systemGridColumn.getValue().entrySet()) { - if (null != systemGridElement.getValue()) { - systemGridElement.getValue().clear(); - } - } - if (null != systemGridColumn.getValue()) { - systemGridColumn.getValue().clear(); - } - } - systemGrid.clear(); - - // Step 2: Read the default file - try (FileInputStream fis = new FileInputStream(defaultFilePath)) { - updateSystems(fis); - } catch (Exception ex) { - MekHQ.getLogger().error(this, ex); - } - - // Step 3: Load all the xml files within the planets subdirectory, if it exists - Utilities.parseXMLFiles(planetsPath, this::updateSystems); - - List toRemove = new ArrayList<>(); - for (PlanetarySystem system : systemList.values()) { - if ((null == system.getX()) || (null == system.getY())) { - MekHQ.getLogger().error(this, String.format("System \"%s\" is missing coordinates", system.getId())); - toRemove.add(system); - continue; - } - //make sure the primary slot is not larger than the number of planets - if (system.getPrimaryPlanetPosition() > system.getPlanets().size()) { - MekHQ.getLogger().error(this, String.format("System \"%s\" has a primary slot greater than the number of planets", system.getId())); - toRemove.add(system); - continue; - } - int x = (int) (system.getX() / 30.0); - int y = (int) (system.getY() / 30.0); - systemGrid.computeIfAbsent(x, k -> new HashMap<>()); - systemGrid.get(x).computeIfAbsent(y, k -> new HashSet<>()); - systemGrid.get(x).get(y).add(system); + private void cleanupSystems() { + List toRemove = new ArrayList<>(); + for (PlanetarySystem system : systemList.values()) { + if ((null == system.getX()) || (null == system.getY())) { + MekHQ.getLogger().error(String.format("System \"%s\" is missing coordinates", system.getId())); + toRemove.add(system); + continue; } - for (PlanetarySystem system : toRemove) { - systemList.remove(system.getId()); + //make sure the primary slot is not larger than the number of planets + if (system.getPrimaryPlanetPosition() > system.getPlanets().size()) { + MekHQ.getLogger().error(String.format("System \"%s\" has a primary slot greater than the number of planets", system.getId())); + toRemove.add(system); + continue; } - done(); + int x = (int) (system.getX() / 30.0); + int y = (int) (system.getY() / 30.0); + systemGrid.computeIfAbsent(x, k -> new HashMap<>()); + systemGrid.get(x).computeIfAbsent(y, k -> new HashSet<>()); + systemGrid.get(x).get(y).add(system); + } + for (PlanetarySystem system : toRemove) { + systemList.remove(system.getId()); } - MekHQ.getLogger().info(this, String.format(Locale.ROOT, - "Loaded a total of %d systems in %.3fs.", - systemList.size(), (System.currentTimeMillis() - currentTime) / 1000.0)); + } + + private void logVeryCloseSystems() { // Planetary sanity check time! for (PlanetarySystem system : systemList.values()) { List veryCloseSystems = getNearbySystems(system, 1); if (veryCloseSystems.size() > 1) { for (PlanetarySystem closeSystem : veryCloseSystems) { if (!system.getId().equals(closeSystem.getId())) { - MekHQ.getLogger().warning(this, String.format(Locale.ROOT, + MekHQ.getLogger().warning(String.format(Locale.ROOT, "Extremely close systems detected. Data error? %s <-> %s: %.3f ly", //$NON-NLS-1$ system.getId(), closeSystem.getId(), system.getDistanceTo(closeSystem))); } @@ -488,7 +476,7 @@ public void writePlanetaryEvent(Writer out, Planet.PlanetaryEvent event) { try { planetMarshaller.marshal(event, out); } catch (Exception e) { - MekHQ.getLogger().error(getClass(), "writePlanet(Writer,Planet.PlanetaryEvent)", e); //$NON-NLS-1$ + MekHQ.getLogger().error(e); } } @@ -501,7 +489,7 @@ public void writePlanetarySystemEvent(Writer out, PlanetarySystem.PlanetarySyste try { marshaller.marshal(event, out); } catch (Exception e) { - MekHQ.getLogger().error(getClass(), "writePlanet(Writer,Planet.PlanetaryEvent)", e); //$NON-NLS-1$ + MekHQ.getLogger().error(e); } } @@ -515,7 +503,7 @@ public Planet.PlanetaryEvent readPlanetaryEvent(Node node) { try { return (Planet.PlanetaryEvent) planetUnmarshaller.unmarshal(node); } catch (JAXBException e) { - MekHQ.getLogger().error(getClass(), "readPlanetaryEvent(Node)", e); //$NON-NLS-1$ + MekHQ.getLogger().error(e); } return null; } @@ -530,25 +518,11 @@ public PlanetarySystem.PlanetarySystemEvent readPlanetarySystemEvent(Node node) try { return (PlanetarySystem.PlanetarySystemEvent) unmarshaller.unmarshal(node); } catch (JAXBException e) { - MekHQ.getLogger().error(getClass(), "readPlanetarySystemEvent(Node)", e); //$NON-NLS-1$ + MekHQ.getLogger().error(e); } return null; } - public static void reload(boolean waitForFinish) { - systems = null; - getInstance(); - if (waitForFinish) { - try { - while (!systems.isInitialized()) { - Thread.sleep(10); - } - } catch (InterruptedException iex) { - MekHQ.getLogger().error(Systems.class, "reload(boolean)", iex); //$NON-NLS-1$ - } - } - } - /** @return true if the planet was known and got updated, false otherwise */ /*public boolean updatePlanetaryEvents(String id, Collection events) { return updatePlanetaryEvents(id, events, false); diff --git a/MekHQ/src/mekhq/campaign/universe/UnitGeneratorParameters.java b/MekHQ/src/mekhq/campaign/universe/UnitGeneratorParameters.java index 7f1f3e2412e..250e63bac52 100644 --- a/MekHQ/src/mekhq/campaign/universe/UnitGeneratorParameters.java +++ b/MekHQ/src/mekhq/campaign/universe/UnitGeneratorParameters.java @@ -67,7 +67,7 @@ public UnitGeneratorParameters clone() { * @return */ public Parameters getRATGeneratorParameters() { - FactionRecord fRec = Faction.getFactionRecordOrFallback(getFaction()); + FactionRecord fRec = Factions.getInstance().getFactionRecordOrFallback(getFaction()); String rating = RATGeneratorConnector.getFactionSpecificRating(fRec, getQuality()); List weightClasses = new ArrayList<>(); diff --git a/MekHQ/src/mekhq/gui/FactionComboBox.java b/MekHQ/src/mekhq/gui/FactionComboBox.java index 030c04429b9..95f54838259 100644 --- a/MekHQ/src/mekhq/gui/FactionComboBox.java +++ b/MekHQ/src/mekhq/gui/FactionComboBox.java @@ -33,7 +33,7 @@ import javax.swing.JComboBox; import javax.swing.JList; -import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.gui.model.SortedComboBoxModel; /** @@ -79,7 +79,7 @@ public void addFactionEntries(Collection list, int year) { HashMap map = new HashMap(); HashSet collisions = new HashSet(); for (String key : list) { - String fullName = Faction.getFaction(key).getFullName(year); + String fullName = Factions.getInstance().getFaction(key).getFullName(year); if (map.containsValue(fullName)) { collisions.add(fullName); } diff --git a/MekHQ/src/mekhq/gui/InterstellarMapPanel.java b/MekHQ/src/mekhq/gui/InterstellarMapPanel.java index 1de87467a1d..8e81e7aa14f 100644 --- a/MekHQ/src/mekhq/gui/InterstellarMapPanel.java +++ b/MekHQ/src/mekhq/gui/InterstellarMapPanel.java @@ -84,6 +84,7 @@ import mekhq.campaign.Campaign; import mekhq.campaign.JumpPath; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.Faction.Tag; import mekhq.campaign.universe.SocioIndustrialData; import mekhq.campaign.universe.PlanetarySystem; @@ -664,7 +665,7 @@ protected void paintComponent(Graphics g) { } Map capitals = new HashMap<>(); - for (Faction faction : Faction.getFactions()) { + for (Faction faction : Factions.getInstance().getFactions()) { capitals.put(faction, faction.getStartingPlanet(campaign.getLocalDate())); } diff --git a/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java b/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java index c16a76addc4..18af2b27398 100644 --- a/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java @@ -99,6 +99,7 @@ import mekhq.campaign.personnel.enums.TimeInDisplayFormat; import mekhq.campaign.rating.UnitRatingMethod; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.RATManager; import mekhq.gui.FileDialogs; import mekhq.gui.SpecialAbilityPanel; @@ -638,8 +639,8 @@ private void initComponents() { panGeneral.add(lblFaction, gridBagConstraints); factionModel = new SortedComboBoxModel<>(); - for (String sName : Faction.getChoosableFactionCodes()) { - Faction f = Faction.getFaction(sName); + for (String sName : Factions.getInstance().getChoosableFactionCodes()) { + Faction f = Factions.getInstance().getFaction(sName); if (f.validIn(date.getYear())) { factionModel.addElement(f.getFullName(date.getYear())); } @@ -4827,7 +4828,7 @@ private void updateOptions() { GameOptions gameOpts = campaign.getGameOptions(); int campaignYear = campaign.getGameYear(); gameOpts.getOption("year").setValue(campaignYear); - campaign.setFactionCode(Faction.getFactionFromFullNameAndYear + campaign.setFactionCode(Factions.getInstance().getFactionFromFullNameAndYear (String.valueOf(comboFaction.getSelectedItem()), date.getYear()).getShortName()); if (null != comboFactionNames.getSelectedItem()) { RandomNameGenerator.getInstance().setChosenFaction((String) comboFactionNames.getSelectedItem()); @@ -5217,8 +5218,8 @@ private void btnDateActionPerformed(ActionEvent evt) { date = dc.getDate(); btnDate.setText(MekHQ.getMekHQOptions().getDisplayFormattedDate(date)); factionModel = new SortedComboBoxModel<>(); - for (String sname : Faction.getChoosableFactionCodes()) { - Faction f = Faction.getFaction(sname); + for (String sname : Factions.getInstance().getChoosableFactionCodes()) { + Faction f = Factions.getInstance().getFaction(sname); if (f.validIn(date.getYear())) { factionModel.addElement(f.getFullName(date.getYear())); } diff --git a/MekHQ/src/mekhq/gui/dialog/ChooseFactionsDialog.java b/MekHQ/src/mekhq/gui/dialog/ChooseFactionsDialog.java index 635977f06e0..827872308d2 100644 --- a/MekHQ/src/mekhq/gui/dialog/ChooseFactionsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ChooseFactionsDialog.java @@ -46,6 +46,7 @@ import megamek.common.util.EncodeControl; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; public class ChooseFactionsDialog extends JDialog { private static final long serialVersionUID = 805616085217507489L; @@ -160,7 +161,7 @@ private static class FactionListModel extends AbstractListModel { private List names; public FactionListModel(LocalDate date) { - for (Faction faction : Faction.getFactions()) { + for (Faction faction : Factions.getInstance().getFactions()) { factionMap.put(faction.getFullName(date.getYear()), faction); } names = new ArrayList<>(factionMap.navigableKeySet()); diff --git a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java index 715bb34c8d7..baad9869b5d 100644 --- a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java @@ -46,7 +46,7 @@ import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.Contract; import mekhq.campaign.mission.Mission; -import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.gui.FactionComboBox; import mekhq.gui.view.ContractSummaryPanel; @@ -304,7 +304,7 @@ public boolean isCellEditable(int row, int column) { gbc.fill = GridBagConstraints.NONE; panelRetainer.add(lblCurrentRetainer, gbc); if (null != campaign.getRetainerEmployerCode()) { - lblRetainerEmployer.setText(Faction.getFaction(campaign.getRetainerEmployerCode()).getFullName(campaign.getGameYear())); + lblRetainerEmployer.setText(Factions.getInstance().getFaction(campaign.getRetainerEmployerCode()).getFullName(campaign.getGameYear())); } gbc.gridx = 1; gbc.gridy = 0; @@ -348,7 +348,7 @@ public boolean isCellEditable(int row, int column) { lblCurrentRetainer.setVisible(true); lblRetainerEmployer.setVisible(true); btnEndRetainer.setVisible(true); - lblRetainerEmployer.setText(Faction.getFaction(campaign.getRetainerEmployerCode()).getFullName(campaign.getGameYear())); + lblRetainerEmployer.setText(Factions.getInstance().getFaction(campaign.getRetainerEmployerCode()).getFullName(campaign.getGameYear())); //Remove the selected faction and add the previous one, if any countSuccessfulContracts(); lblRetainerAvailable.setVisible(possibleRetainerContracts.size() > 0); diff --git a/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java b/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java index 59732067d32..bb0a8045f46 100644 --- a/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/CustomizeAtBContractDialog.java @@ -42,7 +42,7 @@ import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.PlanetarySystem; import mekhq.campaign.universe.RandomFactionGenerator; import mekhq.campaign.universe.Systems; @@ -605,8 +605,8 @@ private void showAllFactions(boolean allFactions) { cbEmployer.removeAllItems(); cbEnemy.removeAllItems(); if (allFactions) { - cbEmployer.addFactionEntries(Faction.getFactionList(), campaign.getGameYear()); - cbEnemy.addFactionEntries(Faction.getFactionList(), campaign.getGameYear()); + cbEmployer.addFactionEntries(Factions.getInstance().getFactionList(), campaign.getGameYear()); + cbEnemy.addFactionEntries(Factions.getInstance().getFactionList(), campaign.getGameYear()); } else { cbEmployer.addFactionEntries(currentFactions, campaign.getGameYear()); cbEnemy.addFactionEntries(currentFactions, campaign.getGameYear()); diff --git a/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java b/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java index 641724f8ca5..47dd596d1eb 100644 --- a/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java @@ -49,6 +49,7 @@ import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.Faction.Tag; import mekhq.campaign.universe.Planet; import mekhq.campaign.universe.PlanetarySystem; @@ -838,7 +839,7 @@ private void setUserPreferences() { private DefaultComboBoxModel getFactionsComboBoxModel() { int year = campaign.getGameYear(); - List orderedFactions = Faction.getFactions().stream() + List orderedFactions = Factions.getInstance().getFactions().stream() .sorted((a, b) -> a.getFullName(year).compareToIgnoreCase(b.getFullName(year))) .collect(Collectors.toList()); diff --git a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java index 71ba488cdf7..94a6c56fdfc 100644 --- a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java @@ -27,6 +27,7 @@ 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; @@ -38,6 +39,10 @@ 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; @@ -56,7 +61,7 @@ import mekhq.campaign.mod.am.InjuryTypes; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.ranks.Ranks; -import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.RATManager; import mekhq.gui.preferences.JWindowPreference; import mekhq.preferences.PreferencesNode; @@ -129,12 +134,13 @@ class Task extends SwingWorker { private boolean cancelled = false; @Override - public Campaign doInBackground() throws IOException, CampaignXmlParseException, NullEntityException { + public Campaign doInBackground() throws IOException, CampaignXmlParseException, NullEntityException, + DOMException, ParseException, SAXException, ParserConfigurationException { //region Progress 0 //Initialize progress property. setProgress(0); - Faction.generateFactions(); + Factions.setInstance(Factions.loadDefault()); CurrencyManager.getInstance().loadCurrencies(); @@ -143,12 +149,8 @@ public Campaign doInBackground() throws IOException, CampaignXmlParseException, //Load values needed for CampaignOptionsDialog RATManager.populateCollectionNames(); - while (!Systems.getInstance().isInitialized()) { - try { - Thread.sleep(50); - } catch (InterruptedException ignored) { - } - } + // Initialize the systems + Systems.setInstance(Systems.loadDefault()); RandomNameGenerator.getInstance(); RandomCallsignGenerator.getInstance(); diff --git a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java index d230f439769..5bc487e83a5 100644 --- a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java @@ -53,6 +53,7 @@ import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.IUnitGenerator; import mekhq.gui.CampaignGUI; import mekhq.gui.preferences.JComboBoxPreference; @@ -589,7 +590,7 @@ private GridBagConstraints newGridBagConstraints(int x, int y, int width, int he private List getFactionChoices(int year) { List factionChoices = new ArrayList<>(); - for (Faction faction : Faction.getFactions()) { + for (Faction faction : Factions.getInstance().getFactions()) { factionChoices.add(new FactionChoice(faction, year)); } diff --git a/MekHQ/src/mekhq/gui/dialog/NewPlanetaryEventDialog.java b/MekHQ/src/mekhq/gui/dialog/NewPlanetaryEventDialog.java index c77fe49aa6e..ea90bb47db8 100644 --- a/MekHQ/src/mekhq/gui/dialog/NewPlanetaryEventDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/NewPlanetaryEventDialog.java @@ -46,6 +46,7 @@ import mekhq.adapter.SocioIndustrialDataAdapter; import mekhq.campaign.Campaign; import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.Planet; import mekhq.gui.preferences.JWindowPreference; import mekhq.preferences.PreferencesNode; @@ -480,7 +481,7 @@ private void updateDate() { if ((null != event) && (null != event.faction)) { factionSet = new HashSet<>(); for (String f : event.faction) { - factionSet.add(Faction.getFaction(f)); + factionSet.add(Factions.getInstance().getFaction(f)); } } factionsButton.setText(Faction.getFactionNames(factionSet, date.getYear())); diff --git a/MekHQ/src/mekhq/module/atb/AtBEventProcessor.java b/MekHQ/src/mekhq/module/atb/AtBEventProcessor.java index 87598f3d332..f02b534a387 100644 --- a/MekHQ/src/mekhq/module/atb/AtBEventProcessor.java +++ b/MekHQ/src/mekhq/module/atb/AtBEventProcessor.java @@ -42,7 +42,7 @@ import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.rating.IUnitRating; -import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.Factions; import mekhq.campaign.universe.IUnitGenerator; import mekhq.campaign.universe.RandomFactionGenerator; import mekhq.campaign.universe.UnitGeneratorParameters; @@ -209,20 +209,20 @@ private void addRecruitUnit(Person p) { MechSummary ms = campaign.getUnitGenerator().generate(params); if (null != ms) { - if (Faction.getFaction(faction).isClan() && ms.getName().matches(".*Platoon.*")) { + if (Factions.getInstance().getFaction(faction).isClan() && ms.getName().matches(".*Platoon.*")) { String name = "Clan " + ms.getName().replaceAll("Platoon", "Point"); ms = MechSummaryCache.getInstance().getMech(name); - MekHQ.getLogger().info(this, "looking for Clan infantry " + name); + MekHQ.getLogger().info("looking for Clan infantry " + name); } try { en = new MechFileParser(ms.getSourceFile(), ms.getEntryName()).getEntity(); } catch (EntityLoadingException ex) { en = null; - MekHQ.getLogger().error(this, "Unable to load entity: " + MekHQ.getLogger().error("Unable to load entity: " + ms.getSourceFile() + ": " + ms.getEntryName() + ": " + ex.getMessage(), ex); } } else { - MekHQ.getLogger().error(this, "Personnel market could not find " + MekHQ.getLogger().error("Personnel market could not find " + UnitType.getTypeName(unitType) + " for recruit from faction " + faction); return; } @@ -288,7 +288,7 @@ public static String getRecruitFaction(Campaign c) { if ((c.getGameYear() > 3055) && (Compute.randomInt(20) == 0)) { ArrayList clans = new ArrayList<>(); for (String f : RandomFactionGenerator.getInstance().getCurrentFactions()) { - if (Faction.getFaction(f).isClan()) { + if (Factions.getInstance().getFaction(f).isClan()) { clans.add(f); } } diff --git a/MekHQ/unittests/mekhq/campaign/market/ContractMarketAtBGenerationTests.java b/MekHQ/unittests/mekhq/campaign/market/ContractMarketAtBGenerationTests.java new file mode 100644 index 00000000000..96b8868aac1 --- /dev/null +++ b/MekHQ/unittests/mekhq/campaign/market/ContractMarketAtBGenerationTests.java @@ -0,0 +1,1741 @@ +/* + * Copyright (c) 2020 - 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.market; + +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.CurrentLocation; +import mekhq.campaign.Hangar; +import mekhq.campaign.JumpPath; +import mekhq.campaign.finances.Accountant; +import mekhq.campaign.finances.Money; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.rating.IUnitRating; +import mekhq.campaign.rating.UnitRatingMethod; +import mekhq.campaign.universe.Faction; +import mekhq.campaign.universe.FactionHints; +import mekhq.campaign.universe.Factions; +import mekhq.campaign.universe.RandomFactionGenerator; +import mekhq.campaign.universe.PlanetarySystem; +import mekhq.campaign.universe.Systems; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.Vector; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(Parameterized.class) +public class ContractMarketAtBGenerationTests { + + private final int gameYear; + private final int unitRating; + private final boolean isClanEnemy; + + public ContractMarketAtBGenerationTests(int gameYear, int unitRating, boolean isClanEnemy) { + this.gameYear = gameYear; + this.unitRating = unitRating; + this.isClanEnemy = isClanEnemy; + } + + @Parameters(name = "Run {index}: gameYear={0}, unitRating={1}, isClanEnemy={2}") + public static Iterable data() throws Throwable { + List gameYears = Arrays.asList(new Integer[] { 2750, 3025, 3055, 3067, 3120 }); + + List parameters = new ArrayList<>(); + for (int gameYear : gameYears) { + for (int rating = IUnitRating.DRAGOON_F; rating <= IUnitRating.DRAGOON_ASTAR; ++rating) { + parameters.add(new Object[] { gameYear, rating, false }); + parameters.add(new Object[] { gameYear, rating, true }); + } + } + return parameters; + } + + @Test + public void addMercWithoutRetainerAtBContractSucceeds() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void addMercWithoutRetainerMinorPowerAtBContractSucceeds() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(false).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void addMercWithoutRetainerEmployerNeutralAtBContractSucceeds() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(true).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void addMercWithoutRetainerEmployerNeutralAtWarAtBContractSucceeds() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(true).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + doReturn(true).when(hints).isAtWarWith(eq(employerFaction), eq(enemyFaction), any()); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void mercEmployerRetries() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + // Return "MERC" the first time to coerce a retry + when(rfg.getEmployer()).thenReturn("MERC").thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + assertTrue(contract.isMercSubcontract()); + } + + @Test + public void mercEmployerRetriesFail() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + // Return "MERC" every time + when(rfg.getEmployer()).thenReturn("MERC"); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNull(contract); + } + + @Test + public void mercMissiongTargetRetries() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + // Don't find the mission target and force a retry + doReturn(null).doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void mercMissionTargetRetriesFail() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + // Don't ever find the mission target and force a retry failure + doReturn(null).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNull(contract); + } + + @Test + public void mercJumpPathRetries() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + // Fail to find a jump path at first, kicking off a retry + doReturn(null).doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void mercJumpPathFails() { + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employer = "EMPLOYER"; + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + // Fail to find a jump path + doReturn(null).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNull(contract); + } + + @Test + public void addMercWithRetainerAtBContractSucceeds() { + String employer = "EMPLOYER"; + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(employer); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void addMercWithRetainerMinorPowerAtBContractSucceeds() { + String employer = "EMPLOYER"; + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(employer); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(false).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void addMercWithRetainerEmployerNeutralAtBContractSucceeds() { + String employer = "EMPLOYER"; + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(employer); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(true).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void addMercWithRetainerEmployerNeutralAtWarAtBContractSucceeds() { + String employer = "EMPLOYER"; + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn("MERC"); + when(campaign.getRetainerEmployerCode()).thenReturn(employer); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(true).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + doReturn(true).when(hints).isAtWarWith(eq(employerFaction), eq(enemyFaction), any()); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void nonMercAtBContractSucceeds() { + String employer = "EMPLOYER"; + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn(employer); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void nonMercMinorPowerAtBContractSucceeds() { + String employer = "EMPLOYER"; + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn(employer); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(false).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(false).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void nonMercNeutralAtBContractSucceeds() { + String employer = "EMPLOYER"; + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn(employer); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + when(rfg.getEmployer()).thenReturn(employer); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(true).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @Test + public void nonMercNeutralAtWarAtBContractSucceeds() { + String employer = "EMPLOYER"; + Campaign campaign = mock(Campaign.class); + when(campaign.getFactionCode()).thenReturn(employer); + when(campaign.getRetainerEmployerCode()).thenReturn(null); + when(campaign.getUnitRatingMod()).thenReturn(unitRating); + when(campaign.getLocalDate()).thenReturn(LocalDate.ofYearDay(gameYear, 1)); + when(campaign.getGameYear()).thenReturn(gameYear); + + CampaignOptions campaignOptions = mock(CampaignOptions.class); + when(campaignOptions.getVariableContractLength()).thenReturn(false); + when(campaignOptions.getUnitRatingMethod()).thenReturn(UnitRatingMethod.FLD_MAN_MERCS_REV); + when(campaignOptions.usePeacetimeCost()).thenReturn(false); + when(campaign.getCampaignOptions()).thenReturn(campaignOptions); + + Accountant accountant = mock(Accountant.class); + when(accountant.getContractBase()).thenReturn(Money.of(1)); + when(accountant.getOverheadExpenses()).thenReturn(Money.of(1)); + when(campaign.getAccountant()).thenReturn(accountant); + + Hangar hangar = mock(Hangar.class); + doReturn(Money.of(1)).when(hangar).getUnitCosts(any(), any()); + when(campaign.getHangar()).thenReturn(hangar); + + Force forces = mock(Force.class); + doReturn(new Vector()).when(forces).getAllUnits(anyBoolean()); + when(campaign.getForces()).thenReturn(forces); + + Factions factions = mock(Factions.class); + Factions.setInstance(factions); + + String employerFullName = "Contract Employer"; + Faction employerFaction = mock(Faction.class); + when(employerFaction.getShortName()).thenReturn(employer); + doReturn(employerFullName).when(employerFaction).getFullName(anyInt()); + doReturn(employerFaction).when(factions).getFaction(eq(employer)); + + String enemy = "ENEMY"; + String enemyFullName = "Contract Enemy"; + Faction enemyFaction = mock(Faction.class); + when(enemyFaction.getShortName()).thenReturn(enemy); + when(enemyFaction.isClan()).thenReturn(isClanEnemy); + doReturn(enemyFullName).when(employerFaction).getFullName(anyInt()); + doReturn(enemyFaction).when(factions).getFaction(eq(enemy)); + + Faction pirates = mock(Faction.class); + doReturn(pirates).when(factions).getFaction(eq("PIR")); + + Faction rebels = mock(Faction.class); + doReturn(rebels).when(factions).getFaction(eq("REB")); + + Systems systems = mock(Systems.class); + Systems.setInstance(systems); + + String current = "CURRENT"; + PlanetarySystem currentSystem = mock(PlanetarySystem.class); + when(currentSystem.getId()).thenReturn(current); + when(campaign.getCurrentSystem()).thenReturn(currentSystem); + doReturn(currentSystem).when(systems).getSystemById(eq(current)); + doReturn(currentSystem).when(campaign).getSystemByName(eq(current)); + + CurrentLocation currentLocation = mock(CurrentLocation.class); + when(campaign.getLocation()).thenReturn(currentLocation); + + String missionTarget = "TARGET"; + PlanetarySystem targetSystem = mock(PlanetarySystem.class); + when(targetSystem.getId()).thenReturn(missionTarget); + doReturn(targetSystem).when(systems).getSystemById(eq(missionTarget)); + doReturn(targetSystem).when(campaign).getSystemByName(eq(missionTarget)); + + RandomFactionGenerator rfg = mock(RandomFactionGenerator.class); + RandomFactionGenerator.setInstance(rfg); + doReturn(enemy).when(rfg).getEnemy(eq(employer), anyBoolean()); + doReturn(missionTarget).when(rfg).getMissionTarget(anyString(), anyString()); + + FactionHints hints = mock(FactionHints.class); + doReturn(true).when(hints).isISMajorPower(eq(employerFaction)); + doReturn(true).when(hints).isISMajorPower(eq(enemyFaction)); + doReturn(true).when(hints).isNeutral(eq(employerFaction)); + doReturn(false).when(hints).isNeutral(eq(enemyFaction)); + doReturn(true).when(hints).isAtWarWith(eq(employerFaction), eq(enemyFaction), any()); + when(rfg.getFactionHints()).thenReturn(hints); + + JumpPath jumpPath = mock(JumpPath.class); + when(jumpPath.getJumps()).thenReturn(1); + doReturn(10.0).when(jumpPath).getTotalTime(any(), anyDouble()); + doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem)); + doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean()); + + ContractMarket market = new ContractMarket(); + + AtBContract contract = market.addAtBContract(campaign); + assertNotNull(contract); + } + + @After + public void cleanupAfterTests() { + Factions.setInstance(null); + Systems.setInstance(null); + RandomFactionGenerator.setInstance(null); + } +} diff --git a/MekHQ/unittests/mekhq/campaign/market/ContractMarketIntegrationTest.java b/MekHQ/unittests/mekhq/campaign/market/ContractMarketIntegrationTest.java new file mode 100644 index 00000000000..3fc1dbfce4d --- /dev/null +++ b/MekHQ/unittests/mekhq/campaign/market/ContractMarketIntegrationTest.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2020 - 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.market; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.UUID; + +import javax.xml.parsers.ParserConfigurationException; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.w3c.dom.DOMException; +import org.xml.sax.SAXException; + +import megamek.common.Bay; +import megamek.common.Compute; +import megamek.common.Crew; +import megamek.common.CrewType; +import megamek.common.EquipmentType; +import megamek.common.MMRandom; +import megamek.common.MMRoll; +import megamek.common.Mech; +import mekhq.campaign.Campaign; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.force.Force; +import mekhq.campaign.mission.AtBContract; +import mekhq.campaign.mission.Contract; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.enums.PersonnelStatus; +import mekhq.campaign.personnel.ranks.Ranks; +import mekhq.campaign.rating.UnitRatingMethod; +import mekhq.campaign.unit.Unit; +import mekhq.campaign.universe.Factions; +import mekhq.campaign.universe.RandomFactionGenerator; +import mekhq.campaign.universe.Systems; + +public class ContractMarketIntegrationTest { + private static final int REASONABLE_GENERATION_ATTEMPTS = 3; + + private Campaign campaign; + + @BeforeClass + public static void setupStatics() + throws DOMException, SAXException, IOException, ParserConfigurationException, ParseException { + EquipmentType.initializeTypes(); + Factions.setInstance(Factions.loadDefault()); + Systems.setInstance(Systems.loadDefault()); + Ranks.initializeRankSystems(); + } + + @AfterClass + public static void cleanupStatics() { + Systems.setInstance(null); + RandomFactionGenerator.setInstance(null); + Factions.setInstance(null); + } + + @Before + public void setupCampaign() { + CampaignOptions options = new CampaignOptions(); + options.setUnitRatingMethod(UnitRatingMethod.NONE); + + campaign = new Campaign(); + campaign.setCampaignOptions(options); + + RandomFactionGenerator.getInstance().startup(campaign); + + fillHangar(campaign); + } + + @Test + public void addAtBContractMercsTest() { + ContractMarket market = new ContractMarket(); + + // Simulate clicking GM Add on the contract market three times + for (int ii = 0; ii < REASONABLE_GENERATION_ATTEMPTS; ++ii) { + market.addAtBContract(campaign); + } + + assertFalse(market.getContracts().isEmpty()); + } + + @Test + public void generateContractOffersMercsTest() { + ContractMarket market = new ContractMarket(); + + // Simulate three months of contract generation ... + boolean foundContract = false; + for (int ii = 0; ii < REASONABLE_GENERATION_ATTEMPTS; ++ii) { + market.generateContractOffers(campaign, true); + + // ... and one of these three should get us a contract! + foundContract |= market.getContracts().size() > 0; + } + + assertTrue(foundContract); + } + + @Test + public void addAtBContractMercRetainerTest() { + campaign.setRetainerEmployerCode("LA"); + + ContractMarket market = new ContractMarket(); + + // Simulate clicking GM Add on the contract market three times + for (int ii = 0; ii < 3; ++ii) { + market.addAtBContract(campaign); + } + + assertFalse(market.getContracts().isEmpty()); + } + + @Test + public void generateContractOffersMercRetainerTest() { + campaign.setRetainerEmployerCode("CS"); + + ContractMarket market = new ContractMarket(); + + // Simulate three months of contract generation ... + boolean foundContract = false; + for (int ii = 0; ii < REASONABLE_GENERATION_ATTEMPTS; ++ii) { + market.generateContractOffers(campaign, true); + + // ... and one of these three should get us a contract! + foundContract |= market.getContracts().size() > 0; + } + + assertTrue(foundContract); + } + + @Test + public void generateContractOffersMercSubcontractTest() { + AtBContract existing = mock(AtBContract.class); + when(existing.getId()).thenReturn(1); + when(existing.getScenarios()).thenReturn(new ArrayList<>()); + when(existing.isActive()).thenReturn(true); + when(existing.getEmployerCode()).thenReturn("FWL"); + when(existing.getEnemyCode()).thenReturn("CC"); + when(existing.getSystemId()).thenReturn("Sian"); + when(existing.getEndingDate()).thenReturn(campaign.getLocalDate().plusDays(3000)); + campaign.importMission(existing); + + ContractMarket market = new ContractMarket(); + + java.security.SecureRandom realRng = new java.security.SecureRandom(); + MMRandom rng = mock(MMRandom.class); + // Override and ensure we are guaranteed a sub-contract + MMRoll roll1d6 = mock(MMRoll.class); + when(roll1d6.getIntValue()).thenReturn(6); + doReturn(roll1d6).when(rng).d6(); + doReturn(roll1d6).when(rng).d6(eq(1)); + MMRoll roll2d6 = mock(MMRoll.class); + when(roll2d6.getIntValue()).thenReturn(12); + doReturn(roll2d6).when(rng).d6(eq(2)); + // Keep the rest random + doAnswer(inv -> { + int max = inv.getArgument(0); + return realRng.nextInt(max); + }).when(rng).randomInt(anyInt()); + + try { + Compute.setRNG(rng); + + // Simulate three months of contract generation to get a sub contract ... + boolean foundContract = false; + for (int ii = 0; ii < REASONABLE_GENERATION_ATTEMPTS; ++ii) { + market.generateContractOffers(campaign, true); + + // ... and hopefully, one of these should get us a sub-contract! 3 of 12 chance. + for (Contract c : market.getContracts()) { + foundContract |= (c instanceof AtBContract) + && (((AtBContract) c).getParentContract() == existing); + } + + if (foundContract) { + break; + } + } + + assertTrue(foundContract); + } finally { + Compute.setRNG(MMRandom.R_DEFAULT); + } + } + + @Test + public void addAtBContractHouseTest() { + campaign.setFactionCode("DC"); + + ContractMarket market = new ContractMarket(); + + // Simulate clicking GM Add on the contract market three times + for (int ii = 0; ii < 3; ++ii) { + market.addAtBContract(campaign); + } + + assertFalse(market.getContracts().isEmpty()); + } + + @Test + public void generateContractOffersHouseTest() { + campaign.setFactionCode("FS"); + + ContractMarket market = new ContractMarket(); + + // Simulate three months of contract generation ... + boolean foundContract = false; + for (int ii = 0; ii < REASONABLE_GENERATION_ATTEMPTS; ++ii) { + market.generateContractOffers(campaign, true); + + // ... and one of these three should get us a contract! + foundContract |= market.getContracts().size() > 0; + } + + assertTrue(foundContract); + } + + private void fillHangar(Campaign campaign) { + // Add 12 mechs in 3 forces + for (int jj = 0; jj < 3; ++jj) { + Force force = new Force("Force " + String.valueOf(jj)); + for (int ii = 0; ii < 4; ++ii) { + Unit unit = createMech(campaign); + force.addUnit(unit.getId()); + + campaign.getHangar().addUnit(unit); + } + + campaign.addForce(force, campaign.getForces()); + } + } + + private Unit createMech(Campaign campaign) { + Mech entity = mock(Mech.class); + when(entity.getCrew()).thenReturn(new Crew(CrewType.SINGLE)); + when(entity.getTransportBays()).thenReturn(new java.util.Vector()); + Unit unit = new Unit(entity, campaign); + unit.setId(UUID.randomUUID()); + unit.addPilotOrSoldier(createPilot()); + return unit; + } + + private Person createPilot() { + Person person = mock(Person.class); + when(person.getId()).thenReturn(UUID.randomUUID()); + when(person.getStatus()).thenReturn(PersonnelStatus.ACTIVE); + return person; + } +} diff --git a/MekHQ/unittests/mekhq/campaign/universe/FactionsIntegrationTest.java b/MekHQ/unittests/mekhq/campaign/universe/FactionsIntegrationTest.java new file mode 100644 index 00000000000..0f59d301a22 --- /dev/null +++ b/MekHQ/unittests/mekhq/campaign/universe/FactionsIntegrationTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 - 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; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.time.LocalDate; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; + +import org.junit.Test; +import org.w3c.dom.DOMException; +import org.xml.sax.SAXException; + +public class FactionsIntegrationTest { + @Test + public void loadDefaultTest() + throws DOMException, SAXException, IOException, ParserConfigurationException { + Factions factions = Factions.loadDefault(); + + assertNotNull(factions); + + List choosableFactionCodes = factions.getChoosableFactionCodes(); + assertNotNull(choosableFactionCodes); + assertTrue(choosableFactionCodes.contains("MERC")); + assertTrue(choosableFactionCodes.contains("FS")); + + for (String factionCode : choosableFactionCodes) { + assertNotNull(String.format("Missing faction %s in choosable faction list", factionCode), + factions.getFaction(factionCode)); + } + + Faction capellans = factions.getFaction("CC"); + assertNotNull(capellans); + assertFalse(capellans.isClan()); + assertEquals("Sian", capellans.getStartingPlanet(LocalDate.of(3025, 1, 1))); + assertTrue(capellans.is(Faction.Tag.IS)); + assertTrue(capellans.is(Faction.Tag.MAJOR)); + + Faction comStar = factions.getFaction("CS"); + assertNotNull(comStar); + assertTrue(comStar.isComStar()); + assertEquals("Terra", comStar.getStartingPlanet(LocalDate.of(3025, 1, 1))); + assertEquals("Tukayyid", comStar.getStartingPlanet(LocalDate.of(3067, 1, 1))); + assertTrue(comStar.is(Faction.Tag.IS)); + assertTrue(comStar.is(Faction.Tag.INACTIVE)); + assertTrue(comStar.is(Faction.Tag.MAJOR)); + + Faction ghostBear = factions.getFaction("CGB"); + assertNotNull(ghostBear); + assertTrue(ghostBear.isClan()); + assertEquals("Arcadia (Clan)", ghostBear.getStartingPlanet(LocalDate.of(3025, 1, 1))); + assertEquals("Alshain", ghostBear.getStartingPlanet(LocalDate.of(3067, 1, 1))); + assertTrue(ghostBear.is(Faction.Tag.CLAN)); + assertTrue(ghostBear.is(Faction.Tag.MAJOR)); + } +} diff --git a/MekHQ/unittests/mekhq/campaign/universe/SystemsIntegrationTest.java b/MekHQ/unittests/mekhq/campaign/universe/SystemsIntegrationTest.java new file mode 100644 index 00000000000..4f00d729dd6 --- /dev/null +++ b/MekHQ/unittests/mekhq/campaign/universe/SystemsIntegrationTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 - 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; + +import static org.junit.Assert.*; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.text.ParseException; + +import org.junit.Test; +import org.w3c.dom.DOMException; + +public class SystemsIntegrationTest { + @Test + public void loadDefaultTest() + throws DOMException, FileNotFoundException, IOException, ParseException { + Systems systems = Systems.loadDefault(); + + assertNotNull(systems); + + PlanetarySystem terra = systems.getSystemById("Terra"); + assertNotNull(terra); + assertEquals(0.0, terra.getX(), 0.001); + assertEquals(0.0, terra.getY(), 0.001); + + Planet thirdRock = terra.getPlanetById("Terra"); + assertNotNull(thirdRock); + assertEquals(thirdRock, terra.getPlanet(3)); + } +}