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

Fix: Tank armor and invalid builds #6343

Merged
merged 3 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 41 additions & 35 deletions megamek/src/megamek/common/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,7 @@
*/
package megamek.common;

import java.awt.Image;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import megamek.MMConstants;
import megamek.client.bot.princess.FireControl;
import megamek.client.ui.Base64Image;
Expand All @@ -32,27 +23,11 @@
import megamek.client.ui.swing.calculationReport.DummyCalculationReport;
import megamek.codeUtilities.StringUtility;
import megamek.common.MovePath.MoveStepType;
import megamek.common.actions.AbstractAttackAction;
import megamek.common.actions.ChargeAttackAction;
import megamek.common.actions.DfaAttackAction;
import megamek.common.actions.DisplacementAttackAction;
import megamek.common.actions.EntityAction;
import megamek.common.actions.PushAttackAction;
import megamek.common.actions.TeleMissileAttackAction;
import megamek.common.actions.WeaponAttackAction;
import megamek.common.actions.*;
import megamek.common.annotations.Nullable;
import megamek.common.battlevalue.BVCalculator;
import megamek.common.enums.AimingMode;
import megamek.common.enums.BasementType;
import megamek.common.enums.BuildingType;
import megamek.common.enums.GamePhase;
import megamek.common.enums.MPBoosters;
import megamek.common.enums.WeaponSortOrder;
import megamek.common.equipment.AmmoMounted;
import megamek.common.equipment.ArmorType;
import megamek.common.equipment.BombMounted;
import megamek.common.equipment.MiscMounted;
import megamek.common.equipment.WeaponMounted;
import megamek.common.enums.*;
import megamek.common.equipment.*;
import megamek.common.event.GameEntityChangeEvent;
import megamek.common.force.Force;
import megamek.common.hexarea.HexArea;
Expand All @@ -64,13 +39,7 @@
import megamek.common.planetaryconditions.Wind;
import megamek.common.preference.PreferenceManager;
import megamek.common.util.DiscordFormat;
import megamek.common.weapons.AlamoMissileWeapon;
import megamek.common.weapons.AltitudeBombAttack;
import megamek.common.weapons.CapitalMissileBearingsOnlyHandler;
import megamek.common.weapons.DiveBombAttack;
import megamek.common.weapons.SpaceBombAttack;
import megamek.common.weapons.Weapon;
import megamek.common.weapons.WeaponHandler;
import megamek.common.weapons.*;
import megamek.common.weapons.bayweapons.AR10BayWeapon;
import megamek.common.weapons.bayweapons.BayWeapon;
import megamek.common.weapons.bayweapons.CapitalMissileBayWeapon;
Expand All @@ -80,6 +49,15 @@
import megamek.logging.MMLogger;
import megamek.utilities.xml.MMXMLUtility;

import java.awt.*;
import java.math.BigInteger;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
* Entity is a master class for basically anything on the board except terrain.
*/
Expand All @@ -91,6 +69,16 @@ public abstract class Entity extends TurnOrdered implements Transporter, Targeta

private static final long serialVersionUID = 1430806396279853295L;

public enum InvalidSourceBuildReason {
UNIT_OLDER_THAN_EQUIPMENT_INTRO_YEAR,
NOT_ENOUGH_SLOT_COUNT,
UNIT_OVERWEIGHT,
INVALID_OR_OUTDATED_BUILD,
INCOMPLETE_BUILD,
INVALID_ENGINE,
INVALID_CREW,
}

public static final int DOES_NOT_TRACK_HEAT = 999;
public static final int UNLIMITED_JUMP_DOWN = 999;

Expand Down Expand Up @@ -397,6 +385,7 @@ public abstract class Entity extends TurnOrdered implements Transporter, Targeta

private List<Integer> passedThroughFacing = new ArrayList<>();

private List<InvalidSourceBuildReason> invalidSourceBuildReasons = new ArrayList<>();
/**
* Stores the player selected hex ground to air targeting.
* For ground to air, distance to target for the ground unit is determined
Expand Down Expand Up @@ -15840,4 +15829,21 @@ public void removeFleeZone() {
fleeZone = HexArea.EMPTY_AREA;
hasFleeZone = false;
}

public void setInvalidSourceBuildReasons(List<InvalidSourceBuildReason> invalidSourceBuildReasons) {
this.invalidSourceBuildReasons = invalidSourceBuildReasons;
}

public List<InvalidSourceBuildReason> getInvalidSourceBuildReasons() {
return invalidSourceBuildReasons;
}

public boolean canonUnitWithInvalidBuild() {
if (this.isCanon() && mulId > -1)
{
return !this.getInvalidSourceBuildReasons().isEmpty();
}
return false;
}

}
65 changes: 49 additions & 16 deletions megamek/src/megamek/common/loaders/BLKFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,6 @@
*/
package megamek.common.loaders;

import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Vector;
import java.util.stream.Collectors;

import megamek.common.*;
import megamek.common.InfantryBay.PlatoonType;
import megamek.common.equipment.AmmoMounted;
Expand All @@ -36,8 +26,14 @@
import megamek.common.weapons.InfantryAttack;
import megamek.common.weapons.bayweapons.BayWeapon;
import megamek.common.weapons.infantry.InfantryWeapon;
import megamek.logging.MMLogger;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

public class BLKFile {
private static final MMLogger logger = MMLogger.create(BLKFile.class);

BuildingBlock dataFile;

Expand Down Expand Up @@ -122,11 +118,30 @@ protected void setBasicEntityData(Entity entity) throws EntityLoadingException {
entity.setIcon(dataFile.getDataAsString("icon")[0]);
}

if (dataFile.exists("invalidSourceBuildReasons")) {
loadInvalidSourceBuildReasons(entity);
}

setTechLevel(entity);
setFluff(entity);
checkManualBV(entity);
}

protected void loadInvalidSourceBuildReasons(Entity entity) {
String[] reasons = dataFile.getDataAsString("invalidSourceBuildReasons");
var invalidSourceBuildReasons = new ArrayList<Entity.InvalidSourceBuildReason>();
if (reasons != null) {
for (var reason : reasons) {
try {
invalidSourceBuildReasons.add(Entity.InvalidSourceBuildReason.valueOf(reason));
} catch (Exception e) {
logger.warn("Unable to load reason {}", reason);
}
}
}
entity.setInvalidSourceBuildReasons(invalidSourceBuildReasons);
}

protected void loadQuirks(Entity entity) throws EntityLoadingException {
try {
List<QuirkEntry> quirks = new ArrayList<>();
Expand Down Expand Up @@ -291,21 +306,35 @@ protected void loadSVArmor(Entity sv) throws EntityLoadingException {
TechConstants.isClan(dataFile.getDataAsInt(sv.getLocationName(i) + "_armor_tech")[0]));
sv.setArmorType(armor.getArmorType(), i);
sv.setBARRating(armor.getBAR(), i);
sv.setArmorTechLevel(armor.getStaticTechLevel().getCompoundTechLevel(false), i);
sv.setArmorTechLevel(armor.getStaticTechLevel().getCompoundTechLevel(sv.isClan()), i);
}
} else {
if (dataFile.exists("barrating")) {
megamek.common.equipment.ArmorType armor = ArmorType.svArmor(dataFile.getDataAsInt("barrating")[0]);
sv.setArmorType(armor.getArmorType());
if (dataFile.exists("armor_type")) {
sv.setArmorType(dataFile.getDataAsInt("armor_type")[0]);
} else {
sv.setArmorType(armor.getArmorType());
}
if (dataFile.exists("armor_tech_level")) {
sv.setArmorTechLevel(dataFile.getDataAsInt("armor_tech_level")[0]);
} else {
sv.setArmorTechLevel(armor.getStaticTechLevel().getCompoundTechLevel(sv.isClan()));
}
sv.setBARRating(armor.getBAR());
sv.setArmorTechLevel(armor.getStaticTechLevel().getCompoundTechLevel(false));
} else {
if (dataFile.exists("armor_type")) {
sv.setArmorType(dataFile.getDataAsInt("armor_type")[0]);
} else {
throw new EntityLoadingException("could not find armor_type block.");
}
if (dataFile.exists("armor_tech_level")) {
sv.setArmorTechLevel(dataFile.getDataAsInt("armor_tech_level")[0]);
} else {
sv.setArmorTechLevel(sv.getStaticTechLevel().getCompoundTechLevel(sv.isClan()));
}
}

if (dataFile.exists("armor_tech")) {
sv.setArmorTechRating(dataFile.getDataAsInt("armor_tech")[0]);
} else if (dataFile.exists("armor_tech_rating")) {
Expand Down Expand Up @@ -754,14 +783,15 @@ public static BuildingBlock getBlock(Entity t) throws EntitySavingException {
if (!t.hasPatchworkArmor() && (t.getArmorType(1) != ArmorType.T_ARMOR_UNKNOWN)) {
blk.writeBlockData("armor_type", t.getArmorType(1));
blk.writeBlockData("armor_tech_rating", t.getArmorTechRating());
blk.writeBlockData("armor_tech_level", t.getArmorTechLevel(1));
} else if (t.hasPatchworkArmor()) {
blk.writeBlockData("armor_type",
EquipmentType.T_ARMOR_PATCHWORK);
for (int i = 1; i < t.locations(); i++) {
ArmorType armor = ArmorType.forEntity(t, i);
blk.writeBlockData(t.getLocationName(i) + "_armor_type", t.getArmorType(i));
blk.writeBlockData(t.getLocationName(i) + "_armor_tech",
TechConstants.getTechName(t.getArmorTechLevel(i)));
blk.writeBlockData(t.getLocationName(i) + "_armor_type", armor.getArmorType());
blk.writeBlockData(t.getLocationName(i) + "_armor_tech", TechConstants.getTechName(t.getArmorTechLevel(i)));
blk.writeBlockData(t.getLocationName(i) + "_armor_tech_rating", armor.getTechRating());
if (armor.hasFlag(MiscType.F_SUPPORT_VEE_BAR_ARMOR)) {
blk.writeBlockData(t.getLocationName(i) + "_barrating", armor.getBAR());
}
Expand Down Expand Up @@ -1116,6 +1146,9 @@ public static BuildingBlock getBlock(Entity t) throws EntitySavingException {
if (t.getFluff().hasEmbeddedFluffImage()) {
blk.writeBlockData("fluffimage", t.getFluff().getBase64FluffImage().getBase64String());
}
if (t.canonUnitWithInvalidBuild()) {
blk.writeBlockData("invalidSourceBuildReasons", t.getInvalidSourceBuildReasons().stream().map(Enum::name).toList());
}
return blk;
}

Expand Down
55 changes: 36 additions & 19 deletions megamek/src/megamek/common/loaders/BLKTankFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import megamek.common.FuelType;
import megamek.common.SuperHeavyTank;
import megamek.common.Tank;
import megamek.common.equipment.ArmorType;
import megamek.common.util.BuildingBlock;
import megamek.logging.MMLogger;

Expand Down Expand Up @@ -192,25 +193,7 @@ public Entity getEntity() throws EntityLoadingException {
t.initializeArmor(fullArmor[x], x);
}

boolean patchworkArmor = false;
if (dataFile.exists("armor_type")) {
if (dataFile.getDataAsInt("armor_type")[0] == EquipmentType.T_ARMOR_PATCHWORK) {
patchworkArmor = true;
} else {
t.setArmorType(dataFile.getDataAsInt("armor_type")[0]);
}
} else {
t.setArmorType(EquipmentType.T_ARMOR_STANDARD);
}
if (!patchworkArmor && dataFile.exists("armor_tech")) {
t.setArmorTechLevel(dataFile.getDataAsInt("armor_tech")[0]);
}
if (patchworkArmor) {
for (int i = 1; i < t.locations(); i++) {
t.setArmorType(dataFile.getDataAsInt(t.getLocationName(i) + "_armor_type")[0], i);
t.setArmorTechLevel(dataFile.getDataAsInt(t.getLocationName(i) + "_armor_type")[0], i);
}
}
setupArmorTypeAndTechLevel(t);

t.autoSetInternal();

Expand Down Expand Up @@ -286,4 +269,38 @@ public Entity getEntity() throws EntityLoadingException {
loadQuirks(t);
return t;
}

private void setupArmorTypeAndTechLevel(Tank t) {
boolean patchworkArmor = false;
if (dataFile.exists("armor_type")) {
if (dataFile.getDataAsInt("armor_type")[0] == EquipmentType.T_ARMOR_PATCHWORK) {
patchworkArmor = true;
} else {
var armorTypeId = dataFile.getDataAsInt("armor_type")[0];
t.setArmorType(armorTypeId);
var armorType = ArmorType.of(armorTypeId, t.isClan());
var techLevel = -1;
if (dataFile.exists("armor_tech_level")) {
techLevel = dataFile.getDataAsInt("armor_tech_level")[0];
} else {
techLevel = armorType.getStaticTechLevel().getCompoundTechLevel(t.isClan());
}
t.setArmorTechLevel(techLevel);
}
} else {
t.setArmorType(EquipmentType.T_ARMOR_STANDARD);
}
if (!patchworkArmor && dataFile.exists("armor_tech")) {
t.setArmorTechLevel(dataFile.getDataAsInt("armor_tech")[0]);
}
if (patchworkArmor) {
for (int i = 1; i < t.locations(); i++) {
var armorTypeId = dataFile.getDataAsInt(t.getLocationName(i) + "_armor_type")[0];
var armorType = ArmorType.of(armorTypeId, t.isClan());
t.setArmorType(armorTypeId, i);
var techLevel = armorType.getStaticTechLevel().getCompoundTechLevel(t.isClan());
t.setArmorTechLevel(techLevel, i);
}
}
}
}
10 changes: 5 additions & 5 deletions megamek/src/megamek/common/verifier/TestAdvancedAerospace.java
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ public static int minimumBaseCrew(Jumpship vessel) {
/**
* One gunner is required for each capital weapon and each six standard scale
* weapons, rounding up
*
*
* @return The vessel's minimum gunner requirements.
*/
public static int requiredGunners(Jumpship vessel) {
Expand Down Expand Up @@ -622,7 +622,7 @@ public boolean correctEntity(StringBuffer buff, int ammoTechLvl) {
if (skip()) {
return true;
}
if (!correctWeight(buff)) {
if (!allowOverweightConstruction() && !correctWeight(buff)) {
buff.insert(0, printTechLevel() + printShortMovement());
buff.append(printWeightCalculation());
correct = false;
Expand All @@ -648,7 +648,7 @@ public boolean correctEntity(StringBuffer buff, int ammoTechLvl) {
correct &= correctGravDecks(buff);
correct &= correctBays(buff);
correct &= correctCriticals(buff);
if (getEntity().hasQuirk(OptionsConstants.QUIRK_NEG_ILLEGAL_DESIGN)) {
if (getEntity().hasQuirk(OptionsConstants.QUIRK_NEG_ILLEGAL_DESIGN) || getEntity().canonUnitWithInvalidBuild()) {
correct = true;
}
return correct;
Expand Down Expand Up @@ -865,7 +865,7 @@ public boolean hasIllegalEquipmentCombinations(StringBuffer buff) {

/**
* Checks that the unit meets minimum crew and quarters requirements.
*
*
* @param buffer Where to write messages explaining failures.
* @return true if the crew data is valid.
*/
Expand Down Expand Up @@ -901,7 +901,7 @@ public boolean correctCrew(StringBuffer buffer) {
/**
* Checks that the unit does not exceed the maximum number or size of gravity
* decks.
*
*
* @param buffer Where to write messages explaining failures.
* @return true if the crew data is valid.
*/
Expand Down
Loading
Loading