diff --git a/MekHQ/data/CamOpsContractData2.xml b/MekHQ/data/CamOpsContractData2.xml
deleted file mode 100644
index 9786e5467f..0000000000
--- a/MekHQ/data/CamOpsContractData2.xml
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-
- SuperPower
- SUPER
-
-
-
-
- Great
-
- 3
- 2
- 2
-
-
-
-
-
- 0
- 0
-
-
-
-
- 2
- SmallState
-
-
-
-
- 2
- Individual
-
-
-
-
- SuperPower
- Major
-
-
-
-
- Major
-
-
-
- 2
- 1, 1
-
-
-
-
-
-
-
- 1
-
- false
- 0
- 0
- 0
- 0
- 0
- 0
- 0
-
-
-
-
-
- SuperPower
-
- false
- 0
- 0
- 0
- 0
- 0
- 0
- 0
-
-
-
-
-
- 0
-
- false
- 0
- 0
- 0
- 0
- 0
- 0
- 0
-
-
-
-
-
- 2
- 0
-
-
-
-
- 2
- 0
-
-
-
-
- 2
- 0
-
-
-
-
- 2
- 0
-
-
-
-
- 2
- 0
-
-
-
diff --git a/MekHQ/data/universe/atbconfig.xml b/MekHQ/data/universe/atbconfig.xml
index 6ce1e38f55..c9cab40b18 100644
--- a/MekHQ/data/universe/atbconfig.xml
+++ b/MekHQ/data/universe/atbconfig.xml
@@ -138,28 +138,32 @@ of 4. An entry of the form option has a weight of 1.
*Note that start and end dates are optional, but must
follow yyyy-MM-dd format. Planet names much match the
name in planets.xml exactly.-->
-
- Outreach
- Solaris
- Arc-Royal
- Fletcher
- Galatea
- Westerhand
- Northwind
- Herotitus
-
-
-
-
-
-
-
- 100000
-
-
- 4
-
-
+
+
+ 100000
+
+
+ 4
+
+
diff --git a/MekHQ/data/universe/factions.xml b/MekHQ/data/universe/factions.xml
index 6c57d22f2e..030c3ddb2a 100644
--- a/MekHQ/data/universe/factions.xml
+++ b/MekHQ/data/universe/factions.xml
@@ -50,7 +50,7 @@ layeredForceIconLogoCategory - the category of the faction's logo piece
layeredForceIconLogoFilename - the filename of the faction's logo piece
tags - a comma-separated list of tags. Currently recognised tags: "is", "periphery",
"deep_periphery", "clan", "pirate", "merc", "trader", "minor", "rebel", "inactive", "hidden",
-"abandoned", "chaos", "playable", "super"
+"abandoned", "chaos", "playable", "super", "noble", "planetary_government", "corporation", "small"
start - the founding date of the faction
end - the date the faction ceases to exist
successor - unimplemented tag describing another faction code as the specified faction's successor
diff --git a/MekHQ/resources/mekhq/resources/AtBConfigDefaults.properties b/MekHQ/resources/mekhq/resources/AtBConfigDefaults.properties
index d952b4cdc3..2299b46c2e 100644
--- a/MekHQ/resources/mekhq/resources/AtBConfigDefaults.properties
+++ b/MekHQ/resources/mekhq/resources/AtBConfigDefaults.properties
@@ -10,7 +10,7 @@ botLance.CLAN=1:LLLLL,1:LLLLM,1:LLLMM|1:LLMMM,1:LMMMM,2:MMMMM,1:MMMMH,1:MMMHH|1:
botLance.CS=1:LLLLLL,2:LLLLLM,2:LLLLMM,1:LLLMMM|1:LLLMMM,1:LLMMMM,1:LMMMMM,1:MMMMMM,1:MMMMMH,1:MMMMHH|1:MMMHHH,1:MMHHHH,1:MHHHHH,1:HHHHHH,1:HHHHHA,1:HHHHAA|2:HHHAAA,1:HHAAAA,2:HAAAAA,1:AAAAAA
#The following are not required for AtB to function and are only used if atbconfig.xml is missing or broken
-hiringHalls=3031-01-01,3067-10-15,Outreach|2700-01-01,,Solaris|3057-01-01,,Arc-Royal|3058-01-01,3081-03-15,Fletcher|2650-01-01,,Galatea|3000-01-01,,Westerhand|3057-01-01,3081-03-15,Northwind|3020-01-01,,Herotitus
+hiringHalls=3031-01-01,3067-10-15,great,Outreach|2700-01-01,,minor,Solaris|3057-01-01,,standard,Arc-Royal|3058-01-01,3081-03-15,minor,Fletcher|2650-01-01,,great,Galatea|3000-01-01,,great,Westerhand|3057-01-01,3081-03-15,great,Northwind|3020-01-01,,minor,Herotitus|2694-01-01,,questionable,Antallos (Port Krin)|2912-01-01,,questionable,Astrokaszy|3052-01-01,,minor,Noisiel|2811-01-01,3045-01-01,minor,Le Blanc
shipSearchCost=100000
shipSearchLengthWeeks=4
diff --git a/MekHQ/resources/mekhq/resources/Market.properties b/MekHQ/resources/mekhq/resources/Market.properties
index 7dc5e76dd8..1d1ee337fa 100644
--- a/MekHQ/resources/mekhq/resources/Market.properties
+++ b/MekHQ/resources/mekhq/resources/Market.properties
@@ -5,6 +5,8 @@ ContractMarketMethod.NONE.text=Disabled
ContractMarketMethod.NONE.toolTipText=The Contract Market is disabled.
ContractMarketMethod.ATB_MONTHLY.text=AtB Monthly
ContractMarketMethod.ATB_MONTHLY.toolTipText=This is the standard Against the Bot Contract Market, which refreshes monthly.
+ContractMarketMethod.CAM_OPS.text=Campaign Ops (Under Development)
+ContractMarketMethod.CAM_OPS.toolTipText=This is the standard Campaign Operations Contract Market, which refreshes monthly. This feature is currently under development and may not work properly.
# UnitMarketMethod Enum
UnitMarketMethod.NONE.text=Disabled
diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java
index 213b694e95..0c4448bda2 100644
--- a/MekHQ/src/mekhq/campaign/Campaign.java
+++ b/MekHQ/src/mekhq/campaign/Campaign.java
@@ -59,10 +59,9 @@
import mekhq.campaign.log.HistoricalLogEntry;
import mekhq.campaign.log.LogEntry;
import mekhq.campaign.log.ServiceLogger;
-import mekhq.campaign.market.ContractMarket;
-import mekhq.campaign.market.PartsStore;
-import mekhq.campaign.market.PersonnelMarket;
-import mekhq.campaign.market.ShoppingList;
+import mekhq.campaign.market.*;
+import mekhq.campaign.market.contractMarket.AbstractContractMarket;
+import mekhq.campaign.market.contractMarket.AtbMonthlyContractMarket;
import mekhq.campaign.market.unitMarket.AbstractUnitMarket;
import mekhq.campaign.market.unitMarket.DisabledUnitMarket;
import mekhq.campaign.mission.*;
@@ -210,8 +209,7 @@ public class Campaign implements ITechManager {
private Boolean fieldKitchenWithinCapacity;
- // this is updated and used per gaming session, it is enabled/disabled via the
- // Campaign options
+ // this is updated and used per gaming session, it is enabled/disabled via the Campaign options
// we're re-using the LogEntry class that is used to store Personnel entries
public LinkedList inMemoryLogHistory = new LinkedList<>();
@@ -240,7 +238,7 @@ public class Campaign implements ITechManager {
private ShoppingList shoppingList;
private PersonnelMarket personnelMarket;
- private ContractMarket contractMarket; // AtB
+ private AbstractContractMarket contractMarket;
private AbstractUnitMarket unitMarket;
private transient AbstractDeath death;
@@ -319,7 +317,7 @@ public Campaign() {
shoppingList = new ShoppingList();
news = new News(getGameYear(), id.getLeastSignificantBits());
setPersonnelMarket(new PersonnelMarket());
- setContractMarket(new ContractMarket());
+ setContractMarket(new AtbMonthlyContractMarket());
setUnitMarket(new DisabledUnitMarket());
setDeath(new DisabledRandomDeath(getCampaignOptions(), false));
setDivorce(new DisabledRandomDivorce(getCampaignOptions()));
@@ -465,13 +463,11 @@ public void setPersonnelMarket(final PersonnelMarket personnelMarket) {
this.personnelMarket = personnelMarket;
}
- // TODO : AbstractContractMarket : Swap to AbstractContractMarket
- public ContractMarket getContractMarket() {
+ public AbstractContractMarket getContractMarket() {
return contractMarket;
}
- // TODO : AbstractContractMarket : Swap to AbstractContractMarket
- public void setContractMarket(final ContractMarket contractMarket) {
+ public void setContractMarket(final AbstractContractMarket contractMarket) {
this.contractMarket = contractMarket;
}
@@ -1662,8 +1658,7 @@ public boolean recruitPerson(Person p, PrisonerStatus prisonerStatus, boolean gm
* appropriate to the person's phenotype and the player's faction.
*
* @param person The Bloodname candidate
- * @param ignoreDice If true, skips the random roll and assigns a Bloodname
- * automatically
+ * @param ignoreDice If true, skips the random roll and assigns a Bloodname automatically
*/
public void checkBloodnameAdd(Person person, boolean ignoreDice) {
// if person is non-clan or does not have a phenotype
@@ -1671,8 +1666,7 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) {
return;
}
- // Person already has a bloodname, we open up the dialog to ask if they want to
- // keep the
+ // Person already has a bloodname, we open up the dialog to ask if they want to keep the
// current bloodname or assign a new one
if (!person.getBloodname().isEmpty()) {
int result = JOptionPane.showConfirmDialog(null,
@@ -2221,6 +2215,18 @@ public Person findBestInRole(PersonnelRole role, String skill) {
return findBestInRole(role, skill, null);
}
+ public @Nullable Person findBestAtSkill(String skill) {
+ Person person = null;
+ int highest = 0;
+ for (Person p : getActivePersonnel()) {
+ if (p.getSkill(skill) != null && p.getSkill(skill).getLevel() > highest) {
+ highest = p.getSkill(skill).getLevel();
+ person = p;
+ }
+ }
+ return person;
+ }
+
/**
* @return The list of all active {@link Person}s who qualify as technicians
* ({@link Person#isTech()}));
@@ -3512,7 +3518,7 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem
}
private void processNewDayATB() {
- contractMarket.generateContractOffers(this); // TODO : AbstractContractMarket : Remove
+ contractMarket.generateContractOffers(this);
if ((getShipSearchExpiration() != null) && !getShipSearchExpiration().isAfter(getLocalDate())) {
setShipSearchExpiration(null);
@@ -6501,6 +6507,28 @@ public int getAtBUnitRatingMod() {
: reputation.getAtbModifier();
}
+ public int getReputationFactor() {
+ return switch (campaignOptions.getUnitRatingMethod()) {
+ case NONE -> 5;
+ case FLD_MAN_MERCS_REV -> getAtBUnitRatingMod() * 2;
+ case CAMPAIGN_OPS -> (int) ((getReputation().getReputationRating() * 0.2) + 0.5);
+ };
+ }
+
+ /**
+ * Returns the Strategy skill of the designated commander in the campaign.
+ *
+ * @return The value of the commander's strategy skill if a commander exists, otherwise 0.
+ */
+ public int getCommanderStrategy() {
+ int cmdrStrategy = 0;
+ if (getFlaggedCommander() != null &&
+ getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) {
+ cmdrStrategy = getFlaggedCommander().getSkill(SkillType.S_STRATEGY).getLevel();
+ }
+ return cmdrStrategy;
+ }
+
@Deprecated
public int getUnitRatingAsInteger() {
return getAtBUnitRatingMod();
@@ -7560,8 +7588,7 @@ public void initTimeInRank() {
LocalDate join = null;
for (LogEntry e : p.getPersonnelLog()) {
if (join == null) {
- // If by some nightmare there is no date from the below, just use the first
- // entry.
+ // If by some nightmare there is no date from the below, just use the first entry.
join = e.getDate();
}
@@ -7571,8 +7598,7 @@ public void initTimeInRank() {
}
}
- // For that one in a billion chance the log is empty. Clone today's date and
- // subtract a year
+ // For that one in a billion chance the log is empty. Clone today's date and subtract a year
p.setLastRankChangeDate((join != null) ? join : getLocalDate().minusYears(1));
}
}
@@ -7684,8 +7710,7 @@ public boolean checkOverDueLoans() {
}
/**
- * Checks if a turnover prompt should be displayed based on campaign options and
- * current date.
+ * Checks if a turnover prompt should be displayed based on campaign options and current date.
*
* @return An integer representing the user's choice:
* -1 if turnover prompt should not be displayed.
@@ -7747,7 +7772,8 @@ public int checkTurnoverPrompt() {
JOptionPane.INFORMATION_MESSAGE,
null,
options,
- options[0]);
+ options[0]
+ );
}
/**
diff --git a/MekHQ/src/mekhq/campaign/againstTheBot/AtBConfiguration.java b/MekHQ/src/mekhq/campaign/againstTheBot/AtBConfiguration.java
index 225119ece7..0623f56485 100644
--- a/MekHQ/src/mekhq/campaign/againstTheBot/AtBConfiguration.java
+++ b/MekHQ/src/mekhq/campaign/againstTheBot/AtBConfiguration.java
@@ -25,6 +25,10 @@
import megamek.common.annotations.Nullable;
import megamek.logging.MMLogger;
import mekhq.MekHQ;
+import mekhq.campaign.universe.HiringHall;
+import mekhq.campaign.universe.Planet;
+import mekhq.campaign.universe.enums.HiringHallLevel;
+import mekhq.utilities.MHQXMLUtility;
import mekhq.campaign.Campaign;
import mekhq.campaign.finances.Money;
import mekhq.campaign.personnel.Person;
@@ -77,7 +81,7 @@ public class AtBConfiguration {
private HashMap>> botLanceTables = new HashMap<>();
/* Contract generation */
- private ArrayList> hiringHalls;
+ private HashMap hiringHalls = new HashMap<>();
/* Personnel and unit markets */
private Money shipSearchCost;
@@ -93,7 +97,6 @@ public class AtBConfiguration {
MekHQ.getMHQOptions().getLocale());
private AtBConfiguration() {
- hiringHalls = new ArrayList<>();
dsTable = new WeightedTable<>();
jsTable = new WeightedTable<>();
shipSearchCost = Money.of(100000);
@@ -166,10 +169,16 @@ private void setAllValuesToDefaults() {
case "hiringHalls":
for (String entry : property.split("\\|")) {
String[] fields = entry.split(",");
- hiringHalls.add(new DatedRecord<>(
- !fields[0].isBlank() ? MHQXMLUtility.parseDate(fields[0]) : null,
- !fields[1].isBlank() ? MHQXMLUtility.parseDate(fields[1]) : null,
- fields[2]));
+ LocalDate startDate = !fields[0].isBlank() ? MHQXMLUtility.parseDate(fields[0]) : null;
+ LocalDate endDate = !fields[1].isBlank() ? MHQXMLUtility.parseDate(fields[1]) : null;
+ HiringHallLevel level = null;
+ try {
+ level = HiringHallLevel.valueOf(fields[2].toUpperCase());
+ } catch (IllegalArgumentException ex) {
+ level = HiringHallLevel.GREAT;
+ }
+ String name = fields[3];
+ hiringHalls.put(name, new HiringHall(level, startDate, endDate, name));
}
break;
case "shipSearchCost":
@@ -315,8 +324,16 @@ public static String getParentFactionType(final Faction faction) {
}
public boolean isHiringHall(String planet, LocalDate date) {
- return hiringHalls.stream().anyMatch(rec -> rec.getValue().equals(planet)
- && rec.fitsDate(date));
+ HiringHall hall = hiringHalls.get(planet);
+ return hall != null && hall.isActive(date);
+ }
+
+ public HiringHallLevel getHiringHallLevel(String planet, LocalDate date) {
+ HiringHall hall = hiringHalls.get(planet);
+ if (hall != null && hall.isActive(date)) {
+ return hall.getLevel();
+ }
+ return HiringHallLevel.NONE;
}
public Money getShipSearchCost() {
@@ -499,7 +516,19 @@ private void loadContractGenerationNodeFromXml(Node node) {
if (wn2.getAttributes().getNamedItem("end") != null) {
end = MHQXMLUtility.parseDate(wn2.getAttributes().getNamedItem("end").getTextContent());
}
- hiringHalls.add(new DatedRecord<>(start, end, wn2.getTextContent()));
+ HiringHallLevel level = HiringHallLevel.NONE;
+ if (wn2.getAttributes().getNamedItem("level") != null) {
+ try {
+ level = HiringHallLevel.valueOf(wn2.getAttributes().getNamedItem("level").getTextContent().toUpperCase());
+ } catch (IllegalArgumentException e) {
+ logger.warn("Invalid value for Hiring Hall level, falling back to NONE: " + e);
+ }
+ } else {
+ // Backwards compatibility--hiring halls in atbconfig.xml should default to GREAT
+ level = HiringHallLevel.GREAT;
+ }
+ String planetName = wn2.getTextContent();
+ hiringHalls.put(planetName, new HiringHall(level, start, end, planetName));
}
}
}
diff --git a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java
index 0bbc8daf25..db0f7cb6a0 100644
--- a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java
+++ b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java
@@ -37,7 +37,8 @@
import mekhq.campaign.force.Force;
import mekhq.campaign.force.Lance;
import mekhq.campaign.icons.UnitIcon;
-import mekhq.campaign.market.ContractMarket;
+import mekhq.campaign.market.contractMarket.AbstractContractMarket;
+import mekhq.campaign.market.contractMarket.AtbMonthlyContractMarket;
import mekhq.campaign.market.PersonnelMarket;
import mekhq.campaign.market.ShoppingList;
import mekhq.campaign.mission.AtBContract;
@@ -271,7 +272,7 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException {
foundPersonnelMarket = true;
} else if (xn.equalsIgnoreCase("contractMarket")) {
// CAW: implicit DEPENDS-ON to the node
- retVal.setContractMarket(ContractMarket.generateInstanceFromXML(wn, retVal, version));
+ retVal.setContractMarket(AbstractContractMarket.generateInstanceFromXML(wn, retVal, version));
foundContractMarket = true;
} else if (xn.equalsIgnoreCase("unitMarket")) {
// Windchild: implicit DEPENDS ON to the nodes
@@ -486,7 +487,7 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException {
}
if (!foundContractMarket) {
- retVal.setContractMarket(new ContractMarket());
+ retVal.setContractMarket(new AtbMonthlyContractMarket());
}
if (!foundUnitMarket) {
diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java
new file mode 100644
index 0000000000..80c4ab09a1
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AbstractContractMarket.java
@@ -0,0 +1,538 @@
+package mekhq.campaign.market.contractMarket;
+
+import megamek.Version;
+import megamek.codeUtilities.MathUtility;
+import megamek.common.Compute;
+import megamek.common.enums.SkillLevel;
+import megamek.logging.MMLogger;
+import mekhq.campaign.Campaign;
+import mekhq.campaign.market.enums.ContractMarketMethod;
+import mekhq.campaign.mission.AtBContract;
+import mekhq.campaign.mission.Contract;
+import mekhq.campaign.mission.Mission;
+import mekhq.campaign.mission.enums.AtBContractType;
+import mekhq.campaign.mission.enums.ContractCommandRights;
+import mekhq.campaign.rating.IUnitRating;
+import mekhq.campaign.universe.Factions;
+import mekhq.campaign.universe.RandomFactionGenerator;
+import mekhq.utilities.MHQXMLUtility;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+import java.util.*;
+
+/**
+ * Abstract base class for various Contract Market types in AtB/Stratcon. Responsible for generation
+ * and initialization of AtBContracts.
+ */
+public abstract class AbstractContractMarket {
+ public static final int CLAUSE_COMMAND = 0;
+ public static final int CLAUSE_SALVAGE = 1;
+ public static final int CLAUSE_SUPPORT = 2;
+ public static final int CLAUSE_TRANSPORT = 3;
+ public static final int CLAUSE_NUM = 4;
+
+
+ protected List contracts = new ArrayList<>();
+ protected int lastId = 0;
+ protected Map contractIds = new HashMap<>();
+ protected Map clauseMods = new HashMap<>();
+
+ /**
+ * An arbitrary maximum number of attempts to generate a contract.
+ */
+ protected final static int MAXIMUM_GENERATION_RETRIES = 3;
+
+ /* It is possible to call addFollowup more than once for the
+ * same contract by canceling the dialog and running it again;
+ * this is the easiest place to track it to prevent
+ * multiple followup contracts.
+ * key: followup id
+ * value: main contract id
+ */
+ protected HashMap followupContracts = new HashMap<>();
+
+ /**
+ * An arbitrary maximum number of attempts to find a random employer faction that
+ * is not a Mercenary.
+ */
+ protected final static int MAXIMUM_ATTEMPTS_TO_FIND_NON_MERC_EMPLOYER = 20;
+
+ private final ContractMarketMethod method;
+ private static final MMLogger logger = MMLogger.create(AbstractContractMarket.class);
+
+
+ /**
+ * Generate a new contract and add it to the market.
+ * @param campaign
+ * @return The newly generated contract
+ */
+ abstract public AtBContract addAtBContract(Campaign campaign);
+
+ /**
+ * Generate available contract offers for the player's force.
+ * @param campaign
+ * @param newCampaign Boolean indicating whether this is a fresh campaign.
+ */
+ abstract public void generateContractOffers(Campaign campaign, boolean newCampaign);
+
+ /**
+ * Add a followup contract to an existing contract.
+ * @param campaign
+ * @param contract
+ */
+ abstract public void addFollowup(Campaign campaign, AtBContract contract);
+
+ /**
+ * Calculate the total payment modifier for the contract based on the configured market method
+ * (e.g., CAM_OPS, ATB_MONTHLY).
+ * @param campaign
+ * @param contract
+ * @return a double representing the total payment multiplier.
+ */
+ abstract public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract);
+
+ protected AbstractContractMarket(final ContractMarketMethod method) {
+ this.method = method;
+ }
+
+ /**
+ *
+ * @return the Method (e.g., CAM_OPS, ATB_MONTHLY) associated with the Contract Market instance
+ */
+ public ContractMarketMethod getMethod() {
+ return method;
+ }
+
+ /**
+ * Empty an available contract from the market.
+ * @param c contract to remove
+ */
+ public void removeContract(Contract c) {
+ contracts.remove(c);
+ contractIds.remove(c.getId());
+ clauseMods.remove(c.getId());
+ followupContracts.remove(c.getId());
+ }
+
+ /**
+ * Rerolls a specific clause in a contract, usually via negotiation.
+ * @param c the contract being negotiated
+ * @param clause ID representing the type of clause.
+ * @param campaign
+ */
+ public void rerollClause(AtBContract c, int clause, Campaign campaign) {
+ if (null != clauseMods.get(c.getId())) {
+ switch (clause) {
+ case CLAUSE_COMMAND -> rollCommandClause(c, clauseMods.get(c.getId()).mods[clause]);
+ case CLAUSE_SALVAGE ->
+ rollSalvageClause(c, clauseMods.get(c.getId()).mods[clause], campaign.getCampaignOptions().getContractMaxSalvagePercentage());
+ case CLAUSE_TRANSPORT -> rollTransportClause(c, clauseMods.get(c.getId()).mods[clause]);
+ case CLAUSE_SUPPORT -> rollSupportClause(c, clauseMods.get(c.getId()).mods[clause]);
+ }
+ clauseMods.get(c.getId()).rerollsUsed[clause]++;
+ c.calculateContract(campaign);
+ }
+ }
+
+ /**
+ * Returns the number of rerolls used so far for a specific clause.
+ * @param c
+ * @param clause ID representing the type of clause.
+ * @return
+ */
+ public int getRerollsUsed(Contract c, int clause) {
+ if (null != clauseMods.get(c.getId())) {
+ return clauseMods.get(c.getId()).rerollsUsed[clause];
+ }
+ return 0;
+ }
+
+ /**
+ * @return a list of currently active contracts on the market
+ */
+ public List getContracts() {
+ return contracts;
+ }
+
+ /**
+ * Empties the market and generates a new batch of contract offers for an existing campaign.
+ * @param campaign
+ */
+ public void generateContractOffers(Campaign campaign) {
+ generateContractOffers(campaign, false);
+ }
+
+ protected void updateReport(Campaign campaign) {
+ if (campaign.getCampaignOptions().isContractMarketReportRefresh()) {
+ campaign.addReport("Contract market updated");
+ }
+ }
+
+ /**
+ * Determines the number of required lances to be deployed for a contract. For Mercenary subcontracts
+ * this defaults to 1; otherwise the number is based on the number of combat units in the campaign.
+ * @param campaign
+ * @param contract
+ * @return The number of lances required to be deployed.
+ */
+ public int calculateRequiredLances(Campaign campaign, AtBContract contract) {
+ int maxDeployedLances = calculateMaxDeployedLances(campaign);
+ if (contract.isSubcontract()) {
+ return 1;
+ } else {
+ int requiredLances = Math.max(AtBContract.getEffectiveNumUnits(campaign) / 6, 1);
+ return Math.min(requiredLances, maxDeployedLances);
+ }
+ }
+
+ /**
+ * Determine the maximum number of lances the force can deploy. The result is based on the
+ * commander's Strategy skill and various campaign options.
+ * @param campaign
+ * @return the maximum number of lances that can be deployed on the contract.
+ */
+ public int calculateMaxDeployedLances(Campaign campaign) {
+ return campaign.getCampaignOptions().getBaseStrategyDeployment() +
+ campaign.getCampaignOptions().getAdditionalStrategyDeployment() *
+ campaign.getCommanderStrategy();
+ }
+
+ protected SkillLevel getSkillRating(int roll) {
+ if (roll <= 5) {
+ return SkillLevel.GREEN;
+ } else if (roll <= 9) {
+ return SkillLevel.REGULAR;
+ } else if (roll <= 11) {
+ return SkillLevel.VETERAN;
+ } else {
+ return SkillLevel.ELITE;
+ }
+ }
+
+ protected int getQualityRating(int roll) {
+ if (roll <= 5) {
+ return IUnitRating.DRAGOON_F;
+ } else if (roll <= 8) {
+ return IUnitRating.DRAGOON_D;
+ } else if (roll <= 10) {
+ return IUnitRating.DRAGOON_C;
+ } else if (roll == 11) {
+ return IUnitRating.DRAGOON_B;
+ } else {
+ return IUnitRating.DRAGOON_A;
+ }
+ }
+
+ protected void rollCommandClause(final Contract contract, final int modifier) {
+ final int roll = Compute.d6(2) + modifier;
+ if (roll < 3) {
+ contract.setCommandRights(ContractCommandRights.INTEGRATED);
+ } else if (roll < 8) {
+ contract.setCommandRights(ContractCommandRights.HOUSE);
+ } else if (roll < 12) {
+ contract.setCommandRights(ContractCommandRights.LIAISON);
+ } else {
+ contract.setCommandRights(ContractCommandRights.INDEPENDENT);
+ }
+ }
+
+ protected void rollSalvageClause(AtBContract contract, int mod, int contractMaxSalvagePercentage) {
+ contract.setSalvageExchange(false);
+ int roll = Math.min(Compute.d6(2) + mod, 13);
+ if (roll < 2) {
+ contract.setSalvagePct(0);
+ } else if (roll < 4) {
+ contract.setSalvageExchange(true);
+ int r;
+ do {
+ r = Compute.d6(2);
+ } while (r < 4);
+ contract.setSalvagePct(Math.min((r - 3) * 10, contractMaxSalvagePercentage));
+ } else {
+ contract.setSalvagePct(Math.min((roll - 3) * 10, contractMaxSalvagePercentage));
+ }
+ }
+
+ protected void rollSupportClause(AtBContract contract, int mod) {
+ int roll = Compute.d6(2) + mod;
+ contract.setStraightSupport(0);
+ contract.setBattleLossComp(0);
+ if (roll < 3) {
+ contract.setStraightSupport(0);
+ } else if (roll < 8) {
+ contract.setStraightSupport((roll - 2) * 20);
+ } else if (roll == 8) {
+ contract.setBattleLossComp(10);
+ } else {
+ contract.setBattleLossComp(Math.min((roll - 8) * 20, 100));
+ }
+ }
+
+ protected void rollTransportClause(AtBContract contract, int mod) {
+ int roll = Compute.d6(2) + mod;
+ if (roll < 2) {
+ contract.setTransportComp(0);
+ } else if (roll < 6) {
+ contract.setTransportComp((20 + (roll - 2) * 5));
+ } else if (roll < 10) {
+ contract.setTransportComp((45 + (roll - 6) * 5));
+ } else {
+ contract.setTransportComp(100);
+ }
+ }
+
+ protected AtBContractType findMissionType(int unitRatingMod, boolean majorPower) {
+ final AtBContractType[][] table = {
+ // col 0: IS Houses
+ { AtBContractType.GUERRILLA_WARFARE, AtBContractType.RECON_RAID, AtBContractType.PIRATE_HUNTING,
+ AtBContractType.PLANETARY_ASSAULT, AtBContractType.OBJECTIVE_RAID,
+ AtBContractType.OBJECTIVE_RAID,
+ AtBContractType.EXTRACTION_RAID, AtBContractType.RECON_RAID, AtBContractType.GARRISON_DUTY,
+ AtBContractType.CADRE_DUTY, AtBContractType.RELIEF_DUTY },
+ // col 1: Others
+ { AtBContractType.GUERRILLA_WARFARE, AtBContractType.RECON_RAID, AtBContractType.PLANETARY_ASSAULT,
+ AtBContractType.OBJECTIVE_RAID, AtBContractType.EXTRACTION_RAID, AtBContractType.PIRATE_HUNTING,
+ AtBContractType.SECURITY_DUTY, AtBContractType.OBJECTIVE_RAID, AtBContractType.GARRISON_DUTY,
+ AtBContractType.CADRE_DUTY, AtBContractType.DIVERSIONARY_RAID }
+ };
+ int roll = MathUtility.clamp(Compute.d6(2) + unitRatingMod - IUnitRating.DRAGOON_C, 2, 12);
+ return table[majorPower ? 0 : 1][roll - 2];
+ }
+
+ protected void setEnemyCode(AtBContract contract) {
+ if (contract.getContractType().isPirateHunting()) {
+ contract.setEnemyCode("PIR");
+ } else if (contract.getContractType().isRiotDuty()) {
+ contract.setEnemyCode("REB");
+ } else {
+ contract.setEnemyCode(RandomFactionGenerator.getInstance().getEnemy(contract.getEmployerCode(),
+ contract.getContractType().isGarrisonType()));
+ }
+ }
+
+ protected void setAttacker(AtBContract contract) {
+ boolean isAttacker = !contract.getContractType().isGarrisonType()
+ || (contract.getContractType().isReliefDuty() && (Compute.d6() < 4))
+ || contract.getEnemy().isRebel();
+ contract.setAttacker(isAttacker);
+ }
+
+ protected void setSystemId(AtBContract contract) throws NoContractLocationFoundException {
+ // FIXME : Windchild : I don't work properly
+ if (contract.isAttacker()) {
+ contract.setSystemId(RandomFactionGenerator.getInstance().getMissionTarget(contract.getEmployerCode(),
+ contract.getEnemyCode()));
+ } else {
+ contract.setSystemId(RandomFactionGenerator.getInstance().getMissionTarget(contract.getEnemyCode(),
+ contract.getEmployerCode()));
+ }
+ if (contract.getSystem() == null) {
+ String errorMsg = "Could not find contract location for "
+ + contract.getEmployerCode() + " vs. " + contract.getEnemyCode();
+ logger.warn(errorMsg);
+ throw new NoContractLocationFoundException(errorMsg);
+ }
+ }
+
+ protected void setIsRiotDuty(AtBContract contract) {
+ if (contract.getContractType().isGarrisonDuty() && contract.getEnemy().isRebel()) {
+ contract.setContractType(AtBContractType.RIOT_DUTY);
+ }
+ }
+
+ protected void setAllyRating(AtBContract contract, int year) {
+ int mod = 0;
+ if (contract.getEnemy().isRebelOrPirate()) {
+ mod -= 1;
+ }
+
+ if (contract.getContractType().isGuerrillaWarfare() || contract.getContractType().isCadreDuty()) {
+ mod -= 3;
+ } else if (contract.getContractType().isGarrisonDuty() || contract.getContractType().isSecurityDuty()) {
+ mod -= 2;
+ }
+
+ if (AtBContract.isMinorPower(contract.getEmployerCode())) {
+ mod -= 1;
+ }
+
+ if (contract.getEnemy().isIndependent()) {
+ mod -= 2;
+ }
+
+ if (contract.getContractType().isPlanetaryAssault()) {
+ mod += 1;
+ }
+
+ if (Factions.getInstance().getFaction(contract.getEmployerCode()).isClan() && !contract.isAttacker()) {
+ // facing front-line units
+ mod += 1;
+ }
+ contract.setAllySkill(getSkillRating(Compute.d6(2) + mod));
+ if (year > 2950 && year < 3039 &&
+ !Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) {
+ mod -= 1;
+ }
+ contract.setAllyQuality(getQualityRating(Compute.d6(2) + mod));
+ }
+
+ protected void setEnemyRating(AtBContract contract, int year) {
+ int mod = 0;
+ if (contract.getEnemy().isRebelOrPirate()) {
+ mod -= 2;
+ }
+ if (contract.getContractType().isGuerrillaWarfare()) {
+ mod += 2;
+ }
+ if (contract.getContractType().isPlanetaryAssault()) {
+ mod += 1;
+ }
+ if (AtBContract.isMinorPower(contract.getEmployerCode())) {
+ mod -= 1;
+ }
+ if (Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) {
+ mod += contract.isAttacker() ? 2 : 4;
+ }
+ contract.setEnemySkill(getSkillRating(Compute.d6(2) + mod));
+ if (year > 2950 && year < 3039 &&
+ !Factions.getInstance().getFaction(contract.getEnemyCode()).isClan()) {
+ mod -= 1;
+ }
+ contract.setEnemyQuality(getQualityRating(Compute.d6(2) + mod));
+ }
+
+ public void writeToXML(final PrintWriter pw, int indent) {
+ MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "contractMarket");
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "method", method.toString());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lastId", lastId);
+ for (final Contract contract : contracts) {
+ contract.writeToXML(pw, indent);
+ }
+
+ for (final Integer key : clauseMods.keySet()) {
+ if (!contractIds.containsKey(key)) {
+ continue;
+ }
+
+ MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "clauseMods", "id", key);
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "mods", clauseMods.get(key).mods);
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "rerollsUsed", clauseMods.get(key).rerollsUsed);
+ MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "clauseMods");
+ }
+ MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "contractMarket");
+ }
+
+ public static AbstractContractMarket generateInstanceFromXML(Node wn, Campaign c, Version version) {
+ AbstractContractMarket retVal = null;
+
+ try {
+ retVal = parseMarketMethod(wn);
+ // Okay, now load Part-specific fields!
+ NodeList nl = wn.getChildNodes();
+
+ // Loop through the nodes and load our contract offers
+ for (int x = 0; x < nl.getLength(); x++) {
+ Node wn2 = nl.item(x);
+
+ // If it's not an element node, we ignore it.
+ if (wn2.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+ if (wn2.getNodeName().equalsIgnoreCase("lastId")) {
+ retVal.lastId = Integer.parseInt(wn2.getTextContent());
+ } else if (wn2.getNodeName().equalsIgnoreCase("mission")) {
+ Mission m = Mission.generateInstanceFromXML(wn2, c, version);
+
+ if (m instanceof Contract) {
+ retVal.contracts.add((Contract) m);
+ retVal.contractIds.put(m.getId(), (Contract) m);
+ }
+ } else if (wn2.getNodeName().equalsIgnoreCase("clauseMods")) {
+ int key = Integer.parseInt(wn2.getAttributes().getNamedItem("id").getTextContent());
+ ClauseMods cm = new ClauseMods();
+ NodeList nl2 = wn2.getChildNodes();
+ for (int i = 0; i < nl2.getLength(); i++) {
+ Node wn3 = nl2.item(i);
+ if (wn3.getNodeName().equalsIgnoreCase("mods")) {
+ String [] s = wn3.getTextContent().split(",");
+ for (int j = 0; j < s.length; j++) {
+ cm.mods[j] = Integer.parseInt(s[j]);
+ }
+ } else if (wn3.getNodeName().equalsIgnoreCase("rerollsUsed")) {
+ String [] s = wn3.getTextContent().split(",");
+ for (int j = 0; j < s.length; j++) {
+ cm.rerollsUsed[j] = Integer.parseInt(s[j]);
+ }
+ }
+ }
+ retVal.clauseMods.put(key, cm);
+ }
+ }
+
+ // Restore any parent contract references
+ for (Contract contract : retVal.contracts) {
+ if (contract instanceof AtBContract) {
+ final AtBContract atbContract = (AtBContract) contract;
+ atbContract.restore(c);
+ }
+ }
+ } catch (Exception ex) {
+ // Errrr, apparently either the class name was invalid...
+ // Or the listed name doesn't exist.
+ // Doh!
+ logger.error("", ex);
+ }
+
+ return retVal;
+ }
+
+ private static AbstractContractMarket parseMarketMethod(Node xmlNode) {
+ AbstractContractMarket market = null;
+ NodeList nodeList = xmlNode.getChildNodes();
+ for (int x = 0; x < nodeList.getLength(); x++) {
+ Node childNode = nodeList.item(x);
+ if (childNode.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+ if (childNode.getNodeName().equalsIgnoreCase("method")) {
+ String name = childNode.getTextContent();
+ if (Objects.equals(name, ContractMarketMethod.CAM_OPS.toString())) {
+ market = new CamOpsContractMarket();
+ break;
+ } else if (Objects.equals(name, ContractMarketMethod.NONE.toString())) {
+ market = new DisabledContractMarket();
+ break;
+ } else {
+ market = new AtbMonthlyContractMarket();
+ break;
+ }
+ }
+ }
+ if (market == null) {
+ logger.warn("No Contract Market method found in XML...falling back to AtB_Monthly");
+ market = new AtbMonthlyContractMarket();
+ }
+ return market;
+ }
+
+ /* Keep track of how many rerolls remain for each contract clause
+ * based on the admin's negotiation skill. Also track bonuses, as
+ * the random clause bonuses should be persistent.
+ */
+ protected static class ClauseMods {
+ public int[] rerollsUsed = {0, 0, 0, 0};
+ public int[] mods = {0, 0, 0, 0};
+ }
+
+ /**
+ * Exception indicating that no valid location was generated for a contract and that the contract
+ * is invalid.
+ */
+ public static class NoContractLocationFoundException extends RuntimeException {
+ public NoContractLocationFoundException(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/market/ContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java
similarity index 54%
rename from MekHQ/src/mekhq/campaign/market/ContractMarket.java
rename to MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java
index b67c14e312..6e2f237146 100644
--- a/MekHQ/src/mekhq/campaign/market/ContractMarket.java
+++ b/MekHQ/src/mekhq/campaign/market/contractMarket/AtbMonthlyContractMarket.java
@@ -18,21 +18,12 @@
* You should have received a copy of the GNU General Public License
* along with MekHQ. If not, see .
*/
-package mekhq.campaign.market;
+package mekhq.campaign.market.contractMarket;
-import java.io.PrintWriter;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import megamek.Version;
-import megamek.codeUtilities.MathUtility;
import megamek.common.Compute;
import megamek.common.annotations.Nullable;
import megamek.common.enums.SkillLevel;
@@ -42,8 +33,6 @@
import mekhq.campaign.JumpPath;
import mekhq.campaign.market.enums.ContractMarketMethod;
import mekhq.campaign.mission.AtBContract;
-import mekhq.campaign.mission.Contract;
-import mekhq.campaign.mission.Mission;
import mekhq.campaign.mission.enums.AtBContractType;
import mekhq.campaign.mission.enums.ContractCommandRights;
import mekhq.campaign.personnel.Person;
@@ -55,7 +44,6 @@
import mekhq.campaign.universe.PlanetarySystem;
import mekhq.campaign.universe.RandomFactionGenerator;
import mekhq.campaign.universe.Systems;
-import mekhq.utilities.MHQXMLUtility;
/**
* Contract offers that are generated monthly under AtB rules.
@@ -64,65 +52,14 @@
*
* @author Neoancient
*/
-public class ContractMarket {
- private static final MMLogger logger = MMLogger.create(ContractMarket.class);
-
- // TODO: Implement a method that rolls each day to see whether a new contract
- // appears or an offer disappears
-
- public final static int CLAUSE_COMMAND = 0;
- public final static int CLAUSE_SALVAGE = 1;
- public final static int CLAUSE_SUPPORT = 2;
- public final static int CLAUSE_TRANSPORT = 3;
- public final static int CLAUSE_NUM = 4;
-
- /**
- * An arbitrary maximum number of attempts to generate a contract.
- */
- private final static int MAXIMUM_GENERATION_RETRIES = 3;
-
- /**
- * An arbitrary maximum number of attempts to find a random employer faction
- * that
- * is not a Mercenary.
- */
- private final static int MAXIMUM_ATTEMPTS_TO_FIND_NON_MERC_EMPLOYER = 20;
-
- private ContractMarketMethod method = ContractMarketMethod.ATB_MONTHLY;
-
- private List contracts;
- private int lastId = 0;
- private Map contractIds;
- private Map clauseMods;
-
- /*
- * It is possible to call addFollowup more than once for the
- * same contract by canceling the dialog and running it again;
- * this is the easiest place to track it to prevent
- * multiple followup contracts.
- * key: followup id
- * value: main contract id
- */
- private HashMap followupContracts;
-
- public ContractMarket() {
- contracts = new ArrayList<>();
- contractIds = new HashMap<>();
- clauseMods = new HashMap<>();
- followupContracts = new HashMap<>();
- }
-
- public List getContracts() {
- return contracts;
- }
+public class AtbMonthlyContractMarket extends AbstractContractMarket {
+ private static final MMLogger logger = MMLogger.create(AtbMonthlyContractMarket.class);
- public void removeContract(Contract c) {
- contracts.remove(c);
- contractIds.remove(c.getId());
- clauseMods.remove(c.getId());
- followupContracts.remove(c.getId());
+ public AtbMonthlyContractMarket() {
+ super(ContractMarketMethod.ATB_MONTHLY);
}
+ @Override
public AtBContract addAtBContract(Campaign campaign) {
AtBContract c = generateAtBContract(campaign, campaign.getAtBUnitRatingMod());
if (c != null) {
@@ -131,42 +68,9 @@ public AtBContract addAtBContract(Campaign campaign) {
return c;
}
- public int getRerollsUsed(Contract c, int clause) {
- if (null != clauseMods.get(c.getId())) {
- return clauseMods.get(c.getId()).rerollsUsed[clause];
- }
- return 0;
- }
-
- public void rerollClause(AtBContract c, int clause, Campaign campaign) {
- if (null != clauseMods.get(c.getId())) {
- switch (clause) {
- case CLAUSE_COMMAND:
- rollCommandClause(c, clauseMods.get(c.getId()).mods[clause]);
- break;
- case CLAUSE_SALVAGE:
- rollSalvageClause(c, clauseMods.get(c.getId()).mods[clause],
- campaign.getCampaignOptions().getContractMaxSalvagePercentage());
- break;
- case CLAUSE_TRANSPORT:
- rollTransportClause(c, clauseMods.get(c.getId()).mods[clause]);
- break;
- case CLAUSE_SUPPORT:
- rollSupportClause(c, clauseMods.get(c.getId()).mods[clause]);
- break;
- }
- clauseMods.get(c.getId()).rerollsUsed[clause]++;
- c.calculateContract(campaign);
- }
- }
-
- public void generateContractOffers(Campaign campaign) {
- generateContractOffers(campaign, false);
- }
-
+ @Override
public void generateContractOffers(Campaign campaign, boolean newCampaign) {
- if (((method == ContractMarketMethod.ATB_MONTHLY) && (campaign.getLocalDate().getDayOfMonth() == 1))
- || newCampaign) {
+ if (((campaign.getLocalDate().getDayOfMonth() == 1)) || newCampaign) {
// need to copy to prevent concurrent modification errors
new ArrayList<>(contracts).forEach(this::removeContract);
@@ -187,8 +91,7 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) {
boolean inBackwater = true;
if (currentFactions.size() > 1) {
- // More than one faction, if any is *not* periphery, we're not in backwater
- // either
+ // More than one faction, if any is *not* periphery, we're not in backwater either
for (Faction f : currentFactions) {
if (!f.isPeriphery()) {
inBackwater = false;
@@ -198,8 +101,7 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) {
// Just one faction. Are there any others nearby?
Faction onlyFaction = currentFactions.iterator().next();
if (!onlyFaction.isPeriphery()) {
- for (PlanetarySystem key : Systems.getInstance().getNearbySystems(campaign.getCurrentSystem(),
- 30)) {
+ for (PlanetarySystem key : Systems.getInstance().getNearbySystems(campaign.getCurrentSystem(), 30)) {
for (Faction f : key.getFactionSet(campaign.getLocalDate())) {
if (!onlyFaction.equals(f)) {
inBackwater = false;
@@ -227,11 +129,8 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) {
if (campaign.getAtBConfig().isHiringHall(campaign.getCurrentSystem().getId(),
campaign.getLocalDate())) {
numContracts++;
- /*
- * Though the rules do not state these modifiers are mutually exclusive, the
- * fact that the
- * distance of Galatea from a border means that it has no advantage for Mercs
- * over border
+ /* Though the rules do not state these modifiers are mutually exclusive, the fact that the
+ * distance of Galatea from a border means that it has no advantage for Mercs over border
* worlds. Common sense dictates that worlds with hiring halls should not be
* subject to the -1 for backwater/interior.
*/
@@ -240,16 +139,12 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) {
}
}
} else {
- /*
- * Per IOps Beta, government units determine number of contracts as on a system
- * with a great hall
- */
+ /* Per IOps Beta, government units determine number of contracts as on a system with a great hall */
numContracts++;
}
/*
- * If located on a faction's capital (interpreted as the starting planet for
- * that faction),
+ * If located on a faction's capital (interpreted as the starting planet for that faction),
* generate one contract offer for that faction.
*/
for (Faction f : campaign.getCurrentSystem().getFactionSet(campaign.getLocalDate())) {
@@ -277,9 +172,7 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) {
contracts.add(c);
}
}
- if (campaign.getCampaignOptions().isContractMarketReportRefresh()) {
- campaign.addReport("Contract market updated");
- }
+ updateReport(campaign);
}
}
@@ -305,8 +198,7 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u
/*
* If no suitable planet can be found or no jump path to the planet can be
- * calculated after
- * the indicated number of retries, this will return null.
+ * calculated after the indicated number of retries, this will return null.
*/
private @Nullable AtBContract generateAtBContract(Campaign campaign, int unitRatingMod) {
if (campaign.getFaction().isMercenary()) {
@@ -332,8 +224,7 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u
return generateAtBContract(campaign, employer, unitRatingMod, MAXIMUM_GENERATION_RETRIES);
}
- private @Nullable AtBContract generateAtBContract(Campaign campaign, @Nullable String employer, int unitRatingMod,
- int retries) {
+ private @Nullable AtBContract generateAtBContract(Campaign campaign, @Nullable String employer, int unitRatingMod, int retries) {
if (employer == null) {
logger.warn("Could not generate an AtB Contract because there was no employer!");
return null;
@@ -362,31 +253,19 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u
}
}
contract.setEmployerCode(employer, campaign.getGameYear());
- contract.setContractType(findAtBMissionType(unitRatingMod,
+ contract.setContractType(findMissionType(unitRatingMod,
Factions.getInstance().getFaction(contract.getEmployerCode()).isISMajorOrSuperPower()));
- if (contract.getContractType().isPirateHunting()) {
- contract.setEnemyCode("PIR");
- } else if (contract.getContractType().isRiotDuty()) {
- contract.setEnemyCode("REB");
- } else {
- contract.setEnemyCode(RandomFactionGenerator.getInstance().getEnemy(contract.getEmployerCode(),
- contract.getContractType().isGarrisonType()));
- }
-
- if (contract.getContractType().isGarrisonDuty() && contract.getEnemy().isRebel()) {
- contract.setContractType(AtBContractType.RIOT_DUTY);
- }
+ setEnemyCode(contract);
+ setIsRiotDuty(contract);
/*
* Addition to AtB rules: factions which are generally neutral
* (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(Factions.getInstance().getFaction(employer)) &&
- !RandomFactionGenerator.getInstance().getFactionHints().isAtWarWith(
- Factions.getInstance().getFaction(employer),
+ 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.getContractType().isPlanetaryAssault()) {
contract.setContractType(AtBContractType.GARRISON_DUTY);
@@ -394,21 +273,10 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u
contract.setContractType(AtBContractType.SECURITY_DUTY);
}
}
-
- // FIXME : Windchild : I don't work properly
- boolean isAttacker = !contract.getContractType().isGarrisonType()
- || (contract.getContractType().isReliefDuty() && (Compute.d6() < 4))
- || contract.getEnemy().isRebel();
- if (isAttacker) {
- contract.setSystemId(RandomFactionGenerator.getInstance().getMissionTarget(contract.getEmployerCode(),
- contract.getEnemyCode()));
- } else {
- contract.setSystemId(RandomFactionGenerator.getInstance().getMissionTarget(contract.getEnemyCode(),
- contract.getEmployerCode()));
- }
- if (contract.getSystem() == null) {
- logger.warn("Could not find contract location for "
- + contract.getEmployerCode() + " vs. " + contract.getEnemyCode());
+ setAttacker(contract);
+ try {
+ setSystemId(contract);
+ } catch (NoContractLocationFoundException ex) {
return generateAtBContract(campaign, employer, unitRatingMod, retries - 1);
}
JumpPath jp = null;
@@ -424,8 +292,8 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u
return generateAtBContract(campaign, employer, unitRatingMod, retries - 1);
}
- setAllyRating(contract, isAttacker, campaign.getGameYear());
- setEnemyRating(contract, isAttacker, campaign.getGameYear());
+ setAllyRating(contract, campaign.getGameYear());
+ setEnemyRating(contract, campaign.getGameYear());
if (contract.getContractType().isCadreDuty()) {
contract.setAllySkill(SkillLevel.GREEN);
@@ -433,9 +301,10 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u
}
contract.calculateLength(campaign.getCampaignOptions().isVariableContractLength());
- setAtBContractClauses(contract, unitRatingMod, campaign);
+ setContractClauses(contract, unitRatingMod, campaign);
- contract.calculatePaymentMultiplier(campaign);
+ contract.setRequiredLances(calculateRequiredLances(campaign, contract));
+ contract.setMultiplier(calculatePaymentMultiplier(campaign, contract));
contract.setPartsAvailabilityLevel(contract.getContractType().calculatePartsAvailabilityLevel());
@@ -444,9 +313,8 @@ private void checkForSubcontracts(Campaign campaign, AtBContract contract, int u
contract.setName(String.format("%s - %s - %s %s",
contract.getStartDate().format(DateTimeFormatter.ofPattern("yyyy")
- .withLocale(MekHQ.getMHQOptions().getDateLocale())),
- employer,
- contract.getSystem().getName(contract.getStartDate()), contract.getContractType()));
+ .withLocale(MekHQ.getMHQOptions().getDateLocale())), employer,
+ contract.getSystem().getName(contract.getStartDate()), contract.getContractType()));
return contract;
}
@@ -455,7 +323,7 @@ protected AtBContract generateAtBSubcontract(Campaign campaign,
AtBContract parent, int unitRatingMod) {
AtBContract contract = new AtBContract("New Subcontract");
contract.setEmployerCode(parent.getEmployerCode(), campaign.getGameYear());
- contract.setContractType(findAtBMissionType(unitRatingMod,
+ contract.setContractType(findMissionType(unitRatingMod,
Factions.getInstance().getFaction(contract.getEmployerCode()).isISMajorOrSuperPower()));
if (contract.getContractType().isPirateHunting()) {
@@ -484,8 +352,7 @@ protected AtBContract generateAtBSubcontract(Campaign campaign,
*/
// TODO : When MekHQ gets the capability of splitting the unit to different
- // locations, this
- // TODO : restriction can be lessened or lifted.
+ // locations, this restriction can be lessened or lifted.
if (!contract.getEnemy().isRebelOrPirate()) {
boolean factionValid = false;
for (PlanetarySystem p : Systems.getInstance().getNearbySystems(campaign.getCurrentSystem(), 30)) {
@@ -505,13 +372,10 @@ protected AtBContract generateAtBSubcontract(Campaign campaign,
}
}
- // FIXME : Windchild : I don't work properly
- boolean isAttacker = !contract.getContractType().isGarrisonType()
- || (contract.getContractType().isReliefDuty() && (Compute.d6() < 4))
- || contract.getEnemy().isRebel();
+ setAttacker(contract);
contract.setSystemId(parent.getSystemId());
- setAllyRating(contract, isAttacker, campaign.getGameYear());
- setEnemyRating(contract, isAttacker, campaign.getGameYear());
+ setAllyRating(contract, campaign.getGameYear());
+ setEnemyRating(contract, campaign.getGameYear());
if (contract.getContractType().isCadreDuty()) {
contract.setAllySkill(SkillLevel.GREEN);
@@ -533,19 +397,20 @@ protected AtBContract generateAtBSubcontract(Campaign campaign,
}
contract.setTransportComp(100);
- contract.calculatePaymentMultiplier(campaign);
+ contract.setRequiredLances(calculateRequiredLances(campaign, contract));
+ contract.setMultiplier(calculatePaymentMultiplier(campaign, contract));
contract.setPartsAvailabilityLevel(contract.getContractType().calculatePartsAvailabilityLevel());
contract.calculateContract(campaign);
contract.setName(String.format("%s - %s - %s Subcontract %s",
contract.getStartDate().format(DateTimeFormatter.ofPattern("yyyy")
- .withLocale(MekHQ.getMHQOptions().getDateLocale())),
- contract.getEmployer(),
+ .withLocale(MekHQ.getMHQOptions().getDateLocale())), contract.getEmployer(),
contract.getSystem().getName(parent.getStartDate()), contract.getContractType()));
return contract;
}
+ @Override
public void addFollowup(Campaign campaign,
AtBContract contract) {
if (followupContracts.containsValue(contract.getId())) {
@@ -573,9 +438,10 @@ public void addFollowup(Campaign campaign,
followup.setEnemySkill(contract.getEnemySkill());
followup.setEnemyQuality(contract.getEnemyQuality());
followup.calculateLength(campaign.getCampaignOptions().isVariableContractLength());
- setAtBContractClauses(followup, campaign.getAtBUnitRatingMod(), campaign);
+ setContractClauses(followup, campaign.getAtBUnitRatingMod(), campaign);
- followup.calculatePaymentMultiplier(campaign);
+ contract.setRequiredLances(calculateRequiredLances(campaign, contract));
+ contract.setMultiplier(calculatePaymentMultiplier(campaign, contract));
followup.setPartsAvailabilityLevel(followup.getContractType().calculatePartsAvailabilityLevel());
@@ -589,112 +455,51 @@ public void addFollowup(Campaign campaign,
followupContracts.put(followup.getId(), contract.getId());
}
- protected AtBContractType findAtBMissionType(int unitRatingMod, boolean majorPower) {
- final AtBContractType[][] table = {
- // col 0: IS Houses
- { AtBContractType.GUERRILLA_WARFARE, AtBContractType.RECON_RAID, AtBContractType.PIRATE_HUNTING,
- AtBContractType.PLANETARY_ASSAULT, AtBContractType.OBJECTIVE_RAID,
- AtBContractType.OBJECTIVE_RAID,
- AtBContractType.EXTRACTION_RAID, AtBContractType.RECON_RAID, AtBContractType.GARRISON_DUTY,
- AtBContractType.CADRE_DUTY, AtBContractType.RELIEF_DUTY },
- // col 1: Others
- { AtBContractType.GUERRILLA_WARFARE, AtBContractType.RECON_RAID, AtBContractType.PLANETARY_ASSAULT,
- AtBContractType.OBJECTIVE_RAID, AtBContractType.EXTRACTION_RAID, AtBContractType.PIRATE_HUNTING,
- AtBContractType.SECURITY_DUTY, AtBContractType.OBJECTIVE_RAID, AtBContractType.GARRISON_DUTY,
- AtBContractType.CADRE_DUTY, AtBContractType.DIVERSIONARY_RAID }
- };
- int roll = MathUtility.clamp(Compute.d6(2) + unitRatingMod - IUnitRating.DRAGOON_C, 2, 12);
- return table[majorPower ? 0 : 1][roll - 2];
- }
-
- public void setAllyRating(AtBContract contract, boolean isAttacker, int year) {
- int mod = 0;
- if (contract.getEnemy().isRebelOrPirate()) {
- mod -= 1;
+ @Override
+ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract) {
+ int unitRatingMod = campaign.getAtBUnitRatingMod();
+ double multiplier = 1.0;
+ // IntOps reputation factor then Dragoons rating
+ if (campaign.getCampaignOptions().getUnitRatingMethod().isCampaignOperations()) {
+ multiplier *= campaign.getReputationFactor();
+ } else {
+ if (unitRatingMod >= IUnitRating.DRAGOON_A) {
+ multiplier *= 2.0;
+ } else if (unitRatingMod == IUnitRating.DRAGOON_B) {
+ multiplier *= 1.5;
+ } else if (unitRatingMod == IUnitRating.DRAGOON_D) {
+ multiplier *= 0.8;
+ } else if (unitRatingMod == IUnitRating.DRAGOON_F) {
+ multiplier *= 0.5;
+ }
}
- if (contract.getContractType().isGuerrillaWarfare() || contract.getContractType().isCadreDuty()) {
- mod -= 3;
- } else if (contract.getContractType().isGarrisonDuty() || contract.getContractType().isSecurityDuty()) {
- mod -= 2;
- }
+ multiplier *= contract.getContractType().getPaymentMultiplier();
- if (AtBContract.isMinorPower(contract.getEmployerCode())) {
- mod -= 1;
+ final Faction employer = Factions.getInstance().getFaction(contract.getEmployerCode());
+ final Faction enemy = contract.getEnemy();
+ if (employer.isISMajorOrSuperPower() || employer.isClan()) {
+ multiplier *= 1.2;
+ } else if (enemy.isIndependent()) {
+ multiplier *= 1.0;
+ } else {
+ multiplier *= 1.1;
}
- if (contract.getEnemy().isIndependent()) {
- mod -= 2;
+ if (enemy.isRebelOrPirate()) {
+ multiplier *= 1.1;
}
- if (contract.getContractType().isPlanetaryAssault()) {
- mod += 1;
+ int requiredLances = calculateRequiredLances(campaign, contract);
+ int maxDeployedLances = calculateMaxDeployedLances(campaign);
+ if (requiredLances > maxDeployedLances && campaign.getCampaignOptions().isAdjustPaymentForStrategy()) {
+ multiplier *= (double) maxDeployedLances / (double) requiredLances;
}
- 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 &&
- !Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) {
- mod -= 1;
- }
- contract.setAllyQuality(getQualityRating(Compute.d6(2) + mod));
+ return multiplier;
}
- public void setEnemyRating(AtBContract contract, boolean isAttacker, int year) {
- int mod = 0;
- if (contract.getEnemy().isRebelOrPirate()) {
- mod -= 2;
- }
- if (contract.getContractType().isGuerrillaWarfare()) {
- mod += 2;
- }
- if (contract.getContractType().isPlanetaryAssault()) {
- mod += 1;
- }
- if (AtBContract.isMinorPower(contract.getEmployerCode())) {
- mod -= 1;
- }
- if (Factions.getInstance().getFaction(contract.getEmployerCode()).isClan()) {
- mod += isAttacker ? 2 : 4;
- }
- contract.setEnemySkill(getSkillRating(Compute.d6(2) + mod));
- if (year > 2950 && year < 3039 &&
- !Factions.getInstance().getFaction(contract.getEnemyCode()).isClan()) {
- mod -= 1;
- }
- contract.setEnemyQuality(getQualityRating(Compute.d6(2) + mod));
- }
-
- protected SkillLevel getSkillRating(int roll) {
- if (roll <= 5) {
- return SkillLevel.GREEN;
- } else if (roll <= 9) {
- return SkillLevel.REGULAR;
- } else if (roll <= 11) {
- return SkillLevel.VETERAN;
- } else {
- return SkillLevel.ELITE;
- }
- }
-
- protected int getQualityRating(int roll) {
- if (roll <= 5) {
- return IUnitRating.DRAGOON_F;
- } else if (roll <= 8) {
- return IUnitRating.DRAGOON_D;
- } else if (roll <= 10) {
- return IUnitRating.DRAGOON_C;
- } else if (roll == 11) {
- return IUnitRating.DRAGOON_B;
- } else {
- return IUnitRating.DRAGOON_A;
- }
- }
-
- protected void setAtBContractClauses(AtBContract contract, int unitRatingMod, Campaign campaign) {
+ private void setContractClauses(AtBContract contract, int unitRatingMod, Campaign campaign) {
ClauseMods mods = new ClauseMods();
clauseMods.put(contract.getId(), mods);
@@ -706,18 +511,12 @@ protected void setAtBContractClauses(AtBContract contract, int unitRatingMod, Ca
* the highest admin skill, or higher negotiation if the admin
* skills are equal.
*/
- Person adminCommand = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_COMMAND, SkillType.S_ADMIN,
- SkillType.S_NEG);
- Person adminTransport = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_TRANSPORT, SkillType.S_ADMIN,
- SkillType.S_NEG);
- Person adminLogistics = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_LOGISTICS, SkillType.S_ADMIN,
- SkillType.S_NEG);
- int adminCommandExp = (adminCommand == null) ? SkillType.EXP_ULTRA_GREEN
- : adminCommand.getSkill(SkillType.S_ADMIN).getExperienceLevel();
- int adminTransportExp = (adminTransport == null) ? SkillType.EXP_ULTRA_GREEN
- : adminTransport.getSkill(SkillType.S_ADMIN).getExperienceLevel();
- int adminLogisticsExp = (adminLogistics == null) ? SkillType.EXP_ULTRA_GREEN
- : adminLogistics.getSkill(SkillType.S_ADMIN).getExperienceLevel();
+ Person adminCommand = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_COMMAND, SkillType.S_ADMIN, SkillType.S_NEG);
+ Person adminTransport = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_TRANSPORT, SkillType.S_ADMIN, SkillType.S_NEG);
+ Person adminLogistics = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_LOGISTICS, SkillType.S_ADMIN, SkillType.S_NEG);
+ int adminCommandExp = (adminCommand == null) ? SkillType.EXP_ULTRA_GREEN : adminCommand.getSkill(SkillType.S_ADMIN).getExperienceLevel();
+ int adminTransportExp = (adminTransport == null) ? SkillType.EXP_ULTRA_GREEN : adminTransport.getSkill(SkillType.S_ADMIN).getExperienceLevel();
+ int adminLogisticsExp = (adminLogistics == null) ? SkillType.EXP_ULTRA_GREEN : adminLogistics.getSkill(SkillType.S_ADMIN).getExperienceLevel();
/* Treat government units like merc units that have a retainer contract */
if ((!campaign.getFaction().isMercenary() && !campaign.getFaction().isPirate())
@@ -811,158 +610,4 @@ protected void setAtBContractClauses(AtBContract contract, int unitRatingMod, Ca
rollSupportClause(contract, mods.mods[CLAUSE_SUPPORT]);
rollTransportClause(contract, mods.mods[CLAUSE_TRANSPORT]);
}
-
- private void rollCommandClause(final Contract contract, final int modifier) {
- final int roll = Compute.d6(2) + modifier;
- if (roll < 3) {
- contract.setCommandRights(ContractCommandRights.INTEGRATED);
- } else if (roll < 8) {
- contract.setCommandRights(ContractCommandRights.HOUSE);
- } else if (roll < 12) {
- contract.setCommandRights(ContractCommandRights.LIAISON);
- } else {
- contract.setCommandRights(ContractCommandRights.INDEPENDENT);
- }
- }
-
- private void rollSalvageClause(AtBContract contract, int mod, int contractMaxSalvagePercentage) {
- contract.setSalvageExchange(false);
- int roll = Math.min(Compute.d6(2) + mod, 13);
- if (roll < 2) {
- contract.setSalvagePct(0);
- } else if (roll < 4) {
- contract.setSalvageExchange(true);
- int r;
- do {
- r = Compute.d6(2);
- } while (r < 4);
- contract.setSalvagePct(Math.min((r - 3) * 10, contractMaxSalvagePercentage));
- } else {
- contract.setSalvagePct(Math.min((roll - 3) * 10, contractMaxSalvagePercentage));
- }
- }
-
- private void rollSupportClause(AtBContract contract, int mod) {
- int roll = Compute.d6(2) + mod;
- contract.setStraightSupport(0);
- contract.setBattleLossComp(0);
- if (roll < 3) {
- contract.setStraightSupport(0);
- } else if (roll < 8) {
- contract.setStraightSupport((roll - 2) * 20);
- } else if (roll == 8) {
- contract.setBattleLossComp(10);
- } else {
- contract.setBattleLossComp(Math.min((roll - 8) * 20, 100));
- }
- }
-
- private void rollTransportClause(AtBContract contract, int mod) {
- int roll = Compute.d6(2) + mod;
- if (roll < 2) {
- contract.setTransportComp(0);
- } else if (roll < 6) {
- contract.setTransportComp((20 + (roll - 2) * 5));
- } else if (roll < 10) {
- contract.setTransportComp((45 + (roll - 6) * 5));
- } else {
- contract.setTransportComp(100);
- }
- }
-
- public void writeToXML(final PrintWriter pw, int indent) {
- MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "contractMarket");
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lastId", lastId);
- for (final Contract contract : contracts) {
- contract.writeToXML(pw, indent);
- }
-
- for (final Integer key : clauseMods.keySet()) {
- if (!contractIds.containsKey(key)) {
- continue;
- }
-
- MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "clauseMods", "id", key);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "mods", clauseMods.get(key).mods);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "rerollsUsed", clauseMods.get(key).rerollsUsed);
- MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "clauseMods");
- }
- MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "contractMarket");
- }
-
- public static ContractMarket generateInstanceFromXML(Node wn, Campaign c, Version version) {
- ContractMarket retVal = null;
-
- try {
- // Instantiate the correct child class, and call its parsing function.
- retVal = new ContractMarket();
-
- // Okay, now load Part-specific fields!
- NodeList nl = wn.getChildNodes();
-
- // Loop through the nodes and load our contract offers
- for (int x = 0; x < nl.getLength(); x++) {
- Node wn2 = nl.item(x);
-
- // If it's not an element node, we ignore it.
- if (wn2.getNodeType() != Node.ELEMENT_NODE) {
- continue;
- }
-
- if (wn2.getNodeName().equalsIgnoreCase("lastId")) {
- retVal.lastId = Integer.parseInt(wn2.getTextContent());
- } else if (wn2.getNodeName().equalsIgnoreCase("mission")) {
- Mission m = Mission.generateInstanceFromXML(wn2, c, version);
-
- if (m instanceof Contract) {
- retVal.contracts.add((Contract) m);
- retVal.contractIds.put(m.getId(), (Contract) m);
- }
- } else if (wn2.getNodeName().equalsIgnoreCase("clauseMods")) {
- int key = Integer.parseInt(wn2.getAttributes().getNamedItem("id").getTextContent());
- ClauseMods cm = new ClauseMods();
- NodeList nl2 = wn2.getChildNodes();
- for (int i = 0; i < nl2.getLength(); i++) {
- Node wn3 = nl2.item(i);
- if (wn3.getNodeName().equalsIgnoreCase("mods")) {
- String[] s = wn3.getTextContent().split(",");
- for (int j = 0; j < s.length; j++) {
- cm.mods[j] = Integer.parseInt(s[j]);
- }
- } else if (wn3.getNodeName().equalsIgnoreCase("rerollsUsed")) {
- String[] s = wn3.getTextContent().split(",");
- for (int j = 0; j < s.length; j++) {
- cm.rerollsUsed[j] = Integer.parseInt(s[j]);
- }
- }
- }
- retVal.clauseMods.put(key, cm);
- }
- }
-
- // Restore any parent contract references
- for (Contract contract : retVal.contracts) {
- if (contract instanceof AtBContract atbContract) {
- atbContract.restore(c);
- }
- }
- } catch (Exception ex) {
- // Errrr, apparently either the class name was invalid...
- // Or the listed name doesn't exist.
- // Doh!
- logger.error("", ex);
- }
-
- return retVal;
- }
-
- /*
- * Keep track of how many rerolls remain for each contract clause
- * based on the admin's negotiation skill. Also track bonuses, as
- * the random clause bonuses should be persistent.
- */
- public static class ClauseMods {
- public int[] rerollsUsed = { 0, 0, 0, 0 };
- public int[] mods = { 0, 0, 0, 0 };
- }
}
diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java
new file mode 100644
index 0000000000..7a543deb96
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java
@@ -0,0 +1,312 @@
+package mekhq.campaign.market.contractMarket;
+
+import megamek.common.Compute;
+import megamek.common.enums.SkillLevel;
+import megamek.logging.MMLogger;
+import mekhq.MekHQ;
+import mekhq.campaign.Campaign;
+import mekhq.campaign.againstTheBot.AtBConfiguration;
+import mekhq.campaign.market.enums.ContractMarketMethod;
+import mekhq.campaign.mission.AtBContract;
+import mekhq.campaign.mission.enums.AtBContractType;
+import mekhq.campaign.personnel.Person;
+import mekhq.campaign.personnel.SkillType;
+import mekhq.campaign.rating.IUnitRating;
+import mekhq.campaign.universe.Faction;
+import mekhq.campaign.universe.Faction.Tag;
+import mekhq.campaign.universe.Factions;
+import mekhq.campaign.universe.enums.HiringHallLevel;
+
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * Contract Market as described in Campaign Operations, 4th printing.
+ */
+public class CamOpsContractMarket extends AbstractContractMarket {
+ private static final MMLogger logger = MMLogger.create(CamOpsContractMarket.class);
+ private static int BASE_NEGOTIATION_TARGET = 8;
+ private static int EMPLOYER_NEGOTIATION_SKILL_LEVEL = 5;
+
+ private ContractModifiers contractMods = null;
+
+ public CamOpsContractMarket() {
+ super(ContractMarketMethod.CAM_OPS);
+ }
+
+ @Override
+ public AtBContract addAtBContract(Campaign campaign) {
+ Optional c = generateContract(campaign);
+ if (c.isPresent()) {
+ AtBContract atbContract = c.get();
+ contracts.add(atbContract);
+ return atbContract;
+ }
+ return null;
+ }
+
+ @Override
+ public void generateContractOffers(Campaign campaign, boolean newCampaign) {
+ if (!(campaign.getLocalDate().getDayOfMonth() == 1) && !newCampaign) {
+ return;
+ }
+ new ArrayList<>(contracts).forEach(this::removeContract);
+ // TODO: Allow subcontracts?
+ //for (AtBContract contract : campaign.getActiveAtBContracts()) {
+ //checkForSubcontracts(campaign, contract, unitRatingMod);
+ //}
+ // TODO: CamopsMarket: allow players to choose negotiators and send them out, removing them
+ // from other tasks they're doing. For now just use the highest negotiation skill on the force.
+ int ratingMod = getReputationModifier(campaign);
+ contractMods = generateContractModifiers(campaign);
+ int negotiationSkill = findNegotiationSkill(campaign);
+ int numOffers = getNumberOfOffers(
+ rollNegotiation(negotiationSkill, ratingMod + contractMods.offersMod) - BASE_NEGOTIATION_TARGET);
+
+ for (int i = 0; i < numOffers; i++) {
+ addAtBContract(campaign);
+ }
+ updateReport(campaign);
+ }
+
+ @Override
+ public void addFollowup(Campaign campaign, AtBContract contract) {
+ //TODO: add logic if we decide followup contracts should be allow in CamOps
+ }
+
+ @Override
+ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract) {
+ //TODO: add logic from camops 4th printing
+ return 1.0;
+ }
+
+ private HiringHallLevel getHiringHallLevel(Campaign campaign) {
+ AtBConfiguration atbConfig = campaign.getAtBConfig();
+ return atbConfig.getHiringHallLevel(campaign.getCurrentSystem()
+ .getPrimaryPlanet()
+ .getName(campaign.getLocalDate()),
+ campaign.getLocalDate());
+ }
+
+ private ContractModifiers generateContractModifiers(Campaign campaign) {
+ if (campaign.getFaction().isMercenary()) {
+ return new ContractModifiers(getHiringHallLevel(campaign));
+ } else if (campaign.getFaction().isRebelOrPirate()) {
+ return new ContractModifiers(HiringHallLevel.NONE);
+ } else {
+ return new ContractModifiers(HiringHallLevel.GREAT);
+ }
+ }
+
+ private int findNegotiationSkill(Campaign campaign) {
+ // TODO: have pirates use investigation skill instead when it is implemented per CamOps
+ Person negotiator = campaign.findBestAtSkill(SkillType.S_NEG);
+ if (negotiator == null) {
+ return 0;
+ }
+ return negotiator.getSkillLevel(SkillType.S_NEG);
+ }
+
+ private int rollNegotiation(int skill, int modifiers) {
+ return Compute.d6(2) + skill + modifiers;
+ }
+
+ private int rollOpposedNegotiation(int skill, int modifiers) {
+ return Compute.d6(2) + skill + modifiers - Compute.d6(2) + EMPLOYER_NEGOTIATION_SKILL_LEVEL;
+ }
+
+ private int getNumberOfOffers(int margin) {
+ if (margin < 1) {
+ return 0;
+ } else if (margin < 3) {
+ return 1;
+ } else if (margin < 6) {
+ return 2;
+ } else if (margin < 9) {
+ return 3;
+ } else if (margin < 11) {
+ return 4;
+ } else if (margin < 13) {
+ return 5;
+ } else {
+ return 6;
+ }
+ }
+
+ private Optional generateContract(Campaign campaign) {
+ AtBContract contract = new AtBContract("UnnamedContract");
+ lastId++;
+ contract.setId(lastId);
+ contractIds.put(lastId, contract);
+ Faction employer = determineEmployer(campaign);
+ contract.setEmployerCode(employer.getShortName(), campaign.getLocalDate());
+ if (employer.isMercenary()) {
+ contract.setMercSubcontract(true);
+ }
+ contract.setContractType(determineMission(campaign, employer));
+ setEnemyCode(contract);
+ setIsRiotDuty(contract);
+ setAttacker(contract);
+ try {
+ setSystemId(contract);
+ } catch (NoContractLocationFoundException ex) {
+ return Optional.empty();
+ }
+ setAllyRating(contract, campaign.getGameYear());
+ setEnemyRating(contract, campaign.getGameYear());
+ if (contract.getContractType().isCadreDuty()) {
+ contract.setAllySkill(SkillLevel.GREEN);
+ contract.setAllyQuality(IUnitRating.DRAGOON_F);
+ }
+ contract.calculateLength(campaign.getCampaignOptions().isVariableContractLength());
+ setContractClauses(contract, campaign);
+ contract.setRequiredLances(calculateRequiredLances(campaign, contract));
+ contract.setMultiplier(calculatePaymentMultiplier(campaign, contract));
+ contract.setPartsAvailabilityLevel(contract.getContractType().calculatePartsAvailabilityLevel());
+ contract.initContractDetails(campaign);
+ contract.calculateContract(campaign);
+ contract.setName(String.format("%s - %s - %s %s",
+ contract.getStartDate().format(DateTimeFormatter.ofPattern("yyyy")
+ .withLocale(MekHQ.getMHQOptions().getDateLocale())), contract.getEmployer(),
+ contract.getSystem().getName(contract.getStartDate()), contract.getContractType()));
+
+ return Optional.of(contract);
+ }
+
+ private int getReputationModifier(Campaign campaign) {
+ return getReputationScore(campaign) / 10;
+ }
+
+ private int getReputationScore(Campaign campaign) {
+ return campaign.getReputation().getReputationRating();
+ }
+
+ private Faction determineEmployer(Campaign campaign) {
+ Collection employerTags;
+ int roll = Compute.d6(2) + getReputationModifier(campaign) + contractMods.employersMod;
+ if (roll < 6) {
+ // Roll again on the independent employers column
+ roll = Compute.d6(2) + getReputationModifier(campaign) + contractMods.employersMod;
+ employerTags = getEmployerTags(campaign, roll, true);
+ } else {
+ employerTags = getEmployerTags(campaign, roll, false);
+ }
+ return getRandomEmployer(campaign, employerTags);
+ }
+
+ private Faction getRandomEmployer(Campaign campaign, Collection employerTags) {
+ Collection factions = Factions.getInstance().getActiveFactions(campaign.getLocalDate());
+ List filtered = new ArrayList<>();
+ for (Faction faction : factions) {
+ // Clans only hire units within their own clan
+ if (faction.isClan() && !faction.equals(campaign.getFaction())) {
+ continue;
+ }
+ for (Tag employerTag : employerTags) {
+ if (!faction.is(employerTag)) {
+ // The SMALL tag has to be converted to independent for now, since for some reason
+ // independent is coded as a string.
+ if (employerTag == Tag.SMALL && faction.isIndependent()) {
+ continue;
+ }
+ break;
+ }
+ filtered.add(faction);
+ }
+ }
+ Random rand = new Random();
+ return filtered.get(rand.nextInt(filtered.size()));
+ }
+
+ private Collection getEmployerTags(Campaign campaign, int roll, boolean independent) {
+ Collection tags = new ArrayList<>();
+ if (independent) {
+ tags.add(Tag.SMALL);
+ if (roll < 4) {
+ tags.add(Tag.NOBLE);
+ } else if (roll < 6) {
+ tags.add(Tag.PLANETARY_GOVERNMENT);
+ } else if (roll == 6) {
+ tags.add(Tag.MERC);
+ } else if (roll < 9) {
+ tags.add(Tag.PERIPHERY);
+ tags.add(Tag.MAJOR);
+ } else if (roll < 11) {
+ tags.add(Tag.PERIPHERY);
+ tags.add(Tag.MINOR);
+ } else {
+ tags.add(Tag.CORPORATION);
+ }
+ } else {
+ if (roll < 6) {
+ tags.add(Tag.SMALL);
+ } else if (roll < 8) {
+ tags.add(Tag.MINOR);
+ } else if (roll < 11) {
+ tags.add(Tag.MAJOR);
+ } else {
+ if (Factions.getInstance()
+ .getActiveFactions(campaign.getLocalDate())
+ .stream()
+ .anyMatch(Faction::isSuperPower)) {
+ tags.add(Tag.SUPER);
+ } else {
+ tags.add(Tag.MAJOR);
+ }
+ }
+ }
+ return tags;
+ }
+
+ private AtBContractType determineMission(Campaign campaign, Faction employer) {
+ int roll = Compute.d6(2);
+ if (campaign.getFaction().isPirate()) {
+ if (roll < 6) {
+ return AtBContractType.RECON_RAID;
+ } else {
+ return AtBContractType.OBJECTIVE_RAID;
+ }
+ }
+ return findMissionType(getReputationModifier(campaign), employer.isISMajorOrSuperPower());
+ }
+
+ private void setContractClauses(AtBContract contract, Campaign campaign) {
+ // TODO: add logic to determine initial contract clauses from CamOps 4th printing.
+ }
+
+ private class ContractModifiers {
+ protected int offersMod;
+ protected int employersMod;
+ protected int missionsMod;
+
+ protected ContractModifiers(HiringHallLevel level) {
+ switch (level) {
+ case NONE -> {
+ offersMod = -3;
+ employersMod = -2;
+ missionsMod = -2;
+ }
+ case QUESTIONABLE -> {
+ offersMod = 0;
+ employersMod = -2;
+ missionsMod = -2;
+ }
+ case MINOR -> {
+ offersMod = 1;
+ employersMod = 0;
+ missionsMod = 0;
+ }
+ case STANDARD -> {
+ offersMod = 2;
+ employersMod = 1;
+ missionsMod = 1;
+ }
+ case GREAT -> {
+ offersMod = 3;
+ employersMod = 2;
+ missionsMod = 2;
+ }
+ }
+ }
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/DisabledContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/DisabledContractMarket.java
new file mode 100644
index 0000000000..3a5f5117bd
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/market/contractMarket/DisabledContractMarket.java
@@ -0,0 +1,31 @@
+package mekhq.campaign.market.contractMarket;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.market.enums.ContractMarketMethod;
+import mekhq.campaign.mission.AtBContract;
+
+public class DisabledContractMarket extends AbstractContractMarket {
+ public DisabledContractMarket() {
+ super(ContractMarketMethod.NONE);
+ }
+
+ @Override
+ public AtBContract addAtBContract(Campaign campaign) {
+ return null;
+ }
+
+ @Override
+ public void generateContractOffers(Campaign campaign, boolean newCampaign) {
+
+ }
+
+ @Override
+ public void addFollowup(Campaign campaign, AtBContract contract) {
+
+ }
+
+ @Override
+ public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract) {
+ return 1.0;
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/market/enums/ContractMarketMethod.java b/MekHQ/src/mekhq/campaign/market/enums/ContractMarketMethod.java
index 03d931c330..b8ae284b81 100644
--- a/MekHQ/src/mekhq/campaign/market/enums/ContractMarketMethod.java
+++ b/MekHQ/src/mekhq/campaign/market/enums/ContractMarketMethod.java
@@ -19,13 +19,18 @@
package mekhq.campaign.market.enums;
import mekhq.MekHQ;
+import mekhq.campaign.market.contractMarket.AbstractContractMarket;
+import mekhq.campaign.market.contractMarket.AtbMonthlyContractMarket;
+import mekhq.campaign.market.contractMarket.CamOpsContractMarket;
+import mekhq.campaign.market.contractMarket.DisabledContractMarket;
import java.util.ResourceBundle;
public enum ContractMarketMethod {
//region Enum Declarations
NONE("ContractMarketMethod.NONE.text", "ContractMarketMethod.NONE.toolTipText"),
- ATB_MONTHLY("ContractMarketMethod.ATB_MONTHLY.text", "ContractMarketMethod.ATB_MONTHLY.toolTipText");
+ ATB_MONTHLY("ContractMarketMethod.ATB_MONTHLY.text", "ContractMarketMethod.ATB_MONTHLY.toolTipText"),
+ CAM_OPS("ContractMarketMethod.CAM_OPS.text", "ContractMarketMethod.CAM_OPS.toolTipText");
//endregion Enum Declarations
//region Variable Declarations
@@ -56,20 +61,19 @@ public boolean isNone() {
public boolean isAtBMonthly() {
return this == ATB_MONTHLY;
}
+
+ public boolean isCamOps() {
+ return this == CAM_OPS;
+ }
//endregion Boolean Comparison Methods
- // TODO : AbstractContractMarket : Uncomment
-/*
public AbstractContractMarket getContractMarket() {
- switch (this) {
- case ATB_MONTHLY:
- return new AtBMonthlyContractMarket();
- case NONE:
- default:
- return new EmptyContractMarket();
- }
+ return switch (this) {
+ case ATB_MONTHLY -> new AtbMonthlyContractMarket();
+ case CAM_OPS -> new CamOpsContractMarket();
+ case NONE -> new DisabledContractMarket();
+ };
}
-*/
@Override
public String toString() {
diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java
index c9d8aa5810..e798589b76 100644
--- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java
+++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java
@@ -144,6 +144,7 @@ public class AtBContract extends Contract {
protected int nextWeekBattleTypeMod;
private StratconCampaignState stratconCampaignState;
+ private boolean isAttacker;
private static final ResourceBundle resources = ResourceBundle.getBundle(
"mekhq.resources.AtBContract",
@@ -161,6 +162,7 @@ public AtBContract(String name) {
parentContract = null;
mercSubcontract = false;
+ isAttacker = false;
setContractType(AtBContractType.GARRISON_DUTY);
setAllySkill(SkillLevel.REGULAR);
@@ -243,62 +245,6 @@ public static boolean isMinorPower(final String factionCode) {
return !faction.isMajorOrSuperPower() && !faction.isClan();
}
- public void calculatePaymentMultiplier(Campaign campaign) {
- int unitRatingMod = campaign.getAtBUnitRatingMod();
- double multiplier = 1.0;
- // IntOps reputation factor then Dragoons rating
- if (campaign.getCampaignOptions().getUnitRatingMethod().isCampaignOperations()) {
- multiplier *= (unitRatingMod * 0.2) + 0.5;
- } else {
- if (unitRatingMod >= IUnitRating.DRAGOON_A) {
- multiplier *= 2.0;
- } else if (unitRatingMod == IUnitRating.DRAGOON_B) {
- multiplier *= 1.5;
- } else if (unitRatingMod == IUnitRating.DRAGOON_D) {
- multiplier *= 0.8;
- } else if (unitRatingMod == IUnitRating.DRAGOON_F) {
- multiplier *= 0.5;
- }
- }
-
- multiplier *= getContractType().getPaymentMultiplier();
-
- final Faction employer = Factions.getInstance().getFaction(employerCode);
- final Faction enemy = getEnemy();
- if (employer.isISMajorOrSuperPower() || employer.isClan()) {
- multiplier *= 1.2;
- } else if (enemy.isIndependent()) {
- multiplier *= 1.0;
- } else {
- multiplier *= 1.1;
- }
-
- if (enemy.isRebelOrPirate()) {
- multiplier *= 1.1;
- }
-
- int cmdrStrategy = 0;
- if (campaign.getFlaggedCommander() != null &&
- campaign.getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) {
- cmdrStrategy = campaign.getFlaggedCommander().getSkill(SkillType.S_STRATEGY).getLevel();
- }
- int maxDeployedLances = campaign.getCampaignOptions().getBaseStrategyDeployment() +
- campaign.getCampaignOptions().getAdditionalStrategyDeployment() *
- cmdrStrategy;
-
- if (isSubcontract()) {
- requiredLances = 1;
- } else {
- requiredLances = Math.max(getEffectiveNumUnits(campaign) / 6, 1);
- if (requiredLances > maxDeployedLances && campaign.getCampaignOptions().isAdjustPaymentForStrategy()) {
- multiplier *= (double) maxDeployedLances / (double) requiredLances;
- requiredLances = maxDeployedLances;
- }
- }
-
- setMultiplier(multiplier);
- }
-
/**
* Checks the morale level of the campaign based on various factors.
*
@@ -613,6 +559,14 @@ public void setMercSubcontract(boolean sub) {
mercSubcontract = sub;
}
+ public boolean isAttacker() {
+ return isAttacker;
+ }
+
+ public void setAttacker(boolean isAttacker) {
+ this.isAttacker = isAttacker;
+ }
+
public void checkEvents(Campaign c) {
if (c.getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) {
nextWeekBattleTypeMod = 0;
diff --git a/MekHQ/src/mekhq/campaign/mission/AtBScenario.java b/MekHQ/src/mekhq/campaign/mission/AtBScenario.java
index f893fb639b..438fefed78 100644
--- a/MekHQ/src/mekhq/campaign/mission/AtBScenario.java
+++ b/MekHQ/src/mekhq/campaign/mission/AtBScenario.java
@@ -1962,10 +1962,16 @@ public void setLance(Lance l) {
lanceForceId = l.getForceId();
}
+ /**
+ * @return Boolean indicating whether the player is the attacker in this contract.
+ */
public boolean isAttacker() {
return attacker;
}
+ /**
+ * @param attacker Boolean indicating whether the player is the attacker in this contract.
+ */
public void setAttacker(boolean attacker) {
this.attacker = attacker;
}
diff --git a/MekHQ/src/mekhq/campaign/universe/Faction.java b/MekHQ/src/mekhq/campaign/universe/Faction.java
index 122ac4fa12..8a3b64a044 100644
--- a/MekHQ/src/mekhq/campaign/universe/Faction.java
+++ b/MekHQ/src/mekhq/campaign/universe/Faction.java
@@ -307,6 +307,22 @@ public boolean isMinorPower() {
public boolean isSmall() {
return is(Tag.SMALL);
}
+
+ public boolean isNoble() {
+ return is(Tag.NOBLE);
+ }
+
+ public boolean isPlanetaryGovt() {
+ return is(Tag.PLANETARY_GOVERNMENT);
+ }
+
+ public boolean isCorporation() {
+ return is(Tag.CORPORATION);
+ }
+
+ public boolean isInactive() {
+ return is(Tag.INACTIVE);
+ }
// endregion Power Checks
// endregion Checks
@@ -451,6 +467,12 @@ public enum Tag {
/** Faction code is not intended to be for players */
SPECIAL,
/** Faction is meant to be played */
- PLAYABLE
+ PLAYABLE,
+ /** Faction is an independent noble (Camops p. 39) */
+ NOBLE,
+ /** Faction is an independent planetary government (Camops p. 39) */
+ PLANETARY_GOVERNMENT,
+ /** Faction is an independent corporation (Camops p. 39) */
+ CORPORATION
}
}
diff --git a/MekHQ/src/mekhq/campaign/universe/Factions.java b/MekHQ/src/mekhq/campaign/universe/Factions.java
index af3d837cbf..4057eafd17 100644
--- a/MekHQ/src/mekhq/campaign/universe/Factions.java
+++ b/MekHQ/src/mekhq/campaign/universe/Factions.java
@@ -20,6 +20,7 @@
import java.io.FileInputStream;
import java.io.IOException;
+import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -31,6 +32,7 @@
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
+import mekhq.campaign.Campaign;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -97,6 +99,17 @@ public Collection getFactions() {
return factions.values();
}
+ /**
+ * Returns a list of all factions active in a specific year.
+ * @param date
+ * @return
+ */
+ public Collection getActiveFactions(LocalDate date) {
+ return getFactions().stream().filter(f ->
+ f.validIn(date) && !f.isInactive())
+ .collect(Collectors.toList());
+ }
+
public Collection getFactionList() {
return new ArrayList<>(factions.keySet());
}
diff --git a/MekHQ/src/mekhq/campaign/universe/HiringHall.java b/MekHQ/src/mekhq/campaign/universe/HiringHall.java
new file mode 100644
index 0000000000..c5a22850c3
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/universe/HiringHall.java
@@ -0,0 +1,40 @@
+package mekhq.campaign.universe;
+
+import mekhq.campaign.universe.enums.HiringHallLevel;
+
+import java.time.LocalDate;
+
+/**
+ * Class representing a Hiring Hall on a world on specific dates. This data is statically defined in
+ * data/universe/atbconfig.xml.
+ */
+public class HiringHall {
+ private HiringHallLevel level;
+ private LocalDate startDate;
+ private LocalDate endDate;
+ private String planetName;
+
+ public HiringHall(HiringHallLevel level, LocalDate startDate, LocalDate endDate, String planetName) {
+ this.level = level;
+ this.startDate = startDate;
+ this.endDate = endDate;
+ this.planetName = planetName;
+ }
+
+ public boolean isActive(LocalDate date) {
+ return ((startDate == null) || !startDate.isAfter(date))
+ && ((endDate == null) || !endDate.isBefore(date));
+ }
+
+ public String getPlanetName() {
+ return planetName;
+ }
+
+ public HiringHallLevel getLevel() {
+ return level;
+ }
+
+ public void setLevel(HiringHallLevel level) {
+ this.level = level;
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/universe/enums/HiringHallLevel.java b/MekHQ/src/mekhq/campaign/universe/enums/HiringHallLevel.java
new file mode 100644
index 0000000000..5b8ad5704f
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/universe/enums/HiringHallLevel.java
@@ -0,0 +1,13 @@
+package mekhq.campaign.universe.enums;
+
+/**
+ * The level of a Hiring Hall as defined in CamOps (4th printing). Used to determine various modifiers
+ * related to contract generation.
+ */
+public enum HiringHallLevel {
+ NONE,
+ QUESTIONABLE,
+ MINOR,
+ STANDARD,
+ GREAT
+}
diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java
index a7d9f3e0c6..29439f17ec 100644
--- a/MekHQ/src/mekhq/gui/CampaignGUI.java
+++ b/MekHQ/src/mekhq/gui/CampaignGUI.java
@@ -44,6 +44,7 @@
import mekhq.campaign.finances.financialInstitutions.FinancialInstitutions;
import mekhq.campaign.force.Force;
import mekhq.campaign.icons.StandardForceIcon;
+import mekhq.campaign.market.contractMarket.AbstractContractMarket;
import mekhq.campaign.market.unitMarket.AbstractUnitMarket;
import mekhq.campaign.mission.Scenario;
import mekhq.campaign.parts.Part;
@@ -1549,6 +1550,11 @@ private void menuOptionsActionPerformed(final ActionEvent evt) {
getCampaign().getUnitMarket().setOffers(unitMarket.getOffers());
miUnitMarket.setVisible(!getCampaign().getUnitMarket().getMethod().isNone());
}
+
+ AbstractContractMarket contractMarket = getCampaign().getContractMarket();
+ if (contractMarket.getMethod() != newOptions.getContractMarketMethod()) {
+ getCampaign().setContractMarket(newOptions.getContractMarketMethod().getContractMarket());
+ }
if (atb != newOptions.isUseAtB()) {
if (newOptions.isUseAtB()) {
diff --git a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java
index 600f999361..016a9c1e0e 100644
--- a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java
@@ -46,7 +46,7 @@
import mekhq.campaign.Campaign;
import mekhq.campaign.JumpPath;
import mekhq.campaign.finances.enums.TransactionType;
-import mekhq.campaign.market.ContractMarket;
+import mekhq.campaign.market.contractMarket.AbstractContractMarket;
import mekhq.campaign.mission.AtBContract;
import mekhq.campaign.mission.Contract;
import mekhq.campaign.universe.Factions;
@@ -72,7 +72,7 @@ public class ContractMarketDialog extends JDialog {
private static int sharePct = 20;
private Campaign campaign;
- private ContractMarket contractMarket;
+ private AbstractContractMarket contractMarket;
private Contract selectedContract = null;
private List possibleRetainerContracts;
@@ -231,7 +231,7 @@ private void initComponents() {
// Changes in rating or force size since creation can alter some details
if (c instanceof AtBContract atbContract) {
atbContract.initContractDetails(campaign);
- atbContract.calculatePaymentMultiplier(campaign);
+ campaign.getContractMarket().calculatePaymentMultiplier(campaign, atbContract);
atbContract.setPartsAvailabilityLevel(atbContract.getContractType().calculatePartsAvailabilityLevel());
atbContract.setAtBSharesPercent(campaign.getCampaignOptions().isUseShareSystem()
? (Integer) spnSharePct.getValue()
diff --git a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java
index 18be0838b0..090f7a0702 100644
--- a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java
@@ -51,6 +51,7 @@
import mekhq.campaign.event.OptionsChangedEvent;
import mekhq.campaign.finances.CurrencyManager;
import mekhq.campaign.finances.financialInstitutions.FinancialInstitutions;
+import mekhq.campaign.market.enums.ContractMarketMethod;
import mekhq.campaign.mod.am.InjuryTypes;
import mekhq.campaign.personnel.Bloodname;
import mekhq.campaign.personnel.SkillType;
@@ -229,7 +230,7 @@ public Task(JDialog dialog) {
* 5 : Units
* 6 : New Campaign / Campaign Loading
* 7 : Campaign Application
- *
+ *
* @return The loaded campaign
* @throws Exception if anything goes wrong
*/
@@ -346,9 +347,11 @@ public Campaign doInBackground() throws Exception {
// Setup Markets
campaign.getPersonnelMarket().generatePersonnelForDay(campaign);
- // TODO : AbstractContractMarket : Uncomment
- // campaign.getContractMarket().generateContractOffers(campaign, (preset ==
- // null) ? 2 : preset.getContractCount());
+ ContractMarketMethod contractMarketMethod = campaign.getCampaignOptions().getContractMarketMethod();
+ campaign.setContractMarket(contractMarketMethod.getContractMarket());
+ if (!contractMarketMethod.isNone()) {
+ campaign.getContractMarket().generateContractOffers(campaign, true);
+ }
if (!campaign.getCampaignOptions().getUnitMarketMethod().isNone()) {
campaign.setUnitMarket(campaign.getCampaignOptions().getUnitMarketMethod().getUnitMarket());
campaign.getUnitMarket().generateUnitOffers(campaign);
diff --git a/MekHQ/src/mekhq/gui/dialog/NewAtBContractDialog.java b/MekHQ/src/mekhq/gui/dialog/NewAtBContractDialog.java
index c04aae7273..98e8cfaa87 100644
--- a/MekHQ/src/mekhq/gui/dialog/NewAtBContractDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/NewAtBContractDialog.java
@@ -501,8 +501,9 @@ private void updatePlanets() {
protected void updatePaymentMultiplier() {
if (((AtBContract) contract).getEmployerCode() != null &&
((AtBContract) contract).getEnemyCode() != null) {
- ((AtBContract) contract).calculatePaymentMultiplier(campaign);
- spnMultiplier.setValue(contract.getMultiplier());
+ double multiplier = campaign.getContractMarket().calculatePaymentMultiplier(campaign, (AtBContract) contract);
+ contract.setMultiplier(multiplier);
+ spnMultiplier.setValue(multiplier);
}
}
diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
index 4897a0eea2..1923b59cf3 100644
--- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
+++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
@@ -2947,14 +2947,13 @@ private JScrollPane createAgainstTheBotTab() {
}
// TODO : AbstractContractMarket : Delink more from AtB
- if (contractMarketPanel.isEnabled() != enabled) {
- comboContractMarketMethod.setSelectedItem(enabled
- ? ContractMarketMethod.ATB_MONTHLY
- : ContractMarketMethod.NONE);
- contractMarketPanel.setEnabled(enabled);
- comboContractMarketMethod.setEnabled(false); // TODO : AbstractContractMarket : Remove
- // line
- }
+// if (contractMarketPanel.isEnabled() != enabled) {
+// comboContractMarketMethod.setSelectedItem(enabled
+// ? ContractMarketMethod.ATB_MONTHLY
+// : ContractMarketMethod.NONE);
+// contractMarketPanel.setEnabled(enabled);
+// comboContractMarketMethod.setEnabled(true);
+// }
});
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
@@ -8114,7 +8113,7 @@ public Component getListCellRendererComponent(final JList> list, final Object
final boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof ContractMarketMethod) {
- list.setToolTipText(((ContractMarketMethod) value).getToolTipText());
+ list.setToolTipText(wordWrap(((ContractMarketMethod) value).getToolTipText()));
}
return this;
}
@@ -8131,7 +8130,7 @@ public Component getListCellRendererComponent(final JList> list, final Object
chkContractMarketReportRefresh.setEnabled(enabled);
spnContractMaxSalvagePercentage.setEnabled(enabled);
});
- comboContractMarketMethod.setEnabled(false); // TODO : AbstractContractMarket : Remove line
+ comboContractMarketMethod.setEnabled(true);
lblContractSearchRadius.setText(resources.getString("lblContractSearchRadius.text"));
lblContractSearchRadius.setToolTipText(resources.getString("lblContractSearchRadius.toolTipText"));
diff --git a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java
index a7d1b3617c..4c1fdc30df 100644
--- a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java
+++ b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java
@@ -24,7 +24,7 @@
import mekhq.MekHQ;
import mekhq.campaign.Campaign;
import mekhq.campaign.JumpPath;
-import mekhq.campaign.market.ContractMarket;
+import mekhq.campaign.market.contractMarket.AbstractContractMarket;
import mekhq.campaign.mission.AtBContract;
import mekhq.campaign.mission.Contract;
import mekhq.campaign.personnel.Person;
@@ -319,12 +319,12 @@ public void mouseClicked(MouseEvent e) {
}
if (contract instanceof AtBContract) {
campaign.getContractMarket().rerollClause((AtBContract) contract,
- ContractMarket.CLAUSE_COMMAND, campaign);
+ AbstractContractMarket.CLAUSE_COMMAND, campaign);
setCommandRerollButtonText((JButton) ev.getSource());
txtCommand.setText(contract.getCommandRights().toString());
txtCommand.setToolTipText(contract.getCommandRights().getToolTipText());
if (campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_COMMAND) >= cmdRerolls) {
+ AbstractContractMarket.CLAUSE_COMMAND) >= cmdRerolls) {
btn.setEnabled(false);
}
refreshAmounts();
@@ -367,11 +367,11 @@ public void mouseClicked(MouseEvent e) {
}
if (contract instanceof AtBContract) {
campaign.getContractMarket().rerollClause((AtBContract) contract,
- ContractMarket.CLAUSE_TRANSPORT, campaign);
+ AbstractContractMarket.CLAUSE_TRANSPORT, campaign);
setTransportRerollButtonText((JButton) ev.getSource());
txtTransport.setText(contract.getTransportComp() + "%");
if (campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_TRANSPORT) >= tranRerolls) {
+ AbstractContractMarket.CLAUSE_TRANSPORT) >= tranRerolls) {
btn.setEnabled(false);
}
refreshAmounts();
@@ -412,12 +412,12 @@ public void mouseClicked(MouseEvent e) {
}
if (contract instanceof AtBContract) {
campaign.getContractMarket().rerollClause((AtBContract) contract,
- ContractMarket.CLAUSE_SALVAGE, campaign);
+ AbstractContractMarket.CLAUSE_SALVAGE, campaign);
setSalvageRerollButtonText((JButton) ev.getSource());
txtSalvageRights.setText(contract.getSalvagePct() + "%"
+ (contract.isSalvageExchange() ? " (Exchange)" : ""));
if (campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_SALVAGE) >= logRerolls) {
+ AbstractContractMarket.CLAUSE_SALVAGE) >= logRerolls) {
btn.setEnabled(false);
}
refreshAmounts();
@@ -460,12 +460,12 @@ public void mouseClicked(MouseEvent e) {
}
if (contract instanceof AtBContract) {
campaign.getContractMarket().rerollClause((AtBContract) contract,
- ContractMarket.CLAUSE_SUPPORT, campaign);
+ AbstractContractMarket.CLAUSE_SUPPORT, campaign);
setSupportRerollButtonText((JButton) ev.getSource());
txtStraightSupport.setText(contract.getStraightSupport() + "%");
txtBattleLossComp.setText(contract.getBattleLossComp() + "%");
if (campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_SUPPORT) >= logRerolls) {
+ AbstractContractMarket.CLAUSE_SUPPORT) >= logRerolls) {
btn.setEnabled(false);
}
refreshAmounts();
@@ -504,7 +504,7 @@ public void mouseClicked(MouseEvent e) {
private boolean hasTransportRerolls() {
return allowRerolls && (campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_TRANSPORT) < tranRerolls);
+ AbstractContractMarket.CLAUSE_TRANSPORT) < tranRerolls);
}
private boolean hasCommandRerolls() {
@@ -513,40 +513,40 @@ private boolean hasCommandRerolls() {
&& (campaign.getFaction().isMercenary()
|| campaign.getFaction().isPirate())
&& (campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_COMMAND) < cmdRerolls);
+ AbstractContractMarket.CLAUSE_COMMAND) < cmdRerolls);
}
private boolean hasSalvageRerolls() {
return allowRerolls && (campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_SALVAGE) < logRerolls);
+ AbstractContractMarket.CLAUSE_SALVAGE) < logRerolls);
}
private boolean hasSupportRerolls() {
return allowRerolls && (campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_SUPPORT) < logRerolls);
+ AbstractContractMarket.CLAUSE_SUPPORT) < logRerolls);
}
private void setCommandRerollButtonText(JButton rerollButton) {
int rerolls = (cmdRerolls - campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_COMMAND));
+ AbstractContractMarket.CLAUSE_COMMAND));
rerollButton.setText(generateRerollText(rerolls));
}
private void setTransportRerollButtonText(JButton rerollButton) {
int rerolls = (tranRerolls - campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_TRANSPORT));
+ AbstractContractMarket.CLAUSE_TRANSPORT));
rerollButton.setText(generateRerollText(rerolls));
}
private void setSalvageRerollButtonText(JButton rerollButton) {
int rerolls = (logRerolls - campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_SALVAGE));
+ AbstractContractMarket.CLAUSE_SALVAGE));
rerollButton.setText(generateRerollText(rerolls));
}
private void setSupportRerollButtonText(JButton rerollButton) {
int rerolls = (logRerolls - campaign.getContractMarket().getRerollsUsed(contract,
- ContractMarket.CLAUSE_SUPPORT));
+ AbstractContractMarket.CLAUSE_SUPPORT));
rerollButton.setText(generateRerollText(rerolls));
}
diff --git a/MekHQ/unittests/mekhq/campaign/market/ContractMarketAtBGenerationTests.java b/MekHQ/unittests/mekhq/campaign/market/ContractMarketAtBGenerationTests.java
index 48a010f4a5..4c2583889b 100644
--- a/MekHQ/unittests/mekhq/campaign/market/ContractMarketAtBGenerationTests.java
+++ b/MekHQ/unittests/mekhq/campaign/market/ContractMarketAtBGenerationTests.java
@@ -22,6 +22,7 @@
import mekhq.campaign.finances.Accountant;
import mekhq.campaign.finances.Money;
import mekhq.campaign.force.Force;
+import mekhq.campaign.market.contractMarket.AtbMonthlyContractMarket;
import mekhq.campaign.mission.AtBContract;
import mekhq.campaign.rating.IUnitRating;
import mekhq.campaign.rating.UnitRatingMethod;
@@ -150,7 +151,7 @@ public void addMercWithoutRetainerAtBContractSucceeds(final int gameYear, final
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- assertNotNull(new ContractMarket().addAtBContract(campaign));
+ assertNotNull(new AtbMonthlyContractMarket().addAtBContract(campaign));
}
@ParameterizedTest
@@ -252,7 +253,7 @@ public void addMercWithoutRetainerMinorPowerAtBContractSucceeds(final int gameYe
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -357,7 +358,7 @@ public void addMercWithoutRetainerEmployerNeutralAtBContractSucceeds(final int g
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -463,7 +464,7 @@ public void addMercWithoutRetainerEmployerNeutralAtWarAtBContractSucceeds(final
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -572,7 +573,7 @@ public void mercEmployerRetries(final int gameYear, final int unitRating,
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -608,7 +609,7 @@ public void mercEmployerRetriesFail(final int gameYear, final int unitRating,
// Return "MERC" every time
when(rfg.getEmployer()).thenReturn("MERC");
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNull(contract);
@@ -713,7 +714,7 @@ public void mercMissiongTargetRetries(final int gameYear, final int unitRating,
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -810,7 +811,7 @@ public void mercMissionTargetRetriesFail(final int gameYear, final int unitRatin
doReturn(false).when(hints).isNeutral(eq(enemyFaction));
when(rfg.getFactionHints()).thenReturn(hints);
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNull(contract);
@@ -915,7 +916,7 @@ public void mercJumpPathRetries(final int gameYear, final int unitRating,
doReturn(null).doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -1014,7 +1015,7 @@ public void mercJumpPathFails(final int gameYear, final int unitRating,
// Fail to find a jump path
doReturn(null).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNull(contract);
@@ -1118,7 +1119,7 @@ public void addMercWithRetainerAtBContractSucceeds(final int gameYear, final int
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -1223,7 +1224,7 @@ public void addMercWithRetainerMinorPowerAtBContractSucceeds(final int gameYear,
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -1328,7 +1329,7 @@ public void addMercWithRetainerEmployerNeutralAtBContractSucceeds(final int game
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -1433,7 +1434,7 @@ public void addMercWithRetainerEmployerNeutralAtWarAtBContractSucceeds(final int
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -1538,7 +1539,7 @@ public void nonMercAtBContractSucceeds(final int gameYear, final int unitRating,
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -1643,7 +1644,7 @@ public void nonMercMinorPowerAtBContractSucceeds(final int gameYear, final int u
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -1748,7 +1749,7 @@ public void nonMercNeutralAtBContractSucceeds(final int gameYear, final int unit
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
@@ -1853,7 +1854,7 @@ public void nonMercNeutralAtWarAtBContractSucceeds(final int gameYear, final int
doReturn(jumpPath).when(campaign).calculateJumpPath(eq(currentSystem), eq(targetSystem));
doReturn(Money.of(1)).when(campaign).calculateCostPerJump(anyBoolean(), anyBoolean());
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
AtBContract contract = market.addAtBContract(campaign);
assertNotNull(contract);
diff --git a/MekHQ/unittests/mekhq/campaign/market/ContractMarketIntegrationTest.java b/MekHQ/unittests/mekhq/campaign/market/ContractMarketIntegrationTest.java
index 0a91899731..58f934e72c 100644
--- a/MekHQ/unittests/mekhq/campaign/market/ContractMarketIntegrationTest.java
+++ b/MekHQ/unittests/mekhq/campaign/market/ContractMarketIntegrationTest.java
@@ -22,6 +22,7 @@
import mekhq.campaign.Campaign;
import mekhq.campaign.CampaignOptions;
import mekhq.campaign.force.Force;
+import mekhq.campaign.market.contractMarket.AtbMonthlyContractMarket;
import mekhq.campaign.mission.AtBContract;
import mekhq.campaign.mission.Contract;
import mekhq.campaign.mission.enums.AtBContractType;
@@ -84,7 +85,7 @@ public void beforeEach() {
@Test
public void addAtBContractMercsTest() {
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
// Simulate clicking GM Add on the contract market three times
for (int ii = 0; ii < REASONABLE_GENERATION_ATTEMPTS; ii++) {
@@ -96,7 +97,7 @@ public void addAtBContractMercsTest() {
@Test
public void generateContractOffersMercsTest() {
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
// Simulate three months of contract generation ...
boolean foundContract = false;
@@ -114,7 +115,7 @@ public void generateContractOffersMercsTest() {
public void addAtBContractMercRetainerTest() {
campaign.setRetainerEmployerCode("LA");
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
// Simulate clicking GM Add on the contract market three times
for (int ii = 0; ii < 3; ii++) {
@@ -128,7 +129,7 @@ public void addAtBContractMercRetainerTest() {
public void generateContractOffersMercRetainerTest() {
campaign.setRetainerEmployerCode("CS");
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
// Simulate three months of contract generation ...
boolean foundContract = false;
@@ -158,7 +159,7 @@ public void generateContractOffersMercSubcontractTest() {
when(existing.getCommandRights()).thenReturn(ContractCommandRights.INDEPENDENT);
campaign.importMission(existing);
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
SecureRandom realRng = new SecureRandom();
MMRandom rng = mock(MMRandom.class);
@@ -205,7 +206,7 @@ public void generateContractOffersMercSubcontractTest() {
public void addAtBContractHouseTest() {
campaign.setFactionCode("DC");
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
// Simulate clicking GM Add on the contract market three times
for (int ii = 0; ii < 3; ii++) {
@@ -219,7 +220,7 @@ public void addAtBContractHouseTest() {
public void generateContractOffersHouseTest() {
campaign.setFactionCode("FS");
- ContractMarket market = new ContractMarket();
+ AtbMonthlyContractMarket market = new AtbMonthlyContractMarket();
// Simulate three months of contract generation ...
boolean foundContract = false;