Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Added Campaign Option to Randomize New Unit Quality #4158

Merged
merged 7 commits into from
Jun 8, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ useQualityMaintenance.text=Use quality modifiers in maintenance checks
useQualityMaintenance.toolTipText=If checked, quality modifiers will be added to maintenance checks (WARNING: this will lead to unstable quality ratings over time)
reverseQualityNames.text=Reverse quality names
reverseQualityNames.toolTipText=<html>If checked, quality name reporting will be reversed so that A is the best and F is the worst. <b>Warning:</b> toggling this option will reset the Finances tab.
chkUseRandomUnitQualities.text=Random New Unit Quality
chkUseRandomUnitQualities.toolTipText=If checked, the quality of new units is randomized when they are added to the campaign. Otherwise, quality will always default to D.
useUnofficialMaintenance.text=Only damage parts that are already at worst quality (Unofficial)
useUnofficialMaintenance.toolTipText=<html>When this option is checked and you are using quality maintenance, <br>the margin of failure rolls for damaging parts only happen for parts that are already rated at the lowest level.</html>
logMaintenance.text=Log maintenance rolls in log file
Expand Down
9 changes: 8 additions & 1 deletion MekHQ/src/mekhq/campaign/Campaign.java
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,14 @@ public void purchaseShipSearchResult() {
: calculatePartTransitTime(Compute.d6(2) - 2);

getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), cost, "Purchased " + en.getShortName());
addNewUnit(en, true, transitDays);
int quality = 3;

if (campaignOptions.isUseRandomUnitQualities()) {
quality = Unit.getRandomUnitQuality(0);
}

addNewUnit(en, true, transitDays, quality);

if (!getCampaignOptions().isInstantUnitMarketDelivery()) {
addReport("<font color='" + MekHQ.getMHQOptions().getFontColorPositiveHexColor() + "'>Unit will be delivered in " + transitDays + " days.</font>");
}
Expand Down
13 changes: 13 additions & 0 deletions MekHQ/src/mekhq/campaign/CampaignOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public static String getTransitUnitName(final int unit) {
private int maintenanceBonus;
private boolean useQualityMaintenance;
private boolean reverseQualityNames;
private boolean useRandomUnitQualities;
private boolean useUnofficialMaintenance;
private boolean logMaintenance;

Expand Down Expand Up @@ -617,6 +618,7 @@ public CampaignOptions() {
maintenanceBonus = -1;
useQualityMaintenance = true;
reverseQualityNames = false;
setUseRandomUnitQualities(true);
useUnofficialMaintenance = false;
logMaintenance = false;

Expand Down Expand Up @@ -1276,6 +1278,14 @@ public void setReverseQualityNames(final boolean reverseQualityNames) {
this.reverseQualityNames = reverseQualityNames;
}

public boolean isUseRandomUnitQualities() {
return useRandomUnitQualities;
}

public void setUseRandomUnitQualities(final boolean useRandomUnitQualities) {
this.useRandomUnitQualities = useRandomUnitQualities;
}

public boolean isUseUnofficialMaintenance() {
return useUnofficialMaintenance;
}
Expand Down Expand Up @@ -4552,6 +4562,7 @@ public void writeToXml(final PrintWriter pw, int indent) {
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "maintenanceBonus", maintenanceBonus);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useQualityMaintenance", useQualityMaintenance);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "reverseQualityNames", reverseQualityNames);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomUnitQualities", isUseRandomUnitQualities());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useUnofficialMaintenance", isUseUnofficialMaintenance());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "checkMaintenance", checkMaintenance);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "maxAcquisitions", maxAcquisitions);
Expand Down Expand Up @@ -4995,6 +5006,8 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve
retVal.useQualityMaintenance = Boolean.parseBoolean(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("reverseQualityNames")) {
retVal.reverseQualityNames = Boolean.parseBoolean(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("useRandomUnitQualities")) {
retVal.setUseRandomUnitQualities(Boolean.parseBoolean(wn2.getTextContent()));
} else if (wn2.getNodeName().equalsIgnoreCase("useUnofficialMaintenance")
|| wn2.getNodeName().equalsIgnoreCase("useUnofficalMaintenance")) { // Legacy, 0.49.12 Removal
retVal.setUseUnofficialMaintenance(Boolean.parseBoolean(wn2.getTextContent()));
Expand Down
13 changes: 11 additions & 2 deletions MekHQ/src/mekhq/campaign/Quartermaster.java
Original file line number Diff line number Diff line change
Expand Up @@ -466,17 +466,26 @@ public void arrivePart(Part part) {
public boolean buyUnit(Entity en, int days) {
Objects.requireNonNull(en);

int quality = 3;

if (campaign.getCampaignOptions().isUseRandomUnitQualities()) {
quality = Unit.getRandomUnitQuality(0);
}

if (getCampaignOptions().isPayForUnits()) {
Money cost = new Unit(en, getCampaign()).getBuyCost();
if (getCampaign().getFinances().debit(TransactionType.UNIT_PURCHASE, getCampaign().getLocalDate(),
cost, "Purchased " + en.getShortName())) {
getCampaign().addNewUnit(en, false, days, 3);

getCampaign().addNewUnit(en, false, days, quality);

return true;
} else {
return false;
}
} else {
getCampaign().addNewUnit(en, false, days, 3);

getCampaign().addNewUnit(en, false, days, quality);
return true;
}
}
Expand Down
18 changes: 15 additions & 3 deletions MekHQ/src/mekhq/campaign/mission/AtBContract.java
Original file line number Diff line number Diff line change
Expand Up @@ -510,23 +510,35 @@ public void doBonusRoll(Campaign c) {
rat = "CivilianUnits_PrimMech";
c.addReport("Bonus: civilian Mek");
break;
default:
throw new IllegalStateException("Unexpected value in mekhq/campaign/mission/AtBContract.java/doBonusRoll: " + roll);
}

if (null != rat) {
Entity en = null;
RandomUnitGenerator.getInstance().setChosenRAT(rat);
ArrayList<MechSummary> msl = RandomUnitGenerator.getInstance().generate(1);

int quality = 3;

if (c.getCampaignOptions().isUseRandomUnitQualities()) {
quality = Unit.getRandomUnitQuality(0);
}

if (!msl.isEmpty() && (msl.get(0) != null)) {
try {
en = new MechFileParser(msl.get(0).getSourceFile(), msl.get(0).getEntryName()).getEntity();
} catch (EntityLoadingException ex) {
LogManager.getLogger().error("Unable to load entity: " + msl.get(0).getSourceFile()
+ ": " + msl.get(0).getEntryName() + ": " + ex.getMessage(), ex);
LogManager.getLogger().error("Unable to load entity: {}: {}: {}",
msl.get(0).getSourceFile(),
msl.get(0).getEntryName(),
ex.getMessage(),
ex);
}
}

if (null != en) {
c.addNewUnit(en, false, 0, 3);
c.addNewUnit(en, false, 0, quality);
} else {
c.addReport("<html><font color='" + MekHQ.getMHQOptions().getFontColorNegativeHexColor() + "'>Could not load unit</font></html>");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import megamek.common.MechSummaryCache;
import mekhq.campaign.Campaign;
import mekhq.campaign.storyarc.StoryTrigger;
import mekhq.campaign.unit.Unit;
import mekhq.utilities.MHQXMLUtility;
import org.apache.logging.log4j.LogManager;
import org.w3c.dom.Node;
Expand All @@ -46,19 +47,27 @@ public class AddUnitStoryTrigger extends StoryTrigger {
protected void execute() {
MechSummary ms = MechSummaryCache.getInstance().getMech(entityName);
if (ms == null) {
LogManager.getLogger().error("Cannot find entry for " + entityName);
LogManager.getLogger().error("Cannot find entry for {}", entityName);
return;
}

MechFileParser mechFileParser;
try {
mechFileParser = new MechFileParser(ms.getSourceFile(), ms.getEntryName());
} catch (Exception ex) {
LogManager.getLogger().error("Unable to load unit: " + ms.getEntryName(), ex);
LogManager.getLogger().error("Unable to load unit: {}", ms.getEntryName(), ex);
return;
}

Entity en = mechFileParser.getEntity();
getCampaign().addNewUnit(en, false, 0, 3);

int quality = 3;

if (getCampaign().getCampaignOptions().isUseRandomUnitQualities()) {
quality = Unit.getRandomUnitQuality(0);
}

getCampaign().addNewUnit(en, false, 0, quality);
}

@Override
Expand Down
44 changes: 41 additions & 3 deletions MekHQ/src/mekhq/campaign/unit/Unit.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import megamek.Version;
import megamek.client.ui.swing.tileset.EntityImage;
import megamek.codeUtilities.MathUtility;
import megamek.common.*;
import megamek.common.InfantryBay.PlatoonType;
import megamek.common.annotations.Nullable;
Expand Down Expand Up @@ -5335,7 +5336,7 @@ public Money getFuelCost() {
fuelCost = fuelCost.plus(getTonsBurnDay(entity));// * 3 * 15000;
} else if (entity instanceof ConvFighter) {
fuelCost = fuelCost.plus(getFighterFuelCost(entity));
} else if (entity instanceof megamek.common.Aero) {
} else if (entity instanceof Aero) {
fuelCost = fuelCost.plus(((Aero) entity).getFuelTonnage() * 4.0 * 15000.0);
} else if ((entity instanceof Tank) || (entity instanceof Mech)) {
fuelCost = fuelCost.plus(getVehicleFuelCost(entity));
Expand Down Expand Up @@ -5375,7 +5376,7 @@ public double getTonsBurnDay(Entity e) {
return (tonsburnday * 15 * 15000);
} else if ((e instanceof SmallCraft)) {
return (1.84 * 15 * 15000);
} else if (e instanceof megamek.common.Jumpship) {
} else if (e instanceof Jumpship) {
if (e.getWeight() < 50000) {
tonsburnday = 2.82;
} else if (e.getWeight() < 100000) {
Expand All @@ -5385,7 +5386,7 @@ public double getTonsBurnDay(Entity e) {
} else {
tonsburnday = 39.52;
}
if (e instanceof megamek.common.Warship) {
if (e instanceof Warship) {
return (tonsburnday * 15 * 15000);
}
return (tonsburnday * 3 * 15000);
Expand Down Expand Up @@ -5708,4 +5709,41 @@ public void fixReferences(Campaign campaign) {
transportedUnits = newTransportedUnits;
}
}

/**
* Generates a random unit quality based on a 2d6 roll and a modifier.
* This table is based on the AtB Mek Quality table, but is still useful outside of that ruleset
*
* @param modifier the modifier to be applied to the 2d6 roll
* @return an integer representing the generated unit quality
* @throws IllegalStateException if an unexpected value is encountered during the switch statement
*/
public static int getRandomUnitQuality(int modifier) {
int roll = MathUtility.clamp(
(Compute.d6(2) + modifier),
2,
12
);

switch (roll) {
case 2:
case 3:
case 4:
case 5:
return Part.QUALITY_A;
case 6:
case 7:
case 8:
return Part.QUALITY_B;
case 9:
case 10:
return Part.QUALITY_C;
case 11:
return Part.QUALITY_D;
case 12:
return Part.QUALITY_F;
default:
throw new IllegalStateException("Unexpected value in mekhq/campaign/unit/Unit.java/getRandomUnitQuality: " + roll);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -942,16 +942,34 @@ private void generateEntity(final Campaign campaign,
private List<Unit> createUnits(final Campaign campaign,
final List<CompanyGenerationPersonTracker> trackers) {
final List<Unit> units = new ArrayList<>();

for (final CompanyGenerationPersonTracker tracker : trackers) {
if (tracker.getEntity() == null) {
continue;
}

final Unit unit = campaign.addNewUnit(tracker.getEntity(), false, 0, 3);
int quality = 3;

if (campaign.getCampaignOptions().isUseRandomUnitQualities()) {
int modifier = 0;

if (tracker.getPerson().isCommander()) {
modifier = 2;
} else if (tracker.getPerson().getRank().isOfficer()) {
modifier = 1;
}

quality = Unit.getRandomUnitQuality(modifier);
}

final Unit unit = campaign.addNewUnit(tracker.getEntity(), false, 0, quality);

unit.addPilotOrSoldier(tracker.getPerson());

if (getOptions().isGenerateUnitsAsAttached()) {
tracker.getPerson().setOriginalUnit(unit);
}

units.add(unit);
}
return units;
Expand Down Expand Up @@ -1286,8 +1304,16 @@ public List<Entity> generateMothballedEntities(final Campaign campaign,
*/
private List<Unit> createMothballedSpareUnits(final Campaign campaign,
final List<Entity> mothballedEntities) {
int quality;

if (campaign.getCampaignOptions().isUseRandomUnitQualities()) {
quality = Unit.getRandomUnitQuality(0);
} else {
quality = 3;
}

final List<Unit> mothballedUnits = mothballedEntities.stream()
.map(entity -> campaign.addNewUnit(entity, false, 0, 3))
.map(entity -> campaign.addNewUnit(entity, false, 0, quality))
.collect(Collectors.toList());
mothballedUnits.forEach(Unit::completeMothball);
return mothballedUnits;
Expand Down
9 changes: 8 additions & 1 deletion MekHQ/src/mekhq/gui/CampaignGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -1893,10 +1893,17 @@ private File checkFileEnding(File file, String format) {

protected void loadListFile(final boolean allowNewPilots) {
final File unitFile = FileDialogs.openUnits(getFrame()).orElse(null);

int quality = 3;

if (getCampaign().getCampaignOptions().isUseRandomUnitQualities()) {
quality = Unit.getRandomUnitQuality(0);
}

if (unitFile != null) {
try {
for (Entity entity : new MULParser(unitFile, getCampaign().getGameOptions()).getEntities()) {
getCampaign().addNewUnit(entity, allowNewPilots, 0, 3);
getCampaign().addNewUnit(entity, allowNewPilots, 0, quality);
}
} catch (Exception e) {
LogManager.getLogger().error("", e);
Expand Down
11 changes: 10 additions & 1 deletion MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import mekhq.MekHQ;
import mekhq.campaign.event.ProcurementEvent;
import mekhq.campaign.parts.Part;
import mekhq.campaign.unit.Unit;
import mekhq.campaign.work.IAcquisitionWork;
import mekhq.gui.CampaignGUI;
import mekhq.gui.model.ProcurementTableModel;
Expand Down Expand Up @@ -198,7 +199,15 @@ private void addOneItem(final IAcquisitionWork acquisition) {
if (equipment instanceof Part) {
gui.getCampaign().getQuartermaster().addPart((Part) equipment, 0);
} else if (equipment instanceof Entity) {
gui.getCampaign().addNewUnit((Entity) equipment, false, 0, 3);
int quality;

if (gui.getCampaign().getCampaignOptions().isUseRandomUnitQualities()) {
quality = Unit.getRandomUnitQuality(0);
} else {
quality = 3;
}

gui.getCampaign().addNewUnit((Entity) equipment, false, 0, quality);
} else {
LogManager.getLogger().error("Attempted to add unknown equipment of {}", acquisition.getAcquisitionName());
return;
Expand Down
12 changes: 11 additions & 1 deletion MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -1373,7 +1373,17 @@ private void addRATRolledUnit() {
}

if (getLastRolledUnit() != null) {
final Unit unit = getGUI().getCampaign().addNewUnit(getLastRolledUnit(), false, 0, 3);
int quality;

if (getGUI().getCampaign().getCampaignOptions().isUseRandomUnitQualities()) {
quality = Unit.getRandomUnitQuality(0);
} else {
quality = 3;
}

final Unit unit = getGUI().getCampaign().addNewUnit(getLastRolledUnit(), false, 0, quality);


if ((getPerson() != null) && (getPerson().getUnit() == null)) {
unit.addPilotOrSoldier(getPerson());
getPerson().setOriginalUnit(unit);
Expand Down
8 changes: 7 additions & 1 deletion MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,13 @@ protected JPanel createButtonsPanel() {
protected void select(boolean isGM) {
if (getSelectedEntity() != null) {
if (isGM) {
campaign.addNewUnit(selectedUnit.getEntity(), false, 0, 3);
int quality = 3;

if (campaign.getCampaignOptions().isUseRandomUnitQualities()) {
quality = UnitOrder.getRandomUnitQuality(0);
}

campaign.addNewUnit(selectedUnit.getEntity(), false, 0, quality);
} else {
campaign.getShoppingList().addShoppingItem(selectedUnit, 1, campaign);
}
Expand Down
Loading
Loading