/*
* MegaMekLab - Copyright (C) 2008
*
* Original author - jtighe (torren@users.sourceforge.net)
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*/
package megameklab.com.util;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Collectors;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.text.DefaultCaret;
import javax.swing.text.html.HTMLEditorKit;
import megamek.common.Aero;
import megamek.common.AmmoType;
import megamek.common.BattleArmor;
import megamek.common.BipedMech;
import megamek.common.CriticalSlot;
import megamek.common.Entity;
import megamek.common.EquipmentType;
import megamek.common.Infantry;
import megamek.common.LandAirMech;
import megamek.common.LocationFullException;
import megamek.common.Mech;
import megamek.common.MechView;
import megamek.common.MiscType;
import megamek.common.Mounted;
import megamek.common.QuadMech;
import megamek.common.Tank;
import megamek.common.TechConstants;
import megamek.common.TripodMech;
import megamek.common.VTOL;
import megamek.common.WeaponType;
import megamek.common.verifier.EntityVerifier;
import megamek.common.verifier.TestAero;
import megamek.common.verifier.TestBattleArmor;
import megamek.common.verifier.TestEntity;
import megamek.common.verifier.TestInfantry;
import megamek.common.verifier.TestMech;
import megamek.common.verifier.TestSupportVehicle;
import megamek.common.verifier.TestTank;
import megamek.common.weapons.ACWeapon;
import megamek.common.weapons.AmmoWeapon;
import megamek.common.weapons.BPodWeapon;
import megamek.common.weapons.CLAMS;
import megamek.common.weapons.CLChemicalLaserWeapon;
import megamek.common.weapons.CLLaserAMS;
import megamek.common.weapons.CLLightTAG;
import megamek.common.weapons.CLPlasmaCannon;
import megamek.common.weapons.CLTAG;
import megamek.common.weapons.EnergyWeapon;
import megamek.common.weapons.GaussWeapon;
import megamek.common.weapons.HAGWeapon;
import megamek.common.weapons.HVACWeapon;
import megamek.common.weapons.ISAMS;
import megamek.common.weapons.ISAPDS;
import megamek.common.weapons.ISC3M;
import megamek.common.weapons.ISC3MBS;
import megamek.common.weapons.ISLaserAMS;
import megamek.common.weapons.ISPlasmaRifle;
import megamek.common.weapons.ISTAG;
import megamek.common.weapons.LBXACWeapon;
import megamek.common.weapons.LRMWeapon;
import megamek.common.weapons.LRTWeapon;
import megamek.common.weapons.LegAttack;
import megamek.common.weapons.MGWeapon;
import megamek.common.weapons.MPodWeapon;
import megamek.common.weapons.MRMWeapon;
import megamek.common.weapons.PPCWeapon;
import megamek.common.weapons.RLWeapon;
import megamek.common.weapons.SRMWeapon;
import megamek.common.weapons.SRTWeapon;
import megamek.common.weapons.StopSwarmAttack;
import megamek.common.weapons.StreakLRMWeapon;
import megamek.common.weapons.StreakSRMWeapon;
import megamek.common.weapons.SwarmAttack;
import megamek.common.weapons.SwarmWeaponAttack;
import megamek.common.weapons.ThunderBoltWeapon;
import megamek.common.weapons.UACWeapon;
import megamek.common.weapons.VehicleFlamerWeapon;
import megamek.common.weapons.battlearmor.CLBALBX;
import megamek.common.weapons.battlearmor.CLBALightTAG;
import megamek.common.weapons.battlearmor.ISBALightTAG;
import megamek.common.weapons.infantry.InfantryRifleAutoRifleWeapon;
import megamek.common.weapons.infantry.InfantryWeapon;
public class UnitUtil {
public static int TECH_INTRO = 0;
public static int TECH_STANDARD = 1;
public static int TECH_ADVANCED = 2;
public static int TECH_EXPERIMENTAL = 3;
public static int TECH_UNOFFICAL = 4;
private static Font euroFont = null;
private static Font euroBoldFont = null;
/**
* tells is EquipementType is an equipment that uses crits/mounted and is
* spread across multiple locations
*
* @param eq
* @return
*/
public static boolean isFixedLocationSpreadEquipment(EquipmentType eq) {
return (eq instanceof MiscType)
&& (eq.hasFlag(MiscType.F_JUMP_BOOSTER)
|| eq.hasFlag(MiscType.F_BA_MANIPULATOR)
|| eq.hasFlag(MiscType.F_PARTIAL_WING)
|| eq.hasFlag(MiscType.F_NULLSIG)
|| eq.hasFlag(MiscType.F_VOIDSIG)
|| eq.hasFlag(MiscType.F_ENVIRONMENTAL_SEALING)
|| eq.hasFlag(MiscType.F_TRACKS)
|| eq.hasFlag(MiscType.F_TALON)
|| (eq.hasFlag(MiscType.F_STEALTH) && eq
.hasFlag(MiscType.F_MECH_EQUIPMENT))
|| eq.hasFlag(MiscType.F_CHAMELEON_SHIELD)
|| eq.hasFlag(MiscType.F_BLUE_SHIELD)
|| eq.hasFlag(MiscType.F_MAST_MOUNT)
|| eq.hasFlag(MiscType.F_SCM));
}
/**
* tells if the EquipmentType is a type of armor
*
* @param eq
* @return
*/
public static boolean isArmor(EquipmentType eq) {
return Arrays.asList(EquipmentType.armorNames).contains(eq.getName());
}
/**
* tells if the EquipmentType is a type of armor
*
* @param eq
* @return
*/
public static boolean isStructure(EquipmentType eq) {
for (String armor : EquipmentType.structureNames) {
if (eq.getName().equals(armor)) {
return true;
}
}
return false;
}
/**
* tells if EquipmentType is TSM or TargetComp
*
* @param eq
* @return
*/
public static boolean isTSM(EquipmentType eq) {
return (eq instanceof MiscType)
&& (eq.hasFlag(MiscType.F_TSM) || eq
.hasFlag((MiscType.F_INDUSTRIAL_TSM)));
}
/**
* tells if EquipmentType is MASC
*
* @param eq
* @return
*/
public static boolean isMASC(EquipmentType eq) {
return (eq instanceof MiscType)
&& (eq.hasFlag(MiscType.F_MASC) && !eq
.hasSubType(MiscType.S_SUPERCHARGER));
}
/**
* Returns the number of crits used by EquipmentType eq, 1 if armor or
* structure EquipmentType
*
* @param unit
* @param eq
* @return
*/
public static int getCritsUsed(Entity unit, EquipmentType eq) {
boolean isMisc = eq instanceof MiscType;
double toReturn = eq.getCriticals(unit);
//if it's 0, we can return now (e.g. standard armor or IS, we don't
//want that to count as 1 later on
if (toReturn == 0) {
return 0;
}
if (isMisc
&& eq.hasFlag(MiscType.F_PARTIAL_WING)
&& TechConstants
.isClan(eq.getTechLevel(unit.getTechLevelYear()))) {
toReturn = 3;
} else if (isMisc
&& eq.hasFlag(MiscType.F_PARTIAL_WING)
&& !TechConstants.isClan(eq.getTechLevel(unit
.getTechLevelYear()))) {
toReturn = 4;
} else if (isMisc
&& (eq.hasFlag(MiscType.F_JUMP_BOOSTER)
|| eq.hasFlag(MiscType.F_TALON) || eq
.hasFlag(MiscType.F_STEALTH))) {
toReturn = 2;
} else if (UnitUtil.isFixedLocationSpreadEquipment(eq) || UnitUtil.isTSM(eq)
|| UnitUtil.isArmorOrStructure(eq)) {
toReturn = 1;
}
if ((unit instanceof Mech) && ((Mech) unit).isSuperHeavy()) {
toReturn = Math.ceil(toReturn/2.0);
}
return (int)toReturn;
}
public static void removeMounted(Entity unit, Mounted mount) {
UnitUtil.removeCriticals(unit, mount);
// Some special checks for BA
if (unit instanceof BattleArmor){
// If we're removing a DWP and it has an attached weapon, we need
// to detach the weapon
if (mount.getType().hasFlag(MiscType.F_DETACHABLE_WEAPON_PACK)
&& (mount.getLinked() != null)){
Mounted link = mount.getLinked();
link.setDWPMounted(false);
link.setLinked(null);
link.setLinkedBy(null);
}
// If we are removing a weapon that is mounted in an DWP, we need
// to clear the mounted status of the DWP
if ((mount.getLinkedBy() != null)
&& mount.getLinkedBy().getType().hasFlag(
MiscType.F_DETACHABLE_WEAPON_PACK)){
Mounted dwp = mount.getLinkedBy();
dwp.setLinked(null);
dwp.setLinkedBy(null);
}
// If we're removing an APM and it has an attached weapon, we need
// to detach the weapon
if (mount.getType().hasFlag(MiscType.F_AP_MOUNT)
&& (mount.getLinked() != null)){
Mounted link = mount.getLinked();
link.setAPMMounted(false);
link.setLinked(null);
link.setLinkedBy(null);
}
// If we are removing a weapon that is mounted in an APM, we need
// to clear the mounted status of the AP Mount
if ((mount.getLinkedBy() != null)
&& mount.getLinkedBy().getType().hasFlag(
MiscType.F_AP_MOUNT)){
Mounted apm = mount.getLinkedBy();
apm.setLinked(null);
apm.setLinkedBy(null);
}
}
// Some special checks for Aeros
if (unit instanceof Aero) {
if (mount.getType() instanceof WeaponType) {
// Aeros have additional weapon lists that need to be cleared
((Aero)unit).getTotalWeaponList().remove(mount);
((Aero)unit).getWeaponBayList().remove(mount);
((Aero)unit).getWeaponGroupList().remove(mount);
}
}
unit.getEquipment().remove(mount);
if (mount.getType() instanceof MiscType) {
unit.getMisc().remove(mount);
} else if (mount.getType() instanceof AmmoType) {
unit.getAmmo().remove(mount);
} else {
unit.getWeaponList().remove(mount);
}
// It's possible that the equipment we are removing was linked to
// something else, and so the linkedBy state may be set. We should
// remove it. Using getLinked could be unreliable, so we'll brute force
// it
// An example of this would be removing a linked Artemis IV FCS
for (Mounted m : unit.getEquipment()) {
if (mount.equals(m.getLinkedBy())) {
m.setLinkedBy(null);
}
}
}
/**
* Sets the corresponding critical slots to null for the Mounted object.
*
* @param unit
* @param eq
*/
public static void removeCriticals(Entity unit, Mounted eq) {
if (eq.getLocation() == Entity.LOC_NONE) {
return;
}
for (int loc = 0; loc < unit.locations(); loc++) {
for (int slot = 0; slot < unit.getNumberOfCriticals(loc); slot++) {
CriticalSlot cs = unit.getCritical(loc, slot);
if ((cs != null)
&& (cs.getType() == CriticalSlot.TYPE_EQUIPMENT)) {
if (cs.getMount().equals(eq)) {
// If there are two pieces of equipment in this slot,
// remove first one, and replace it with the second
if (cs.getMount2() != null) {
cs.setMount(cs.getMount2());
cs.setMount2(null);
} else { // If it's the only Mounted, clear the slot
cs = null;
unit.setCritical(loc, slot, cs);
}
} else if ((cs.getMount2() != null)
&& cs.getMount2().equals(eq)) {
cs.setMount2(null);
}
}
}
}
}
public static void addMounted(Entity unit, Mounted mounted, int loc,
boolean rearMounted) throws LocationFullException {
unit.addEquipment(mounted, loc, rearMounted);
mounted.setOmniPodMounted(canPodMount(unit, mounted));
}
/**
* Tells if param EQ is a targetting computer.
*
* @param eq
* Mounted that might be a targetting computer
* @return True if is a targetting computer false if not.
*/
public static boolean isTargettingComputer(Mounted eq) {
if ((eq.getType() instanceof MiscType)
&& eq.getType().hasFlag(MiscType.F_TARGCOMP)) {
return true;
}
return false;
}
/**
* Reset all the Crits and Mounts on the Unit.
*
* @param unit
*/
public static void resetCriticalsAndMounts(Mech unit) {
for (int location = Mech.LOC_HEAD; location <= Mech.LOC_LLEG; location++) {
for (int slot = 0; slot < unit.getNumberOfCriticals(location); slot++) {
CriticalSlot cs = unit.getCritical(location, slot);
if ((cs != null)
&& (cs.getType() == CriticalSlot.TYPE_EQUIPMENT)) {
cs = null;
unit.setCritical(location, slot, cs);
}
}
}
for (Mounted mount : unit.getEquipment()) {
mount.setLocation(Entity.LOC_NONE, false);
}
}
/**
* Check to see if the unit is using Clan TC
*
* @param unit
* @return
*/
public static boolean hasClanTC(Mech unit) {
for (Mounted mount : unit.getMisc()) {
if (mount.getType().hasFlag(MiscType.F_TARGCOMP)
&& TechConstants.isClan(mount.getType().getTechLevel(
unit.getTechLevelYear()))) {
return true;
}
}
return false;
}
/**
* Updates TC Crits and Mounts based on weapons on a unit or if the TC has
* been removed.
*
* @param unit
*/
public static Mounted updateTC(Entity unit, EquipmentType tc) {
UnitUtil.removeTC(unit);
return UnitUtil.createTCMounts(unit, tc);
}
/**
* Creates TC Mount.
*
* @param unit
*/
public static Mounted createTCMounts(Entity unit, EquipmentType tc) {
Mounted mount = null;
try {
mount = unit.addEquipment(tc, Entity.LOC_NONE);
} catch (Exception ex) {
}
return mount;
}
/**
* Checks to see if unit can use the techlevel
*
* @param unit
* @param techLevel
* @return Boolean if the tech level is legal for the passed unit
*/
public static boolean isLegal(Entity unit, int techLevel) {
return TechConstants.isLegal(unit.getTechLevel(), techLevel, false,
unit.isMixedTech());
}
/**
* Checks if the unit has laser heatsinks.
*
* @param unit
* @return
*/
public static boolean hasLaserHeatSinks(Mech unit) {
if (!unit.hasDoubleHeatSinks()) {
return false;
}
for (Mounted mounted : unit.getMisc()) {
if (mounted.getType().hasFlag(MiscType.F_LASER_HEAT_SINK)) {
return true;
}
}
return false;
}
/***
* Checks for Clan DHS
*
* @param unit
* @return
*/
public static boolean hasClanDoubleHeatSinks(Mech unit) {
if (!unit.hasDoubleHeatSinks()) {
return false;
}
for (Mounted mounted : unit.getMisc()) {
if (mounted.getType().hasFlag(MiscType.F_LASER_HEAT_SINK)) {
return false;
}
if (mounted.getType().hasFlag(MiscType.F_DOUBLE_HEAT_SINK)) {
if (mounted.getType().getInternalName()
.equals("CLDoubleHeatSink")) {
return true;
}
return false;
}
}
return false;
}
/**
* checks if Mounted is a heat sink
*
* @param eq
* @return
*/
public static boolean isHeatSink(Mounted eq) {
return ((eq.getType() != null) && isHeatSink(eq.getType()));
}
/**
* Checks if EquipmentType is a heat sink
*
* @param eq
* @return
*/
public static boolean isHeatSink(EquipmentType eq) {
return isHeatSink(eq, false);
}
public static boolean isHeatSink(EquipmentType eq, boolean ignoreprototype) {
if ((eq instanceof MiscType)
&& (eq.hasFlag(MiscType.F_HEAT_SINK)
|| eq.hasFlag(MiscType.F_LASER_HEAT_SINK)
|| eq.hasFlag(MiscType.F_DOUBLE_HEAT_SINK) || (eq
.hasFlag(MiscType.F_IS_DOUBLE_HEAT_SINK_PROTOTYPE) && !ignoreprototype))) {
return true;
}
return false;
}
/**
* Checks if EquipmentType is a Mech Physical weapon
*
* @param eq
* @return
*/
public static boolean isPhysicalWeapon(EquipmentType eq) {
if ((eq instanceof MiscType)
&& ((eq.hasFlag(MiscType.F_CLUB)
|| eq.hasFlag(MiscType.F_HAND_WEAPON) || eq
.hasFlag(MiscType.F_TALON)))) {
if (eq.hasFlag(MiscType.F_CLUB)
&& ((eq.hasSubType(MiscType.S_CLUB) || eq
.hasSubType(MiscType.S_TREE_CLUB)))) {
return false;
}
return true;
}
return false;
}
/**
* Removes the specified number of heat sinks from the mek Heat sinks are
* removed first fwith LOC_NONE above the free crit limit then they are
* removed with a location, and lastly they are removed below the free crit
* limit
*
* @param unit
*/
public static void removeHeatSinks(Mech unit, int number) {
Vector<Mounted> toRemove = new Vector<Mounted>();
int base = UnitUtil.getCriticalFreeHeatSinks(unit,
unit.hasCompactHeatSinks());
boolean splitCompact = false;
if (unit.hasCompactHeatSinks()) {
// first check to see if there is a single compact heat sink outside
// of
// the engine and remove this first if so
Mounted mount = UnitUtil.getSingleCompactHeatSink(unit);
if ((null != mount) && (number > 0)) {
UnitUtil.removeMounted(unit, mount);
number--;
}
// if number is now uneven, then note that we will need to split a
// compact
if ((number % 2) == 1) {
splitCompact = true;
number--;
}
}
Vector<Mounted> unassigned = new Vector<Mounted>();
Vector<Mounted> assigned = new Vector<Mounted>();
Vector<Mounted> free = new Vector<Mounted>();
for (Mounted m : unit.getMisc()) {
if (UnitUtil.isHeatSink(m)) {
if (m.getLocation() == Entity.LOC_NONE) {
if (base > 0) {
free.add(m);
base--;
} else {
unassigned.add(m);
}
} else {
assigned.add(m);
}
}
}
toRemove.addAll(unassigned);
toRemove.addAll(assigned);
toRemove.addAll(free);
if (unit.hasCompactHeatSinks()) {
// need to do some number magic here. The unassigned and assigned
// slots
// should each contain two heat sinks, but if we dip into the free
// then we
// are looking at one heat sink.
int numberDouble = Math.min(number / 2, unassigned.size()
+ assigned.size());
int numberSingle = Math.max(0, number - (2 * numberDouble));
number = numberDouble + numberSingle;
}
number = Math.min(number, toRemove.size());
for (int i = 0; i < number; i++) {
Mounted eq = toRemove.get(i);
UnitUtil.removeMounted(unit, eq);
}
if (splitCompact) {
Mounted eq = toRemove.get(number);
int loc = eq.getLocation();
// remove singleCompact mount and replace with a double
UnitUtil.removeMounted(unit, eq);
if (!eq.getType().hasFlag(MiscType.F_HEAT_SINK)) {
try {
UnitUtil.addMounted(unit,
new Mounted(unit, EquipmentType
.get("IS1 Compact Heat Sink")), loc, false);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
/**
* adds all heat sinks to the mech
*
* @param unit
* @param hsAmount
* @param hsType
*/
public static void addHeatSinkMounts(Mech unit, int hsAmount, String hsType) {
EquipmentType sinkType;
sinkType = EquipmentType.get(UnitUtil.getHeatSinkType(hsType,
unit.isClan()));
if (hsType.equals("Compact")) {
UnitUtil.addCompactHeatSinkMounts(unit, hsAmount);
} else {
for (; hsAmount > 0; hsAmount--) {
try {
unit.addEquipment(new Mounted(unit, sinkType),
Entity.LOC_NONE, false);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
public static void addCompactHeatSinkMounts(Mech unit, int hsAmount) {
// first we need to figure out how many single compacts we need to add
// for the engine, if any
int currentSinks = UnitUtil.countActualHeatSinks(unit);
int engineCompacts = Math.min(hsAmount,
UnitUtil.getCriticalFreeHeatSinks(unit, true));
int engineToAdd = Math.max(0, engineCompacts - currentSinks);
unit.addEngineSinks("IS1 Compact Heat Sink", engineToAdd);
int restHS = hsAmount - engineToAdd;
Mounted singleCompact = getSingleCompactHeatSink(unit);
if ((restHS % 2) == 1) {
if (null == singleCompact) {
try {
unit.addEquipment(new Mounted(unit, EquipmentType
.get("IS1 Compact Heat Sink")),
Entity.LOC_NONE, false);
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
int loc = singleCompact.getLocation();
// remove singleCompact mount and replace with a double
UnitUtil.removeMounted(unit, singleCompact);
try {
addMounted(unit,
new Mounted(unit, EquipmentType.get(UnitUtil
.getHeatSinkType("Compact", unit.isClan()))),
loc, false);
} catch (Exception ex) {
ex.printStackTrace();
}
}
restHS -= 1;
}
for (; restHS > 0; restHS -= 2) {
try {
unit.addEquipment(new Mounted(unit, EquipmentType.get(UnitUtil
.getHeatSinkType("Compact", unit.isClan()))),
Entity.LOC_NONE, false);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* get the single non-compact heat sink that is a non-engine sink, if it
* exits
*
* @param unit
*/
public static Mounted getSingleCompactHeatSink(Mech unit) {
int base = UnitUtil.getCriticalFreeHeatSinks(unit, true);
for (Mounted m : unit.getMisc()) {
if (m.getType().hasFlag(MiscType.F_COMPACT_HEAT_SINK)
&& m.getType().hasFlag(MiscType.F_HEAT_SINK)) {
if (base <= 0) {
return m;
} else {
base--;
}
}
}
return null;
}
public static String getHeatSinkType(String type, boolean clan) {
String heatSinkType = "Heat Sink";
if (type.startsWith("(Clan)")) {
clan = true;
type = type.substring(7).trim();
} else if (type.startsWith("(IS)")) {
clan = false;
type = type.substring(4).trim();
}
if (clan) {
if (type.equals("Single")) {
heatSinkType = "Heat Sink";
} else if (type.equals("Double")) {
heatSinkType = "CLDoubleHeatSink";
} else {
heatSinkType = "Laser Heat Sink";
}
} else {
if (type.equals("Single")) {
heatSinkType = "Heat Sink";
} else if (type.equals("Double")) {
heatSinkType = "ISDoubleHeatSink";
} else {
heatSinkType = "IS2 Compact Heat Sinks";
}
}
return heatSinkType;
}
public static boolean hasSameHeatSinkType(Mech unit, String type) {
// this seems like a total hack, but at present we apparently have no
// good static integer codes for this on entity
String heatSinkType = UnitUtil.getHeatSinkType(type, unit.isClan());
for (Mounted mounted : unit.getMisc()) {
if (type.equals("Compact")
&& mounted.getType().hasFlag(MiscType.F_COMPACT_HEAT_SINK)) {
return true;
}
if (mounted.getType().hasFlag(MiscType.F_HEAT_SINK)
|| mounted.getType().hasFlag(MiscType.F_DOUBLE_HEAT_SINK)
|| mounted.getType().hasFlag(MiscType.F_LASER_HEAT_SINK)) {
return mounted.getType().getInternalName().equals(heatSinkType);
}
}
return false;
}
/**
* updates the heat sinks.
*
* @param unit
* @param hsAmount
* @param hsType
*/
public static void updateHeatSinks(Mech unit, int hsAmount, String hsType) {
// if we have the same type of heat sink, then we should not remove the
// existing heat sinks
int currentSinks = UnitUtil.countActualHeatSinks(unit);
if (UnitUtil.hasSameHeatSinkType(unit, hsType)) {
if (hsAmount < currentSinks) {
UnitUtil.removeHeatSinks(unit, currentSinks - hsAmount);
} else if (hsAmount > currentSinks) {
UnitUtil.addHeatSinkMounts(unit, hsAmount - currentSinks,
hsType);
}
} else {
UnitUtil.removeHeatSinks(unit, hsAmount);
UnitUtil.addHeatSinkMounts(unit, hsAmount, hsType);
}
unit.resetSinks();
}
/**
* This will cycle through the heat sinks and make sure that enough of them
* are set LOC_NONE based on the basechassisheat sinks
*
* @param unit
*/
public static void updateAutoSinks(Mech unit, String hsType) {
int base = UnitUtil.getCriticalFreeHeatSinks(unit,
hsType.equals("Compact"));
Vector<Mounted> unassigned = new Vector<Mounted>();
Vector<Mounted> assigned = new Vector<Mounted>();
for (Mounted m : unit.getMisc()) {
if (UnitUtil.isHeatSink(m)) {
if (m.getLocation() == Entity.LOC_NONE) {
unassigned.add(m);
} else {
assigned.add(m);
}
}
}
int needed = base - unassigned.size();
if (needed <= 0) {
return;
}
for (Mounted m : assigned) {
if (needed <= 0) {
return;
}
UnitUtil.removeCriticals(unit, m);
m.setLocation(Entity.LOC_NONE);
needed--;
}
// There may be more crit-free heatsinks, but if the 'mech doesn't
// have that many heatsinks, the additional space is unused.
}
public static boolean isJumpJet(Mounted m) {
return m.getType().hasFlag(MiscType.F_JUMP_JET)
|| m.getType().hasFlag(MiscType.F_JUMP_BOOSTER);
}
public static String getJumpJetType(int type, boolean clan) {
if (type == Mech.JUMP_IMPROVED) {
if (clan) {
return "CLImprovedJump Jet";
} else {
return "ISImprovedJump Jet";
}
} else if (type == Mech.JUMP_PROTOTYPE) {
return "ISPrototypeJumpJet";
} else if (type == Mech.JUMP_BOOSTER) {
return "Jump Booster";
} else if (type == Mech.JUMP_PROTOTYPE_IMPROVED) {
return "ISPrototypeImprovedJumpJet";
}
return "JumpJet";
}
/**
* Removes all jump jets from the mek
*
* @param unit
*/
public static void removeJumpJets(Mech unit, int number) {
Vector<Mounted> toRemove = new Vector<Mounted>();
ArrayList<Mounted> misceq = unit.getMisc();
for (Mounted eq : misceq) {
if (UnitUtil.isJumpJet(eq)) {
toRemove.add(eq);
if (toRemove.size() >= number) {
break;
}
}
}
for (Mounted eq : toRemove) {
UnitUtil.removeMounted(unit, eq);
}
}
/**
* updates the Jump Jets.
*
* @param unit
* @param jjAmount
* @param jjType
*/
public static void updateJumpJets(Mech unit, int jjAmount, int jjType) {
unit.setOriginalJumpMP(jjAmount);
int ctype = unit.getJumpType();
if (jjType == ctype) {
int currentJJ = (int)unit.getMisc().stream().filter(m -> m.getType()
.hasFlag(MiscType.F_JUMP_JET))
.count();
if (jjAmount < currentJJ) {
UnitUtil.removeJumpJets(unit, currentJJ - jjAmount);
return;
} else if (jjAmount > currentJJ) {
jjAmount = jjAmount - currentJJ;
} else {
return; // No change, get the fuck outta here!
}
} else {
UnitUtil.removeJumpJets(unit, unit.getJumpMP());
}
// if this is the same jump jet type, then only remove if too many
// and add if too low
if (jjType == Mech.JUMP_BOOSTER) {
UnitUtil.removeJumpJets(unit, unit.getJumpMP());
createSpreadMounts(
unit,
EquipmentType.get(UnitUtil.getJumpJetType(jjType,
unit.isClan())));
} else {
while (jjAmount > 0) {
try {
unit.addEquipment(
new Mounted(unit, EquipmentType.get(UnitUtil
.getJumpJetType(jjType, unit.isClan()))),
Entity.LOC_NONE, false);
} catch (Exception ex) {
ex.printStackTrace();
}
jjAmount--;
}
}
}
/**
* Removes all enhancements (TSM and MASC) from the mek
*
* @param unit
*/
public static void removeEnhancements(Mech unit) {
ConcurrentLinkedQueue<Mounted> equipmentList = new ConcurrentLinkedQueue<Mounted>(
unit.getMisc());
for (Mounted eq : equipmentList) {
if (UnitUtil.isTSM(eq.getType()) || UnitUtil.isMASC(eq.getType())) {
UnitUtil.removeCriticals(unit, eq);
}
}
for (Mounted eq : equipmentList) {
if (UnitUtil.isTSM(eq.getType()) || UnitUtil.isMASC(eq.getType())) {
unit.getMisc().remove(eq);
unit.getEquipment().remove(eq);
}
}
}
public static void updateEnhancements(Mech unit, boolean hasMASC,
boolean hasTSM) {
UnitUtil.removeEnhancements(unit);
if (hasTSM) {
if (unit.isIndustrial()) {
UnitUtil.createSpreadMounts(unit,
EquipmentType.get("Industrial TSM"));
} else {
UnitUtil.createSpreadMounts(unit, EquipmentType.get("TSM"));
}
}
if (hasMASC) {
Mounted mount = new Mounted(unit, EquipmentType.get("ISMASC"));
if (unit.isClan()) {
mount = new Mounted(unit, EquipmentType.get("CLMASC"));
}
try {
unit.addEquipment(mount, Entity.LOC_NONE, false);
} catch (LocationFullException lfe) {
// this can't happen, we add to Entity.LOC_NONE
}
}
}
public static boolean isPrintableEquipment(EquipmentType eq) {
return UnitUtil.isPrintableEquipment(eq, false);
}
/**
* simple method to let us know if eq should be printed on the weapons and
* equipment section of the Record sheet.
*
* @param eq
* @return
*/
public static boolean isPrintableEquipment(EquipmentType eq, boolean isMech) {
if (UnitUtil.isArmorOrStructure(eq)) {
return false;
}
if (UnitUtil.isFixedLocationSpreadEquipment(eq)
&& !(eq instanceof MiscType) && eq.hasFlag(MiscType.F_TALON)) {
return false;
}
if (UnitUtil.isJumpJet(eq) && isMech) {
return false;
}
if (!eq.isHittable() && isMech) {
return false;
}
if ((eq instanceof MiscType)
&& (eq.hasFlag(MiscType.F_CASE)
|| eq.hasFlag(MiscType.F_ARTEMIS)
|| eq.hasFlag(MiscType.F_ARTEMIS_V)
|| eq.hasFlag(MiscType.F_APOLLO)
|| (eq.hasFlag(MiscType.F_MASC) && !eq
.hasSubType(MiscType.S_JETBOOSTER))
|| eq.hasFlag(MiscType.F_HARJEL)
|| eq.hasFlag(MiscType.F_MASS)
|| eq.hasFlag(MiscType.F_CHASSIS_MODIFICATION)
|| eq.hasFlag(MiscType.F_MASH_EXTRA)
|| eq.hasFlag(MiscType.F_DRONE_EXTRA) || eq
.hasFlag(MiscType.F_SPONSON_TURRET))) {
return false;
}
if (UnitUtil.isHeatSink(eq)) {
return false;
}
return true;
}
/**
* simple method to let us know if eq should be printed on the weapons and
* equipment section of the Record sheet.
*
* @param eq
* @return
*/
public static boolean isPrintableBAEquipment(EquipmentType eq) {
if (UnitUtil.isArmorOrStructure(eq)) {
return false;
}
if (UnitUtil.isJumpJet(eq)) {
return false;
}
if ((eq instanceof MiscType)
&& ((eq.hasFlag(MiscType.F_AP_MOUNT) && !eq.hasFlag(MiscType.F_BA_MANIPULATOR))
|| eq.hasFlag(MiscType.F_FIRE_RESISTANT)
|| eq.hasFlag(MiscType.F_STEALTH)
|| eq.hasFlag(MiscType.F_ARTEMIS)
|| eq.hasFlag(MiscType.F_ARTEMIS_V)
|| eq.hasFlag(MiscType.F_APOLLO)
|| eq.hasFlag(MiscType.F_HARJEL)
|| eq.hasFlag(MiscType.F_MASS)
|| eq.hasFlag(MiscType.F_DETACHABLE_WEAPON_PACK))) {
return false;
}
if (UnitUtil.isHeatSink(eq)) {
return false;
}
if ((eq instanceof LegAttack) || (eq instanceof SwarmAttack)
|| (eq instanceof StopSwarmAttack)
|| (eq instanceof InfantryRifleAutoRifleWeapon)
|| (eq instanceof SwarmWeaponAttack)) {
return false;
}
return true;
}
public static boolean isBAMultiMount(EquipmentType equip) {
if ((equip instanceof WeaponType)
&& (equip.hasFlag(WeaponType.F_TASER)
|| (((WeaponType)equip).getAmmoType()
== AmmoType.T_NARC))){
return true;
}
return false;
}
/**
* Changes the location for a Mounted instance. Note: for BattleArmor, this
* effects which suit the equipment is placed on (as that is what
* Mounted.location means for BA), but not where on the suit
* it's located (ie, BAMountLocation isn't affected). BattleArmor should
* change this outside of this method.
*
* @param unit
* @param eq
* @param location
* @param secondaryLocation
* @param rear
*/
public static void changeMountStatus(Entity unit, Mounted eq, int location,
int secondaryLocation, boolean rear) {
eq.setLocation(location, rear);
eq.setSecondLocation(secondaryLocation, rear);
eq.setSplit(secondaryLocation > -1);
}
/**
* Checks whether the equipment is eligible for pod mounting in an omni unit, either because the
* equipment itself can never be pod-mounted (such as armor, structure, or myomer enhancements),
* or the number of fixed heat sinks have not been assigned locations.
*
* @param unit
* @param eq
* @return
*/
public static boolean canPodMount(Entity unit, Mounted eq) {
if (!unit.isOmni() || eq.getType().isOmniFixedOnly()) {
return false;
}
if (eq.getType() instanceof MiscType && unit instanceof Mech
&& (eq.getType().hasFlag(MiscType.F_HEAT_SINK)
|| eq.getType().hasFlag(MiscType.F_DOUBLE_HEAT_SINK)
|| eq.getType().hasFlag(MiscType.F_IS_DOUBLE_HEAT_SINK_PROTOTYPE))
&& unit.hasEngine()) {
int needed = Math.max(0, unit.getEngine().getWeightFreeEngineHeatSinks() -
UnitUtil.getCriticalFreeHeatSinks(unit, ((Mech)unit).hasCompactHeatSinks()));
long fixed = unit.getMisc().stream().filter(m ->
(m.getType().hasFlag(MiscType.F_HEAT_SINK)
|| m.getType().hasFlag(MiscType.F_DOUBLE_HEAT_SINK)
|| m.getType().hasFlag(MiscType.F_IS_DOUBLE_HEAT_SINK_PROTOTYPE))
&& m.getLocation() != Entity.LOC_NONE && !m.isOmniPodMounted()).count();
//Do not count this heat among the fixed, since we are checking whether we can change it to pod-mounted
if (eq.getLocation() != Entity.LOC_NONE && !eq.isOmniPodMounted()) {
fixed--;
}
return fixed >= needed;
}
return true;
}
/**
* Removes all pod-mounted equipment from an omni unit
* @param unit
*/
public static void resetBaseChassis(Entity unit) {
if (!unit.isOmni()) {
return;
}
List<Mounted> pods = unit.getEquipment().stream()
.filter(Mounted::isOmniPodMounted)
.collect(Collectors.toList());
for (Mounted m : pods) {
UnitUtil.removeMounted(unit, m);
if (m.getType() instanceof MiscType
&& m.getType().hasFlag(MiscType.F_JUMP_JET)) {
unit.setOriginalJumpMP(unit.getOriginalJumpMP() - 1);
}
}
}
public static boolean hasTargComp(Entity unit) {
for (Mounted mount : unit.getEquipment()) {
if ((mount.getType() instanceof MiscType)
&& mount.getType().hasFlag(MiscType.F_TARGCOMP)) {
return true;
}
}
return false;
}
public static Mounted getTargComp(Entity unit) {
for (Mounted misc : unit.getMisc()) {
if (misc.getType().hasFlag(MiscType.F_TARGCOMP)) {
return misc;
}
}
return null;
}
public static int[] getHighestContinuousNumberOfCritsArray(Mech unit) {
int[] critSpaces = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 };
for (int loc = 0; loc <= Mech.LOC_LLEG; loc++) {
critSpaces[loc] = UnitUtil.getHighestContinuousNumberOfCrits(unit,
loc);
}
return critSpaces;
}
/**
* This method will return the number of contiguous criticals in the given
* location, starting at the given critical slot
*
* @param unit Unit to check critical slots on
* @param location The location on the unit to check slots on
* @param startingSlot The critical slot to start at
* @return
*/
public static int getContiguousNumberOfCrits(Entity unit, int location,
int startingSlot){
int numCritSlots = unit.getNumberOfCriticals(location);
int contiguousCrits = 0;
for (int slot = startingSlot; slot < numCritSlots; slot++) {
if (unit.getCritical(location, slot) == null) {
contiguousCrits++;
} else {
break;
}
}
return contiguousCrits;
}
public static int getHighestContinuousNumberOfCrits(Entity unit,
int location) {
int highestNumberOfCrits = 0;
int currentCritCount = 0;
// Handle locations without crits
if ((location == Entity.LOC_DESTROYED)
|| (location == Entity.LOC_NONE)) {
return 0;
}
for (int slot = 0; slot < unit.getNumberOfCriticals(location); slot++) {
if (unit.getCritical(location, slot) == null) {
currentCritCount++;
} else {
currentCritCount = 0;
}
highestNumberOfCrits = Math.max(currentCritCount,
highestNumberOfCrits);
}
return highestNumberOfCrits;
}
public static double getUnallocatedAmmoTonnage(Entity unit) {
double tonnage = 0;
for (Mounted mount : unit.getAmmo()) {
int ammoType = ((AmmoType)mount.getType()).getAmmoType();
// don't add ammo with just one shot, that's OS ammo
// Unless it's a single shot ammo type, like Cruise Missiles
if ((mount.getLocation() == Entity.LOC_NONE)
&& ((mount.getUsableShotsLeft() > 1)
|| (ammoType == AmmoType.T_CRUISE_MISSILE)
|| (ammoType == AmmoType.T_COOLANT_POD))) {
tonnage += mount.getType().getTonnage(unit);
}
}
return tonnage;
}
public static int getMaximumArmorPoints(Entity unit) {
int points = 0;
if (unit instanceof Mech) {
int headPoints = 3;
if (((Mech)unit).isSuperHeavy()) {
headPoints = 4;
}
points = (unit.getTotalInternal() * 2) + headPoints;
} else if (unit instanceof Tank) {
points = (int) Math.floor((unit.getWeight() * 3.5) + 40);
} else if (unit instanceof BattleArmor) {
points = (unit.getWeightClass() * 4) + 2;
}
return points;
}
public static int getMaximumArmorPoints(Entity unit, int loc) {
if ((unit instanceof Mech) && (loc == Mech.LOC_HEAD)) {
if (((Mech) unit).isSuperHeavy()) {
return 12;
} else {
return 9;
}
} else if (unit instanceof Mech) {
return unit.getInternal(loc) * 2;
} else if (unit instanceof Tank) {
return (int) Math.floor((unit.getWeight() * 3.5) + 40);
} else {
return 0;
}
}
public static double getMaximumArmorTonnage(Entity unit) {
double armorPerTon = 16.0 * EquipmentType.getArmorPointMultiplier(
unit.getArmorType(1), unit.getArmorTechLevel(1));
double armorWeight = 0;
if (unit.getArmorType(1) == EquipmentType.T_ARMOR_HARDENED) {
armorPerTon = 8.0;
}
if (unit instanceof Mech) {
double points = (unit.getTotalInternal() * 2);
// Add in extra armor points for head
if (((Mech) unit).isSuperHeavy()) {
points += 4;
} else {
points += 3;
}
armorWeight = points / armorPerTon;
armorWeight = Math.ceil(armorWeight * 2.0) / 2.0;
} else if (unit instanceof Tank) {
double points = Math.floor((unit.getWeight() * 3.5) + 40);
armorWeight = points / armorPerTon;
armorWeight = Math.ceil(armorWeight * 2.0) / 2.0;
} else if (unit instanceof BattleArmor) {
armorWeight = (unit.getWeightClass() * 4) + 2;
}
if (unit instanceof Aero){
double points =
TestAero.maxArmorPoints(unit, unit.getWeight());
armorWeight = points / armorPerTon;
}
return armorWeight;
}
/**
* NOTE: only use for non-patchwork armor
*
* @param unit
* @param armorTons
* @return
*/
public static int getArmorPoints(Entity unit, double armorTons) {
double armorPerTon = 16.0 * EquipmentType.getArmorPointMultiplier(
unit.getArmorType(1), unit.getArmorTechLevel(1));
if (unit.getArmorType(1) == EquipmentType.T_ARMOR_HARDENED) {
armorPerTon = 8.0;
}
return Math.min((int) Math.floor(armorPerTon * armorTons),
UnitUtil.getMaximumArmorPoints(unit));
}
/**
* NOTE: only use for non-patchwork armor
*
* @param unit
* @param armorTons
* @return
*/
public static int getArmorPoints(Entity unit, int loc, double armorTons) {
double armorPerTon = 16.0 * EquipmentType.getArmorPointMultiplier(
unit.getArmorType(loc), unit.getArmorTechLevel(loc));
if (unit.getArmorType(loc) == EquipmentType.T_ARMOR_HARDENED) {
armorPerTon = 8.0;
}
return Math.min((int) Math.floor(armorPerTon * armorTons),
UnitUtil.getMaximumArmorPoints(unit, loc));
}
public static void compactCriticals(Entity unit) {
for (int loc = 0; loc < unit.locations(); loc++) {
if (unit instanceof Mech) {
UnitUtil.compactCriticals((Mech) unit, loc);
} else {
UnitUtil.compactCriticals(unit, loc);
}
}
}
public static void compactCriticals(Entity unit, int loc) {
int firstEmpty = -1;
for (int slot = 0; slot < unit.getNumberOfCriticals(loc); slot++) {
CriticalSlot cs = unit.getCritical(loc, slot);
if ((cs == null) && (firstEmpty == -1)) {
firstEmpty = slot;
}
if ((firstEmpty != -1) && (cs != null)) {
// move this to the first empty slot
unit.setCritical(loc, firstEmpty, cs);
// mark the old slot empty
unit.setCritical(loc, slot, null);
// restart just after the moved slot's new location
slot = firstEmpty;
firstEmpty = -1;
}
}
}
public static void compactCriticals(Mech unit) {
for (int loc = 0; loc < unit.locations(); loc++) {
UnitUtil.compactCriticals(unit, loc);
}
}
private static void compactCriticals(Mech mech, int loc) {
if (loc == Mech.LOC_HEAD) {
// This location has an empty slot inbetween systems crits
// which will mess up parsing if compacted.
return;
}
int firstEmpty = -1;
for (int slot = 0; slot < mech.getNumberOfCriticals(loc); slot++) {
CriticalSlot cs = mech.getCritical(loc, slot);
if ((cs == null) && (firstEmpty == -1)) {
firstEmpty = slot;
}
if ((firstEmpty != -1) && (cs != null)) {
// move this to the first empty slot
mech.setCritical(loc, firstEmpty, cs);
// mark the old slot empty
mech.setCritical(loc, slot, null);
// restart just after the moved slot's new location
slot = firstEmpty;
firstEmpty = -1;
}
}
}
public static boolean isAMS(WeaponType weapon) {
return weapon.hasFlag(WeaponType.F_AMS);
}
public static boolean hasSwitchableAmmo(WeaponType weapon) {
if (weapon instanceof StreakLRMWeapon) {
return false;
}
if (weapon instanceof StreakSRMWeapon) {
return false;
}
if (weapon instanceof EnergyWeapon) {
return false;
}
if (weapon instanceof GaussWeapon) {
return false;
}
if (weapon instanceof UACWeapon) {
return false;
}
if (weapon instanceof HVACWeapon) {
return false;
}
if (weapon instanceof HAGWeapon) {
return false;
}
if (weapon instanceof MGWeapon) {
return false;
}
if (UnitUtil.isAMS(weapon)) {
return false;
}
if (weapon instanceof ThunderBoltWeapon) {
return false;
}
if (weapon instanceof CLChemicalLaserWeapon) {
return false;
}
if (weapon instanceof MPodWeapon) {
return false;
}
if (weapon instanceof BPodWeapon) {
return false;
}
if (weapon instanceof ISPlasmaRifle) {
return false;
}
if (weapon instanceof CLPlasmaCannon) {
return false;
}
if (weapon instanceof VehicleFlamerWeapon) {
return false;
}
if (!(weapon instanceof AmmoWeapon)) {
return false;
}
if (weapon instanceof CLBALBX) {
return false;
}
return true;
}
/**
* Expands crits that are a single mount by have multiple spreadable crits
* Such as TSM, Endo Steel, Reactive armor.
*
* @param unit
*/
public static void expandUnitMounts(Mech unit) {
for (int location = 0; location <= Mech.LOC_LLEG; location++) {
for (int slot = 0; slot < unit.getNumberOfCriticals(location); slot++) {
CriticalSlot cs = unit.getCritical(location, slot);
if ((cs == null) || (cs.getType() == CriticalSlot.TYPE_SYSTEM)) {
continue;
}
Mounted mount = cs.getMount();
if (!UnitUtil.isFixedLocationSpreadEquipment(mount.getType())
&& (UnitUtil.isTSM(mount.getType()) || UnitUtil
.isArmorOrStructure(mount.getType()))) {
Mounted newMount = new Mounted(unit, mount.getType());
newMount.setLocation(location, mount.isRearMounted());
newMount.setArmored(mount.isArmored());
cs.setMount(newMount);
cs.setArmored(mount.isArmored());
unit.getEquipment().remove(mount);
unit.getMisc().remove(mount);
unit.getEquipment().add(newMount);
unit.getMisc().add(newMount);
}
}
}
}
/**
* create a Mounted and corresponding CriticalSlots for the passed in
* <code>EquipmentType</code> on the passed in <code>Mech</code>
*
* @param unit
* @param equip
* @return
*/
public static Mounted createSpreadMounts(Mech unit, EquipmentType equip) {
// how many non-spreadable contiguous blocks of crits?
int blocks = 0;
boolean isMisc = equip instanceof MiscType;
blocks = equip.getCriticals(unit);
List<Integer> locations = new ArrayList<Integer>();
if (isMisc) {
if ((equip.hasFlag(MiscType.F_INDUSTRIAL_TSM) || equip
.hasFlag(MiscType.F_TSM))) {
// all crits user placeable
for (int i = 0; i < equip.getCriticals(unit); i++) {
locations.add(Entity.LOC_NONE);
}
} else if (equip.hasFlag(MiscType.F_ENVIRONMENTAL_SEALING)) {
// 1 crit in each location
for (int i = 0; i < unit.locations(); i++) {
locations.add(i);
}
} else if (equip.hasFlag(MiscType.F_STEALTH)) {
// 2 in arms, legs, side torsos
locations.add(Mech.LOC_LLEG);
locations.add(Mech.LOC_RLEG);
locations.add(Mech.LOC_LARM);
locations.add(Mech.LOC_RARM);
locations.add(Mech.LOC_LT);
locations.add(Mech.LOC_RT);
blocks = 6;
// Need to account for the center leg
if (unit instanceof TripodMech){
locations.add(Mech.LOC_CLEG);
blocks++;
}
} else if (equip.hasFlag(MiscType.F_SCM)) {
// 1 in arms, legs, side torsos
locations.add(Mech.LOC_LLEG);
locations.add(Mech.LOC_RLEG);
locations.add(Mech.LOC_LARM);
locations.add(Mech.LOC_RARM);
locations.add(Mech.LOC_LT);
locations.add(Mech.LOC_RT);
blocks = 6;
} else if ((equip.hasFlag(MiscType.F_TRACKS)
|| equip.hasFlag(MiscType.F_TALON) || equip
.hasFlag(MiscType.F_JUMP_BOOSTER))) {
// 1 block in each leg
locations.add(Mech.LOC_LLEG);
locations.add(Mech.LOC_RLEG);
if (unit instanceof QuadMech) {
locations.add(Mech.LOC_LARM);
locations.add(Mech.LOC_RARM);
}
blocks = (unit instanceof BipedMech ? 2 : 4);
// Need to account for the center leg
if (unit instanceof TripodMech){
locations.add(Mech.LOC_CLEG);
blocks = 3;
}
} else if (equip.hasFlag(MiscType.F_PARTIAL_WING)) {
// one block in each side torso
locations.add(Mech.LOC_LT);
locations.add(Mech.LOC_RT);
blocks = 2;
} else if ((equip.hasFlag(MiscType.F_VOIDSIG)
|| equip.hasFlag(MiscType.F_NULLSIG) || equip
.hasFlag(MiscType.F_BLUE_SHIELD))) {
// Need to account for the center leg
if (unit instanceof TripodMech){
blocks++;
}
// 1 crit in each location, except the head
for (int i = Mech.LOC_CT; i < unit.locations(); i++) {
locations.add(i);
}
} else if (equip.hasFlag(MiscType.F_CHAMELEON_SHIELD)) {
// Need to account for the center leg
if (unit instanceof TripodMech){
blocks++;
}
// 1 crit in each location except head and CT
for (int i = Mech.LOC_RT; i < unit.locations(); i++) {
locations.add(i);
}
}
}
boolean firstBlock = true;
Mounted mount = new Mounted(unit, equip);
for (; blocks > 0; blocks--) {
// how many crits per block?
int crits = UnitUtil.getCritsUsed(unit, equip);
for (int i = 0; i < crits; i++) {
try {
if (firstBlock || (locations.get(0) == Entity.LOC_NONE)) {
// create only one mount per equipment, for BV and stuff
addMounted(unit, mount, locations.get(0), false);
if (firstBlock) {
firstBlock = false;
}
if (locations.get(0) == Entity.LOC_NONE) {
// only user-placable spread stuff gets location
// none
// for those, we need to create a mount for each
// crit,
// otherwise we can't correctly let the user place
// them
// luckily, that only affects TSM, so BV works out
// correctly
mount = new Mounted(unit, equip);
}
} else {
CriticalSlot cs = new CriticalSlot(mount);
if (!unit.addCritical(locations.get(0), cs)) {
UnitUtil.removeCriticals(unit, mount);
JOptionPane.showMessageDialog(
null,
"No room for equipment",
mount.getName()
+ " does not fit into "
+ unit.getLocationName(locations
.get(0)),
JOptionPane.INFORMATION_MESSAGE);
unit.getMisc().remove(mount);
unit.getEquipment().remove(mount);
return null;
}
}
} catch (LocationFullException lfe) {
lfe.printStackTrace();
JOptionPane.showMessageDialog(
null,
lfe.getMessage(),
mount.getName() + " does not fit into "
+ unit.getLocationName(locations.get(0)),
JOptionPane.INFORMATION_MESSAGE);
unit.getMisc().remove(mount);
unit.getEquipment().remove(mount);
return null;
}
}
locations.remove(0);
}
return mount;
}
public static void loadFonts() {
if ((euroFont != null) && (euroBoldFont != null)) {
return;
}
String fName = "./data/fonts/Eurosti.TTF";
try {
File fontFile = new File(fName);
InputStream is = new FileInputStream(fontFile);
euroFont = Font.createFont(Font.TRUETYPE_FONT, is);
is.close();
} catch (Exception ex) {
ex.printStackTrace();
System.err.println(fName + " not loaded. Using Arial font.");
euroFont = new Font("Arial", Font.PLAIN, 8);
}
fName = "./data/fonts/Eurostib.TTF";
try {
File fontFile = new File(fName);
InputStream is = new FileInputStream(fontFile);
euroBoldFont = Font.createFont(Font.TRUETYPE_FONT, is);
is.close();
} catch (Exception ex) {
ex.printStackTrace();
System.err.println(fName + " not loaded. Using Arial font.");
euroBoldFont = new Font("Arial", Font.PLAIN, 8);
}
}
public static Font deriveFont(float pointSize) {
return UnitUtil.deriveFont(false, pointSize);
}
public static Font deriveFont(boolean boldFont, float pointSize) {
UnitUtil.loadFonts();
if (boldFont) {
return euroBoldFont.deriveFont(pointSize);
}
return euroFont.deriveFont(pointSize);
}
public static Font getNewFont(Graphics2D g2d, String info, boolean bold,
int stringWidth, float pointSize) {
Font font = UnitUtil.deriveFont(bold, pointSize);
while ((ImageHelper.getStringWidth(g2d, info, font) > stringWidth)
&& (pointSize > 0)) {
pointSize -= .1;
font = UnitUtil.deriveFont(bold, pointSize);
}
return font;
}
public static void removeOneShotAmmo(Entity unit) {
ArrayList<Mounted> ammoList = new ArrayList<Mounted>();
for (Mounted mount : unit.getAmmo()) {
if (mount.getLocation() == Entity.LOC_NONE) {
ammoList.add(mount);
}
}
for (Mounted mount : ammoList) {
int index = unit.getEquipment().indexOf(mount);
unit.getEquipment().remove(mount);
unit.getAmmo().remove(mount);
for (int location = 0; location <= Mech.LOC_LLEG; location++) {
for (int slot = 0; slot < unit.getNumberOfCriticals(location); slot++) {
CriticalSlot cs = unit.getCritical(location, slot);
if ((cs == null)
|| (cs.getType() == CriticalSlot.TYPE_SYSTEM)) {
continue;
}
if (cs.getIndex() >= index) {
cs.setIndex(cs.getIndex() - 1);
}
}
}
}
}
public static void removeClanCase(Entity unit) {
ArrayList<Mounted> caseList = new ArrayList<Mounted>();
for (Mounted mount : unit.getMisc()) {
if (mount.getType().getInternalName().equals("CLCASE")) {
caseList.add(mount);
}
}
for (Mounted mount : caseList) {
int index = unit.getEquipment().indexOf(mount);
unit.getEquipment().remove(mount);
unit.getMisc().remove(mount);
for (int location = 0; location <= Mech.LOC_LLEG; location++) {
for (int slot = 0; slot < unit.getNumberOfCriticals(location); slot++) {
CriticalSlot cs = unit.getCritical(location, slot);
if ((cs == null)
|| (cs.getType() == CriticalSlot.TYPE_SYSTEM)) {
continue;
}
if (cs.getIndex() >= index) {
cs.setIndex(cs.getIndex() - 1);
}
}
}
}
}
public static boolean hasAmmo(Entity unit, int location) {
for (Mounted mount : unit.getEquipment()) {
if (mount.getType().isExplosive(mount)
&& ((mount.getLocation() == location) || (mount
.getSecondLocation() == location))) {
return true;
}
}
return false;
}
/**
* Checks to see if something is a Jump Jet
*
* @param eq
* @return
*/
public static boolean isJumpJet(EquipmentType eq) {
if ((eq instanceof MiscType)
&& (eq.hasFlag(MiscType.F_JUMP_BOOSTER)
|| eq.hasFlag(MiscType.F_JUMP_JET) || eq
.hasFlag(MiscType.F_UMU))) {
return true;
}
return false;
}
public static int getBAAmmoCount(Entity ba, WeaponType weapon, int location) {
int ammoCount = 0;
for (Mounted mount : ba.getAmmo()) {
if (mount.getLocation() != location) {
continue;
}
AmmoType ammo = (AmmoType) mount.getType();
if ((ammo.getRackSize() == weapon.getRackSize())
&& (ammo.getAmmoType() == weapon.getAmmoType())) {
ammoCount++;
}
}
return ammoCount;
}
public static String getCritName(Entity unit, EquipmentType eq) {
String name = eq.getName();
if ((eq instanceof WeaponType)
&& (eq.hasFlag(WeaponType.F_C3M) || eq
.hasFlag(WeaponType.F_C3MBS))) {
return name = name.substring(0,
eq.getName().indexOf("with TAG") - 1);
}
if (unit.isMixedTech()
&& (eq.getTechLevel(unit.getTechLevelYear()) != TechConstants.T_ALLOWED_ALL)
&& (eq.getTechLevel(unit.getTechLevelYear()) != TechConstants.T_TECH_UNKNOWN)) {
if (unit.isClan()
&& !TechConstants.isClan(eq.getTechLevel(unit
.getTechLevelYear()))) {
name = name + " (IS)";
}
if (!unit.isClan()
&& TechConstants.isClan(eq.getTechLevel(unit
.getTechLevelYear()))) {
name = name + " (Clan)";
}
}
return name;
}
public static String getToolTipInfo(Entity unit, Mounted eq) {
DecimalFormatSymbols unusualSymbols = new DecimalFormatSymbols();
unusualSymbols.setDecimalSeparator('.');
unusualSymbols.setGroupingSeparator(',');
DecimalFormat myFormatter = new DecimalFormat("#,##0", unusualSymbols);
StringBuilder sb = new StringBuilder("<HTML>");
sb.append(eq.getName());
if ((eq.getType().hasFlag(MiscType.F_DETACHABLE_WEAPON_PACK)
|| eq.getType().hasFlag(MiscType.F_AP_MOUNT))
&& (eq.getLinked() != null)){
sb.append(" (attached " + eq.getLinked().getName()
+ ")");
}
if (eq.isSquadSupportWeapon()){
sb.append(" (squad support weapon)");
}
if (eq.getType() instanceof InfantryWeapon) {
sb.append("<br>Damage/Trooper: ");
double infDamage = ((InfantryWeapon) eq.getType())
.getInfantryDamage();
sb.append(infDamage);
sb.append("<br>Range Class: "
+ ((InfantryWeapon) eq.getType()).getInfantryRange());
} else {
sb.append("<br>Crits: ");
sb.append(eq.getType().getCriticals(unit));
sb.append("<br>Tonnage: ");
if (eq.getType() instanceof MiscType) {
sb.append(((MiscType) eq.getType()).getTonnage(unit,
eq.getLocation()));
} else {
sb.append(eq.getType().getTonnage(unit));
}
if (eq.getType() instanceof WeaponType) {
sb.append("<br>Heat: ");
sb.append(((WeaponType) eq.getType()).getHeat());
}
}
sb.append("<Br>Cost: ");
double cost = eq.getType().getCost(unit, false, eq.getLocation());
sb.append(myFormatter.format(cost));
sb.append(" CBills");
if (eq.isRearMounted()) {
sb.append("<br>Rear Facing");
}
if (eq.isMechTurretMounted()) {
sb.append("<br>Turret mounted");
}
if (eq.isArmored()) {
sb.append("<br>Armored");
}
if ((unit instanceof BattleArmor)
&& eq.getType().hasFlag(WeaponType.F_INF_SUPPORT)){
sb.append("<br>* Infantry support weapons must be held in an " +
"Armored Glove");
} else if ((unit instanceof BattleArmor)
&& eq.getType().hasFlag(WeaponType.F_INFANTRY)){
sb.append("<br>* Infantry weapons must be mounted in AP Mounts");
}
sb.append("</html>");
return sb.toString();
}
/**
* Return the number of critical-space free heatsinks that the given entity
* can have.
*
* @param unit
* The unit mounting the heatsinks
* @param compact
* Whether the heatsinks are compact or not
* @return T he number of critical-free heat sinks.
*/
public static int getCriticalFreeHeatSinks(Entity unit, boolean compact) {
int engineHSCapacity = unit.getEngine().integralHeatSinkCapacity(
compact);
if (unit.isOmni()) {
engineHSCapacity = Math.min(engineHSCapacity, unit.getEngine()
.getBaseChassisHeatSinks(compact));
}
return engineHSCapacity;
}
public static boolean isPreviousCritEmpty(Entity unit, CriticalSlot cs,
int slot, int location) {
if (slot == 0) {
return false;
}
if (unit instanceof Mech) {
if ((slot > 0) && (unit.getCritical(location, slot - 1) != null)) {
return false;
}
}
return true;
}
public static boolean isLastCrit(Entity unit, CriticalSlot cs, int slot,
int location) {
if (unit instanceof Mech) {
return UnitUtil.isLastMechCrit((Mech) unit, cs, slot, location);
}
return true;
}
public static boolean isLastMechCrit(Mech unit, CriticalSlot cs, int slot,
int location) {
if (cs == null) {
return true;
}
// extra check for the last crit in a location, it shouldn't get a
// border
if ((slot + 1) >= unit.getNumberOfCriticals(location)) {
return false;
}
int lastIndex = 0;
if (cs.getType() == CriticalSlot.TYPE_SYSTEM) {
for (int position = 0; position < unit
.getNumberOfCriticals(location); position++) {
if ((cs.getIndex() == Mech.SYSTEM_ENGINE) && (slot >= 3)
&& (position < 3)) {
position = 3;
}
CriticalSlot crit = unit.getCritical(location, position);
if ((crit != null)
&& (crit.getType() == CriticalSlot.TYPE_SYSTEM)
&& (crit.getIndex() == cs.getIndex())) {
lastIndex = position;
} else if (position > slot) {
break;
}
}
} else {
CriticalSlot nextCrit = unit.getCritical(location, slot + 1);
if (nextCrit == null) {
return true;
} else if ((nextCrit.getMount() == null)
|| !nextCrit.getMount().equals(cs.getMount())) {
return true;
} else {
return false;
}
/*
* Mounted originalMount = cs.getMount(); Mounted testMount = null;
*
* if (originalMount == null) { originalMount =
* cs.getMount(); }
*
* if (originalMount == null) { return true; }
*
* int numberOfCrits = slot -
* (originalMount.getType().getCriticals(unit) - 1);
*
* if (numberOfCrits < 0) { return false; } for (int position =
* slot; position >= numberOfCrits; position--) { CriticalSlot crit
* = unit.getCritical(location, position);
*
* if ((crit == null) || (crit.getType() !=
* CriticalSlot.TYPE_EQUIPMENT)) { return false; }
*
* if ((testMount = crit.getMount()) == null) { testMount =
* crit.getMount(); }
*
* if (testMount == null) { return false; }
*
* if (!testMount.equals(originalMount)) { return false; }
*
* } return true;
*/
}
return slot == lastIndex;
}
public static void updateCritsArmoredStatus(Entity unit, Mounted mount) {
for (int position = 0; position < unit.getNumberOfCriticals(mount
.getLocation()); position++) {
CriticalSlot cs = unit.getCritical(mount.getLocation(), position);
if ((cs == null) || (cs.getType() == CriticalSlot.TYPE_SYSTEM)) {
continue;
}
if ((cs.getMount() != null) && cs.getMount().equals(mount)) {
cs.setArmored(mount.isArmored());
} else if ((cs.getMount() != null)
&& cs.getMount().equals(mount)) {
cs.setArmored(mount.isArmored());
}
}
if ((mount.isSplitable() || mount.getType().isSpreadable())
&& (mount.getSecondLocation() != Entity.LOC_NONE)) {
for (int position = 0; position < unit.getNumberOfCriticals(mount
.getSecondLocation()); position++) {
CriticalSlot cs = unit.getCritical(mount.getLocation(),
position);
if ((cs == null) || (cs.getType() == CriticalSlot.TYPE_SYSTEM)) {
continue;
}
if ((cs.getMount() != null) && cs.getMount().equals(mount)) {
cs.setArmored(mount.isArmored());
} else if ((cs.getMount() != null)
&& cs.getMount().equals(mount)) {
cs.setArmored(mount.isArmored());
}
}
}
}
public static void updateCritsArmoredStatus(Entity unit, CriticalSlot cs,
int location) {
if ((cs == null) || (cs.getType() == CriticalSlot.TYPE_EQUIPMENT)) {
return;
}
if (cs.getIndex() <= Mech.SYSTEM_GYRO) {
for (int loc = Mech.LOC_HEAD; loc <= Mech.LOC_LT; loc++) {
for (int slot = 0; slot < unit.getNumberOfCriticals(loc); slot++) {
CriticalSlot newCrit = unit.getCritical(loc, slot);
if ((newCrit != null)
&& (newCrit.getType() == CriticalSlot.TYPE_SYSTEM)
&& (newCrit.getIndex() == cs.getIndex())) {
newCrit.setArmored(cs.isArmored());
}
}
}
} else {
// actuators
for (int slot = 0; slot < unit.getNumberOfCriticals(location); slot++) {
CriticalSlot newCrit = unit.getCritical(location, slot);
if ((newCrit != null)
&& (newCrit.getType() == CriticalSlot.TYPE_SYSTEM)
&& (newCrit.getIndex() == cs.getIndex())) {
newCrit.setArmored(cs.isArmored());
}
}
}
}
public static boolean isArmorOrStructure(EquipmentType eq) {
return UnitUtil.isArmor(eq) || UnitUtil.isStructure(eq);
}
public static boolean isArmorable(CriticalSlot cs) {
if (cs == null) {
return false;
}
if (cs.getType() == CriticalSlot.TYPE_SYSTEM) {
return true;
}
Mounted mount = cs.getMount();
if (mount == null) {
return false;
}
if (UnitUtil.isArmorOrStructure(mount.getType())) {
return false;
}
if (UnitUtil.isTSM(mount.getType())) {
return false;
}
if (mount.getType() instanceof MiscType) {
MiscType misc = (MiscType) mount.getType();
if (misc.hasFlag(MiscType.F_SPIKES)
|| misc.hasFlag(MiscType.F_REACTIVE)
|| misc.hasFlag(MiscType.F_MODULAR_ARMOR)
|| misc.isShield()) {
return false;
}
}
return true;
}
/**
* Returns the units tech type.
*
* @param unit
* @return
*/
public static int getUnitTechType(Entity unit) {
switch (unit.getTechLevel()) {
case TechConstants.T_INTRO_BOXSET:
return UnitUtil.TECH_INTRO;
case TechConstants.T_IS_TW_NON_BOX:
case TechConstants.T_IS_TW_ALL:
case TechConstants.T_CLAN_TW:
return UnitUtil.TECH_STANDARD;
case TechConstants.T_IS_ADVANCED:
case TechConstants.T_CLAN_ADVANCED:
return UnitUtil.TECH_ADVANCED;
case TechConstants.T_IS_EXPERIMENTAL:
case TechConstants.T_CLAN_EXPERIMENTAL:
return UnitUtil.TECH_EXPERIMENTAL;
case TechConstants.T_IS_UNOFFICIAL:
case TechConstants.T_CLAN_UNOFFICIAL:
return UnitUtil.TECH_UNOFFICAL;
}
return UnitUtil.TECH_INTRO;
}
public static void updateLoadedMech(Entity unit) {
if (unit instanceof Mech) {
UnitUtil.updateLoadedMech((Mech) unit);
}
}
public static void updateLoadedMech(Mech unit) {
UnitUtil.removeOneShotAmmo(unit);
UnitUtil.removeClanCase(unit);
UnitUtil.expandUnitMounts(unit);
UnitUtil.checkArmor(unit);
}
public static boolean isUnitWeapon(EquipmentType eq, Entity unit) {
if (unit instanceof Tank) {
return UnitUtil.isTankWeapon(eq, unit);
}
if (unit instanceof BattleArmor) {
return UnitUtil.isBattleArmorWeapon(eq, unit);
}
if (unit instanceof Infantry) {
return UnitUtil.isInfantryEquipment(eq, unit);
}
return UnitUtil.isMechWeapon(eq, unit);
}
public static boolean isUnitEquipment(EquipmentType eq, Entity unit) {
if (unit instanceof Tank) {
return UnitUtil.isTankEquipment(eq, unit instanceof VTOL);
}
if (unit instanceof BattleArmor) {
return UnitUtil.isBattleArmorEquipment(eq);
}
return UnitUtil.isMechEquipment(eq, (Mech) unit);
}
public static boolean isMechWeapon(EquipmentType eq, Entity unit) {
if (eq instanceof InfantryWeapon) {
return false;
}
if (UnitUtil.isHeatSink(eq) || UnitUtil.isArmorOrStructure(eq)
|| UnitUtil.isJumpJet(eq)
|| UnitUtil.isMechEquipment(eq, (Mech) unit)) {
return false;
}
if (eq instanceof AmmoType) {
return false;
}
if (eq instanceof WeaponType) {
WeaponType weapon = (WeaponType) eq;
if (!weapon.hasFlag(WeaponType.F_MECH_WEAPON)) {
return false;
}
if (weapon.getTonnage(unit) <= 0) {
return false;
}
if (weapon.isCapital() || weapon.isSubCapital()) {
return false;
}
if (((weapon instanceof LRMWeapon) || (weapon instanceof LRTWeapon))
&& (weapon.getRackSize() != 5)
&& (weapon.getRackSize() != 10)
&& (weapon.getRackSize() != 15)
&& (weapon.getRackSize() != 20)) {
return false;
}
if (((weapon instanceof SRMWeapon) || (weapon instanceof SRTWeapon))
&& (weapon.getRackSize() != 2)
&& (weapon.getRackSize() != 4)
&& (weapon.getRackSize() != 6)) {
return false;
}
if ((weapon instanceof MRMWeapon) && (weapon.getRackSize() < 10)) {
return false;
}
if ((weapon instanceof RLWeapon) && (weapon.getRackSize() < 10)) {
return false;
}
if (weapon.hasFlag(WeaponType.F_ENERGY)
|| (weapon.hasFlag(WeaponType.F_PLASMA) && (weapon
.getAmmoType() == AmmoType.T_PLASMA))) {
if (weapon.hasFlag(WeaponType.F_ENERGY)
&& weapon.hasFlag(WeaponType.F_PLASMA)
&& (weapon.getAmmoType() == AmmoType.T_NA)) {
return false;
}
}
return true;
}
return false;
}
public static boolean isAeroWeapon(EquipmentType eq, Aero unit) {
if (eq instanceof InfantryWeapon) {
return false;
}
if (UnitUtil.isHeatSink(eq) || UnitUtil.isArmorOrStructure(eq)
|| UnitUtil.isJumpJet(eq)
|| UnitUtil.isAeroEquipment(eq, unit)) {
return false;
}
if (eq instanceof AmmoType) {
return false;
}
if (eq instanceof WeaponType) {
WeaponType weapon = (WeaponType) eq;
if (!weapon.hasFlag(WeaponType.F_AERO_WEAPON)) {
return false;
}
if (weapon.getTonnage(unit) <= 0) {
return false;
}
if (weapon.isCapital() || weapon.isSubCapital()) {
return false;
}
if (((weapon instanceof LRMWeapon) || (weapon instanceof LRTWeapon))
&& (weapon.getRackSize() != 5)
&& (weapon.getRackSize() != 10)
&& (weapon.getRackSize() != 15)
&& (weapon.getRackSize() != 20)) {
return false;
}
if (((weapon instanceof SRMWeapon) || (weapon instanceof SRTWeapon))
&& (weapon.getRackSize() != 2)
&& (weapon.getRackSize() != 4)
&& (weapon.getRackSize() != 6)) {
return false;
}
if ((weapon instanceof MRMWeapon) && (weapon.getRackSize() < 10)) {
return false;
}
if ((weapon instanceof RLWeapon) && (weapon.getRackSize() < 10)) {
return false;
}
if (weapon.hasFlag(WeaponType.F_ENERGY)
|| (weapon.hasFlag(WeaponType.F_PLASMA) && (weapon
.getAmmoType() == AmmoType.T_PLASMA))) {
if (weapon.hasFlag(WeaponType.F_ENERGY)
&& weapon.hasFlag(WeaponType.F_PLASMA)
&& (weapon.getAmmoType() == AmmoType.T_NA)) {
return false;
}
}
return true;
}
return false;
}
public static boolean isAeroEquipment(EquipmentType eq, Aero unit) {
if (UnitUtil.isArmorOrStructure(eq)) {
return false;
}
if ((eq instanceof CLTAG) || (eq instanceof ISC3MBS)
|| (eq instanceof ISC3M) || (eq instanceof ISTAG)
|| eq.equals(EquipmentType.get("IS Coolant Pod"))
|| eq.equals(EquipmentType.get("Clan Coolant Pod"))
|| (eq instanceof CLLightTAG)
|| eq.hasFlag(WeaponType.F_AMS)) {
return true;
}
if ((eq instanceof MiscType)) {
if (eq.hasFlag(MiscType.F_QUAD_TURRET)) {
return false;
}
if ((eq.hasFlag(MiscType.F_SHOULDER_TURRET))) {
return false;
}
if (eq.hasFlag(MiscType.F_AERO_EQUIPMENT)
&& !eq.hasFlag(MiscType.F_CLUB)
&& !eq.hasFlag(MiscType.F_HAND_WEAPON)
&& !eq.hasFlag(MiscType.F_TALON)) {
return true;
}
}
return false;
}
public static boolean isMechEquipment(EquipmentType eq, Mech unit) {
if (UnitUtil.isArmorOrStructure(eq)) {
return false;
}
if ((eq instanceof CLTAG) || (eq instanceof ISC3MBS)
|| (eq instanceof ISC3M) || (eq instanceof ISTAG)
|| eq.equals(EquipmentType.get("IS Coolant Pod"))
|| eq.equals(EquipmentType.get("Clan Coolant Pod"))
|| (eq instanceof CLLightTAG)
|| (eq instanceof ISAMS)
|| (eq instanceof CLAMS)
|| (eq instanceof ISLaserAMS)
|| (eq instanceof CLLaserAMS)
|| (eq instanceof ISAPDS )) {
return true;
}
if ((eq instanceof MiscType)) {
if (eq.hasFlag(MiscType.F_BOMB_BAY) && !(unit instanceof LandAirMech)) {
return false;
}
if (eq.hasFlag(MiscType.F_QUAD_TURRET)
&& !(unit instanceof QuadMech)) {
return false;
}
if (!((unit instanceof BipedMech) || (unit instanceof TripodMech))
&& (eq.hasFlag(MiscType.F_SHOULDER_TURRET))) {
return false;
}
if (unit.isSuperHeavy()
&& (eq.hasFlag(MiscType.F_ACTUATOR_ENHANCEMENT_SYSTEM)
|| eq.hasFlag(MiscType.F_MASC) // to catch Supercharger
|| eq.hasFlag(MiscType.F_SCM)
|| eq.hasFlag(MiscType.F_MODULAR_ARMOR)
|| eq.hasFlag(MiscType.F_PARTIAL_WING)
|| eq.hasFlag(MiscType.F_UMU))) {
return false;
}
if (eq.hasFlag(MiscType.F_MECH_EQUIPMENT)
&& !eq.hasFlag(MiscType.F_CLUB)
&& !eq.hasFlag(MiscType.F_HAND_WEAPON)
&& !eq.hasFlag(MiscType.F_TALON)) {
return true;
}
if (eq.hasFlag(MiscType.F_IS_DOUBLE_HEAT_SINK_PROTOTYPE)) {
return true;
}
}
return false;
}
public static boolean isTankWeapon(EquipmentType eq, Entity unit) {
if (eq instanceof InfantryWeapon) {
return false;
}
if (eq instanceof WeaponType) {
WeaponType weapon = (WeaponType) eq;
if (!weapon.hasFlag(WeaponType.F_TANK_WEAPON)) {
return false;
}
if (weapon.getTonnage(unit) <= 0) {
return false;
}
if (weapon.isCapital() || weapon.isSubCapital()) {
return false;
}
if (((weapon instanceof LRMWeapon) || (weapon instanceof LRTWeapon))
&& (weapon.getRackSize() != 5)
&& (weapon.getRackSize() != 10)
&& (weapon.getRackSize() != 15)
&& (weapon.getRackSize() != 20)) {
return false;
}
if (((weapon instanceof SRMWeapon) || (weapon instanceof SRTWeapon))
&& (weapon.getRackSize() != 2)
&& (weapon.getRackSize() != 4)
&& (weapon.getRackSize() != 6)) {
return false;
}
if ((weapon instanceof MRMWeapon) && (weapon.getRackSize() < 10)) {
return false;
}
if ((weapon instanceof RLWeapon) && (weapon.getRackSize() < 10)) {
return false;
}
if (weapon.hasFlag(WeaponType.F_ENERGY)
|| (weapon.hasFlag(WeaponType.F_PLASMA) && (weapon
.getAmmoType() == AmmoType.T_PLASMA))) {
if (weapon.hasFlag(WeaponType.F_ENERGY)
&& weapon.hasFlag(WeaponType.F_PLASMA)
&& (weapon.getAmmoType() == AmmoType.T_NA)) {
return false;
}
}
return true;
}
return false;
}
public static boolean isBattleArmorAPWeapon(EquipmentType etype){
InfantryWeapon infWeap = null;
if ((etype == null) || !(etype instanceof InfantryWeapon)){
return false;
} else {
infWeap = (InfantryWeapon)etype;
}
return infWeap.hasFlag(WeaponType.F_INFANTRY)
&& !infWeap.hasFlag(WeaponType.F_INF_POINT_BLANK)
&& !infWeap.hasFlag(WeaponType.F_INF_ARCHAIC)
&& (infWeap.getCrew() < 2);
}
public static boolean isBattleArmorWeapon(EquipmentType eq, Entity unit) {
if (eq instanceof WeaponType) {
WeaponType weapon = (WeaponType) eq;
if (!weapon.hasFlag(WeaponType.F_BA_WEAPON)) {
return false;
}
if (weapon.getTonnage(unit) <= 0) {
return false;
}
if (weapon.isCapital() || weapon.isSubCapital()) {
return false;
}
if ((eq instanceof SwarmAttack) || (eq instanceof StopSwarmAttack)
|| (eq instanceof LegAttack)) {
return false;
}
if (weapon.hasFlag(WeaponType.F_ENERGY)
|| (weapon.hasFlag(WeaponType.F_PLASMA) && (weapon
.getAmmoType() == AmmoType.T_PLASMA))) {
return true;
}
if (weapon.hasFlag(WeaponType.F_ENERGY) && (weapon.hasFlag(WeaponType.F_PLASMA))
&& (weapon.hasFlag(WeaponType.F_BA_WEAPON))) {
return true;
}
if (weapon.hasFlag(WeaponType.F_ENERGY)
&& weapon.hasFlag(WeaponType.F_PLASMA)
&& (weapon.getAmmoType() == AmmoType.T_NA))
{
return false;
}
return true;
}
return false;
}
public static boolean isInfantryEquipment(EquipmentType eq, Entity unit) {
// TODO: adjust for field guns and artillery
return eq instanceof InfantryWeapon;
}
public static boolean isTankEquipment(EquipmentType eq, boolean isVTOL) {
if (UnitUtil.isArmorOrStructure(eq)) {
return false;
}
// Chassis modifications should be ignored, as they will be added
// via checkboxes, and not shown as equipment
//if (eq.hasFlag(MiscType.F_CHASSIS_MODIFICATION)) {
// return false;
//}
// Display AMS as equipment (even though it's a weapon)
if (eq.hasFlag(WeaponType.F_AMS)
&& eq.hasFlag(WeaponType.F_TANK_WEAPON)) {
return true;
}
if ((eq instanceof CLTAG) || (eq instanceof ISC3M)
|| (eq instanceof ISC3MBS)
|| (eq instanceof ISTAG) || (eq instanceof CLLightTAG)) {
return true;
}
if ((eq instanceof MiscType) && eq.hasFlag(MiscType.F_TANK_EQUIPMENT)) {
return true;
}
if ((eq instanceof MiscType) && eq.hasFlag(MiscType.F_VTOL_EQUIPMENT)
&& isVTOL) { // Mast Mount!
return true;
}
return false;
}
public static boolean isBattleArmorEquipment(EquipmentType eq) {
if (UnitUtil.isArmorOrStructure(eq)) {
return false;
}
if ((eq instanceof CLBALightTAG) || (eq instanceof ISBALightTAG)) {
return true;
}
if ((eq instanceof MiscType) && eq.hasFlag(MiscType.F_BA_EQUIPMENT)) {
return true;
}
return false;
}
public static boolean canSwarm(BattleArmor ba) {
for (Mounted eq : ba.getEquipment()) {
if ((eq.getType() instanceof SwarmAttack)
|| (eq.getType() instanceof StopSwarmAttack)) {
return true;
}
}
return false;
}
public static boolean canLegAttack(BattleArmor ba) {
for (Mounted eq : ba.getEquipment()) {
if (eq.getType() instanceof LegAttack) {
return true;
}
}
return false;
}
public static boolean hasInfantryWeapons(BattleArmor ba) {
for (Mounted eq : ba.getEquipment()) {
if (eq.getType() instanceof InfantryWeapon) {
return true;
}
}
return false;
}
public static int getShieldDamageAbsorbtion(Mech mech, int location) {
for (int slot = 0; slot < mech.getNumberOfCriticals(location); slot++) {
CriticalSlot cs = mech.getCritical(location, slot);
if (cs == null) {
continue;
}
if (cs.getType() != CriticalSlot.TYPE_EQUIPMENT) {
continue;
}
Mounted m = cs.getMount();
if (m == null) {
System.err.println("Null Mount index: " + cs.getIndex());
m = cs.getMount();
}
EquipmentType type = m.getType();
if ((type instanceof MiscType) && ((MiscType) type).isShield()) {
return m.getBaseDamageAbsorptionRate();
}
}
return 0;
}
public static int getShieldDamageCapacity(Mech mech, int location) {
for (int slot = 0; slot < mech.getNumberOfCriticals(location); slot++) {
CriticalSlot cs = mech.getCritical(location, slot);
if (cs == null) {
continue;
}
if (cs.getType() != CriticalSlot.TYPE_EQUIPMENT) {
continue;
}
Mounted m = cs.getMount();
if (m == null) {
System.err.println("Null Mount index: " + cs.getIndex());
m = cs.getMount();
}
EquipmentType type = m.getType();
if ((type instanceof MiscType) && ((MiscType) type).isShield()) {
return m.getBaseDamageCapacity();
}
}
return 0;
}
/**
* remove all CriticalSlots on the passed unit that are internal structure or
* armor
*
* @param unit
* the Entity
* @param internalStructure
* true to remove IS, false to remove armor
*/
public static void removeISorArmorCrits(Entity unit,
boolean internalStructure) {
ArrayList<String> mountList = new ArrayList<String>();
if (internalStructure) {
for (String struc : EquipmentType.structureNames) {
mountList.add("IS " + struc);
mountList.add("Clan " + struc);
}
} else {
for (String armor : EquipmentType.armorNames) {
mountList.add("IS " + armor);
mountList.add("Clan " + armor);
}
}
for (int location = Mech.LOC_HEAD; location < unit.locations(); location++) {
for (int slot = 0; slot < unit.getNumberOfCriticals(location); slot++) {
CriticalSlot crit = unit.getCritical(location, slot);
if ((crit != null)
&& (crit.getType() == CriticalSlot.TYPE_EQUIPMENT)) {
Mounted mount = crit.getMount();
if ((mount != null)
&& (mount.getType() instanceof MiscType)
&& mountList.contains(mount.getType()
.getInternalName())) {
crit = null;
unit.setCritical(location, slot, crit);
}
}
}
}
}
/**
* remove all Mounted on the passed unit that are internal structure or
* armor
*
* @param unit
* the Entity
* @param internalStructure
* true to remove IS, false to remove armor
*/
public static void removeISorArmorMounts(Entity unit,
boolean internalStructure) {
UnitUtil.removeISorArmorCrits(unit, internalStructure);
ArrayList<String> mountList = new ArrayList<String>();
mountList.add("Standard");
List<String> names;
if (internalStructure) {
names = Arrays.asList(EquipmentType.structureNames);
} else {
names = Arrays.asList(EquipmentType.armorNames);
}
for (String name : names) {
mountList.add(String.format("Clan %1s", name));
mountList.add(String.format("IS %1s", name));
mountList.add(name);
}
for (int pos = 0; pos < unit.getEquipment().size();) {
Mounted mount = unit.getEquipment().get(pos);
if (mountList.contains(mount.getType().getInternalName())) {
unit.getEquipment().remove(pos);
} else {
pos++;
}
}
for (int pos = 0; pos < unit.getMisc().size();) {
Mounted mount = unit.getMisc().get(pos);
if ((mount.getType() instanceof MiscType)
&& mountList.contains(mount.getType().getInternalName())) {
unit.getMisc().remove(pos);
} else {
pos++;
}
}
if (internalStructure) {
unit.setStructureType(EquipmentType.T_STRUCTURE_STANDARD);
} else {
unit.setArmorType(EquipmentType.T_ARMOR_STANDARD);
unit.setArmorTechLevel(unit.getTechLevel());
}
}
public static void checkArmor(Entity unit) {
if (!(unit instanceof Mech)) {
return;
}
boolean foundError = false;
Mech mech = (Mech) unit;
// Check all the mechs locations to see if any armor is greater than can
// be in there.
for (int location = 0; location < mech.locations(); location++) {
// Head armor has a max of 9
if (location == Mech.LOC_HEAD) {
int armor = mech.getArmor(location);
if ((armor > 9) && !mech.isSuperHeavy()) {
foundError = true;
mech.initializeArmor(9, location);
} else if (armor > 12) {
foundError = true;
mech.initializeArmor(9, location);
}
} else {
int armor = mech.getArmor(location);
if (mech.hasRearArmor(location)) {
armor += mech.getArmor(location, true);
}
int totalArmor = mech.getInternal(location) * 2;
// Armor on the location is greater than what can be there.
if (armor > totalArmor) {
foundError = true;
int armorOverage = armor - totalArmor;
// check for locations with rear armor first and remove the
// extra armor from the rear first.
if (mech.hasRearArmor(location)) {
int rearArmor = mech.getArmor(location, true);
if (rearArmor >= armorOverage) {
mech.initializeRearArmor(rearArmor - armorOverage,
location);
armorOverage = 0;
} else {
armorOverage -= rearArmor;
mech.initializeRearArmor(0, location);
}
}
// Any armor overage left remove it from the front. Min 0
// armor in the location.
armor = mech.getArmor(location);
armor = Math.max(0, armor - armorOverage);
mech.initializeArmor(armor, location);
}
}
}
if (foundError) {
JOptionPane
.showMessageDialog(
null,
"Too much armor found on this unit.\n\rMegaMekLab has automatically corrected the problem.\n\rIt is suggested you check the armor allocation.",
"Too much armor", JOptionPane.WARNING_MESSAGE);
}
}
public static boolean hasManipulator(BattleArmor ba) {
for (Mounted mount : ba.getMisc()) {
MiscType eq = (MiscType) mount.getType();
if (eq.hasFlag(MiscType.F_BA_EQUIPMENT)
&& (eq.hasFlag(MiscType.F_ARMORED_GLOVE)
|| eq.hasFlag(MiscType.F_BASIC_MANIPULATOR) || eq
.hasFlag(MiscType.F_BATTLE_CLAW))) {
return true;
}
}
return false;
}
public static boolean isManipulator(Mounted mount) {
if (!(mount.getType() instanceof MiscType)) {
return false;
}
MiscType eq = (MiscType) mount.getType();
if (eq.hasFlag(MiscType.F_BA_EQUIPMENT)
&& (eq.hasFlag(MiscType.F_ARMORED_GLOVE)
|| eq.hasFlag(MiscType.F_BASIC_MANIPULATOR)
|| eq.hasFlag(MiscType.F_BATTLE_CLAW) || eq
.hasFlag(MiscType.F_CARGOLIFTER))) {
return true;
}
return false;
}
public static int getNumberOfEquipmentLikeThis(Entity unit,
EquipmentType baseEQ) {
int numberOfEq = 0;
for (Mounted mount : unit.getEquipment()) {
if (mount.getType().equals(baseEQ)) {
numberOfEq++;
}
}
return numberOfEq;
}
/**
* Returns a TestEntity instance for the supplied Entity.
*
* @param unit
* @return
*/
public static TestEntity getEntityVerifier(Entity unit) {
EntityVerifier entityVerifier = EntityVerifier.getInstance(new File(
"data/mechfiles/UnitVerifierOptions.xml"));
TestEntity testEntity = null;
if (unit instanceof Mech) {
testEntity = new TestMech((Mech) unit, entityVerifier.mechOption,
null);
} else if (unit instanceof Tank) {
if (unit.isSupportVehicle()) {
testEntity = new TestSupportVehicle((Tank) unit,
entityVerifier.tankOption, null);
} else {
testEntity = new TestTank((Tank) unit,
entityVerifier.tankOption, null);
}
} else if ((unit.getEntityType() == Entity.ETYPE_AERO)
&& (unit.getEntityType() !=
Entity.ETYPE_DROPSHIP)
&& (unit.getEntityType() !=
Entity.ETYPE_SMALL_CRAFT)
&& (unit.getEntityType() !=
Entity.ETYPE_FIGHTER_SQUADRON)
&& (unit.getEntityType() !=
Entity.ETYPE_JUMPSHIP)
&& (unit.getEntityType() !=
Entity.ETYPE_SPACE_STATION)) {
testEntity =
new TestAero((Aero)unit,entityVerifier.aeroOption,null);
} else if (unit instanceof BattleArmor){
testEntity = new TestBattleArmor((BattleArmor) unit,
entityVerifier.baOption, null);
} else if (unit instanceof Infantry) {
testEntity = new TestInfantry((Infantry)unit,
entityVerifier.infOption, null);
}
return testEntity;
}
/**
* check that the unit is vaild
*
* @param unit
* @return
*/
public static String validateUnit(Entity unit) {
StringBuffer sb = new StringBuffer();
TestEntity testEntity = getEntityVerifier(unit);
if (testEntity != null){
testEntity.correctEntity(sb, unit.getTechLevel());
}
return sb.toString();
}
public static void removeAllMiscMounteds(Entity unit, BigInteger flag) {
for (int pos = unit.getEquipment().size() - 1; pos >= 0; pos--) {
Mounted mount = unit.getEquipment().get(pos);
if ((mount.getType() instanceof MiscType)
&& ((MiscType) mount.getType()).hasFlag(flag)) {
UnitUtil.removeMounted(unit, mount);
}
}
}
public static void removeAllMounteds(Entity unit, EquipmentType et) {
for (int pos = unit.getEquipment().size() - 1; pos >= 0; pos--) {
Mounted mount = unit.getEquipment().get(pos);
if (mount.getType().equals(et)) {
UnitUtil.removeMounted(unit, mount);
}
}
}
public static void removeTC(Entity unit) {
for (int pos = unit.getEquipment().size() - 1; pos >= 0; pos--) {
Mounted mount = unit.getEquipment().get(pos);
if ((mount.getType() instanceof MiscType)
&& ((MiscType) mount.getType())
.hasFlag(MiscType.F_TARGCOMP)) {
UnitUtil.removeMounted(unit, mount);
}
}
}
public static boolean isValidLocation(Entity unit, EquipmentType eq,
int location) {
if (unit instanceof BattleArmor) {
// Infantry weapons can only be mounted in armored gloves/APMs
if (eq.hasFlag(WeaponType.F_INFANTRY)) {
return false;
}
return true;
}
if ((eq instanceof MiscType)) {
if (((eq.hasFlag(MiscType.F_CLUB) || eq
.hasFlag(MiscType.F_HAND_WEAPON)))) {
if ((unit instanceof QuadMech)
&& !((QuadMech) unit).isIndustrial()) {
return false;
}
if ((location != Mech.LOC_RARM) && (location != Mech.LOC_LARM)) {
if ((unit instanceof QuadMech)
&& ((QuadMech) unit).isIndustrial()) {
if ((location != Mech.LOC_LT)
&& (location != Mech.LOC_RT)) {
return false;
}
} else {
return false;
}
}
}
if (eq.hasFlag(MiscType.F_ACTUATOR_ENHANCEMENT_SYSTEM)) {
if ((location != Mech.LOC_RARM) && (location != Mech.LOC_LARM)
&& (location != Mech.LOC_LLEG)
&& (location != Mech.LOC_RLEG)
&& ((unit instanceof TripodMech)
&& location != Mech.LOC_CLEG)) {
return false;
}
}
if (eq.hasFlag(MiscType.F_HEAD_TURRET)
&& ((location != Mech.LOC_CT)
|| ((unit instanceof Mech) && (((Mech) unit)
.getCockpitType() != Mech.COCKPIT_TORSO_MOUNTED)))) {
return false;
}
if (eq.hasFlag(MiscType.F_QUAD_TURRET)
&& (!(unit instanceof QuadMech) || ((location != Mech.LOC_RT) && (location != Mech.LOC_LT)))) {
return false;
}
if (eq.hasFlag(MiscType.F_SHOULDER_TURRET)
&& (!((unit instanceof BipedMech) || (unit instanceof TripodMech))
|| ((location != Mech.LOC_RT) && (location != Mech.LOC_LT)))) {
return false;
}
//vehicular jump jets are auto-assigned to the body
if (eq.hasFlag(MiscType.F_JUMP_JET) && unit instanceof Mech) {
if (location == Mech.LOC_HEAD) {
return false;
}
if (!(unit instanceof QuadMech)
&& (location == Mech.LOC_LARM || location == Mech.LOC_RARM)) {
return false;
}
}
if (eq.hasFlag(MiscType.F_AP_POD) && unit instanceof Mech) {
if (!(unit instanceof QuadMech)
&& (location == Mech.LOC_LARM || location == Mech.LOC_RARM)) {
return false;
}
if (location != Mech.LOC_LLEG
&& location != Mech.LOC_RLEG
&& location != Mech.LOC_CLEG) {
return false;
}
}
if (eq.hasFlag(MiscType.F_MODULAR_ARMOR)) {
if (unit instanceof Mech && location == Mech.LOC_HEAD) {
return false;
}
if (unit instanceof VTOL && location == VTOL.LOC_ROTOR) {
return false;
}
}
if (eq.hasFlag(MiscType.F_CASE)) {
if (unit instanceof Mech
&& location != Mech.LOC_LT
&& location != Mech.LOC_RT
&& location != Mech.LOC_CT) {
return false;
}
if (unit instanceof Tank && location != Tank.LOC_BODY) {
return false;
}
}
if (unit instanceof Tank) {
if (location == Tank.LOC_BODY) {
//Equipment which cannot be installed in the body
if (eq.hasFlag(MiscType.F_HARJEL)
|| eq.hasFlag(MiscType.F_LIGHT_FLUID_SUCTION_SYSTEM)) {
return false;
}
} else {
//Equipment which must be installed in the body
if (eq.hasFlag(MiscType.F_CASE)) {
return false;
}
}
if (eq.hasFlag(MiscType.F_BULLDOZER) && location != Tank.LOC_FRONT) {
return false;
}
if (unit instanceof VTOL) {
/* Per Tech Manual, no equipment can be installed in the rotor, but TacOps
* allows some. This is equipment which is specifically disallowed.
*/
if (location == VTOL.LOC_ROTOR) {
if (eq.hasFlag(MiscType.F_HARJEL)
|| eq.hasFlag(MiscType.F_MODULAR_ARMOR)
|| eq.hasFlag(MiscType.F_LIGHT_FLUID_SUCTION_SYSTEM)) {
return false;
}
} else {
//Equipment which must be installed in the rotor.
if (eq.hasFlag(MiscType.F_MAST_MOUNT)) {
return false;
}
}
}
}
} else if (eq instanceof WeaponType) {
if (eq.hasFlag(WeaponType.F_VGL)) {
if ((unit instanceof Mech)
&& ((location != Mech.LOC_CT)
&& (location != Mech.LOC_RT) && (location != Mech.LOC_LT))) {
return false;
}
}
if (unit instanceof Tank) {
if (location == Tank.LOC_BODY) {
return false;
}
if (eq.hasFlag(WeaponType.F_B_POD)
//Must be mounted in side or turret
&& (location == Tank.LOC_FRONT
|| location == Tank.LOC_REAR)) {
return false;
}
}
if (unit instanceof VTOL && location == VTOL.LOC_ROTOR) {
return false;
}
}
return true;
}
public static void showValidation(Entity entity, JFrame frame) {
String sb = UnitUtil.validateUnit(entity);
if (sb.length() > 0) {
JOptionPane.showMessageDialog(frame, sb, "Unit Validation",
JOptionPane.ERROR_MESSAGE);
} else {
JOptionPane.showMessageDialog(frame, "Validation Passed",
"Unit Validation", JOptionPane.INFORMATION_MESSAGE);
}
}
public static void showUnitSpecs(Entity unit, JFrame frame) {
HTMLEditorKit kit = new HTMLEditorKit();
MechView mechView = null;
try {
mechView = new MechView(unit, true);
} catch (Exception e) {
// error unit didn't load right. this is bad news.
}
StringBuffer unitSpecs = new StringBuffer("<html><body>");
unitSpecs.append(mechView.getMechReadoutBasic());
unitSpecs.append(mechView.getMechReadoutLoadout());
unitSpecs.append("</body></html>");
// System.err.println(unitSpecs.toString());
JEditorPane textPane = new JEditorPane("text/html", "");
JScrollPane scroll = new JScrollPane();
textPane.setEditable(false);
textPane.setCaret(new DefaultCaret());
textPane.setEditorKit(kit);
scroll.setViewportView(textPane);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.getVerticalScrollBar().setUnitIncrement(20);
textPane.setText(unitSpecs.toString());
scroll.setVisible(true);
JDialog jdialog = new JDialog();
jdialog.add(scroll);
/*
* if (unit instanceof Mech) { EntityVerifier entityVerifier = new
* EntityVerifier(new File("data/mechfiles/UnitVerifierOptions.xml"));
* //$NON-NLS-1$ TestMech test = new TestMech((Mech)unit,
* entityVerifier.mechOption, null); JEditorPane pane2 = new
* JEditorPane();
* pane2.setText(test.printWeightCalculation().toString());
* jdialog.add(pane2); }
*/
Dimension size = new Dimension(CConfig.getIntParam("WINDOWWIDTH") / 2,
CConfig.getIntParam("WINDOWHEIGHT"));
jdialog.setPreferredSize(size);
jdialog.setMinimumSize(size);
scroll.setPreferredSize(size);
scroll.setMinimumSize(size);
// text.setPreferredSize(size);
jdialog.setLocationRelativeTo(frame);
jdialog.setVisible(true);
try {
textPane.setSelectionStart(0);
textPane.setSelectionEnd(0);
} catch (Exception ex) {
}
}
public static void showUnitCostBreakDown(Entity unit, JFrame frame) {
HTMLEditorKit kit = new HTMLEditorKit();
unit.calculateBattleValue(true, true);
unit.getCost(true);
JEditorPane textPane = new JEditorPane("text/html", "");
JScrollPane scroll = new JScrollPane();
textPane.setEditable(false);
textPane.setCaret(new DefaultCaret());
textPane.setEditorKit(kit);
scroll.setViewportView(textPane);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.getVerticalScrollBar().setUnitIncrement(20);
textPane.setText(unit.getBVText());
scroll.setVisible(true);
JDialog jdialog = new JDialog();
jdialog.add(scroll);
Dimension size = new Dimension(CConfig.getIntParam("WINDOWWIDTH") / 2,
CConfig.getIntParam("WINDOWHEIGHT"));
jdialog.setPreferredSize(size);
jdialog.setMinimumSize(size);
scroll.setPreferredSize(size);
scroll.setMinimumSize(size);
jdialog.setLocationRelativeTo(frame);
jdialog.setVisible(true);
try {
textPane.setSelectionStart(0);
textPane.setSelectionEnd(0);
} catch (Exception ex) {
}
}
public static void showUnitWeightBreakDown(Entity unit, JFrame frame) {
TestEntity testEntity = getEntityVerifier(unit);
JOptionPane.showMessageDialog(frame, testEntity.printEntity(),
"Unit Breakdown", JOptionPane.NO_OPTION);
}
public static void showBVCalculations(String bvText, JFrame frame) {
HTMLEditorKit kit = new HTMLEditorKit();
JEditorPane textPane = new JEditorPane("text/html", "");
JScrollPane scroll = new JScrollPane();
textPane.setEditable(false);
textPane.setCaret(new DefaultCaret());
textPane.setEditorKit(kit);
scroll.setViewportView(textPane);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scroll.getVerticalScrollBar().setUnitIncrement(20);
textPane.setText(bvText);
scroll.setVisible(true);
JDialog jdialog = new JDialog();
jdialog.add(scroll);
Dimension size = new Dimension(
(int) (CConfig.getIntParam("WINDOWWIDTH") / 1.5),
CConfig.getIntParam("WINDOWHEIGHT"));
jdialog.setPreferredSize(size);
jdialog.setMinimumSize(size);
scroll.setPreferredSize(size);
scroll.setMinimumSize(size);
// text.setPreferredSize(size);
jdialog.setLocationRelativeTo(frame);
jdialog.setVisible(true);
try {
textPane.setSelectionStart(0);
textPane.setSelectionEnd(0);
} catch (Exception ex) {
}
}
public static boolean hasBAR(Entity unit) {
for (int loc = 0; loc < unit.locations(); loc++) {
if (unit.hasBARArmor(loc)) {
return true;
}
}
return false;
}
public static int getLowestBARRating(Entity unit) {
int bar = 10;
for (int loc = 0; loc < unit.locations(); loc++) {
if (unit.getBARRating(loc) < bar) {
bar = unit.getBARRating(loc);
}
}
return bar;
}
public static String getArmorString(Mech mech, int loc) {
if (!mech.hasPatchworkArmor()) {
return "";
}
StringBuilder sb = new StringBuilder("");
switch (mech.getArmorType(loc)) {
case EquipmentType.T_ARMOR_REFLECTIVE:
sb.append("LR");
break;
case EquipmentType.T_ARMOR_HARDENED:
sb.append("HD");
break;
case EquipmentType.T_ARMOR_LIGHT_FERRO:
sb.append("LF");
break;
case EquipmentType.T_ARMOR_HEAVY_FERRO:
sb.append("HF");
break;
case EquipmentType.T_ARMOR_FERRO_FIBROUS:
case EquipmentType.T_ARMOR_FERRO_FIBROUS_PROTO:
sb.append("FF");
break;
case EquipmentType.T_ARMOR_STEALTH:
sb.append("SA");
break;
case EquipmentType.T_ARMOR_INDUSTRIAL:
sb.append("IN");
break;
case EquipmentType.T_ARMOR_COMMERCIAL:
sb.append("CO");
break;
case EquipmentType.T_ARMOR_FERRO_LAMELLOR:
sb.append("FL");
break;
case EquipmentType.T_ARMOR_REACTIVE:
sb.append("RE");
break;
default:
return "";
}
if (mech.hasBARArmor(loc)) {
sb.append(" B" + mech.getBARRating(loc));
}
return sb.toString();
}
public static boolean canUseAmmo(Entity unit, AmmoType atype) {
boolean match = false;
if ((unit instanceof BattleArmor)
&& !atype.hasFlag(AmmoType.F_BATTLEARMOR)){
return false;
}
if (!(unit instanceof BattleArmor)
&& atype.hasFlag(AmmoType.F_BATTLEARMOR)){
return false;
}
for (Mounted m : unit.getWeaponList()) {
if (m.getType() instanceof AmmoWeapon) {
WeaponType wtype = (WeaponType) m.getType();
if ((wtype.getAmmoType() == atype.getAmmoType())
&& (wtype.getRackSize() == atype.getRackSize())) {
match = true;
}
}
}
return match;
}
public static int countUsedCriticals(Mech unit) {
int nCrits = 0;
for (int i = 0; i < unit.locations(); i++) {
for (int j = 0; j < unit.getNumberOfCriticals(i); j++) {
CriticalSlot cs = unit.getCritical(i, j);
if (null != cs) {
nCrits++;
}
}
}
return nCrits + countUnallocatedCriticals(unit);
}
public static int countUnallocatedCriticals(Mech unit) {
int nCrits = 0;
int engineHeatSinkCount = UnitUtil.getCriticalFreeHeatSinks(unit,
unit.hasCompactHeatSinks());
for (Mounted mount : unit.getMisc()) {
if (UnitUtil.isHeatSink(mount)
&& (mount.getLocation() == Entity.LOC_NONE)) {
if (engineHeatSinkCount > 0) {
engineHeatSinkCount--;
continue;
}
}
if ((mount.getLocation() == Entity.LOC_NONE)) {
nCrits += UnitUtil.getCritsUsed(unit, mount.getType());
}
}
for (Mounted mount : unit.getWeaponList()) {
if (mount.getLocation() == Entity.LOC_NONE) {
nCrits += UnitUtil.getCritsUsed(unit, mount.getType());
}
}
for (Mounted mount : unit.getAmmo()) {
if ((mount.getLocation() == Entity.LOC_NONE)
&& ((mount.getUsableShotsLeft() > 1) || (((AmmoType) mount
.getType()).getAmmoType() == AmmoType.T_COOLANT_POD))) {
nCrits += UnitUtil.getCritsUsed(unit, mount.getType());
}
}
return nCrits;
}
// gives total number of sinks, not just critical slots
public static int countActualHeatSinks(Mech unit) {
int sinks = 0;
for (Mounted mounted : unit.getMisc()) {
if (!UnitUtil.isHeatSink(mounted)) {
continue;
}
if (mounted.getType().hasFlag(MiscType.F_COMPACT_HEAT_SINK)) {
if (mounted.getType().hasFlag(MiscType.F_HEAT_SINK)) {
sinks++;
} else if (mounted.getType().hasFlag(
MiscType.F_DOUBLE_HEAT_SINK)) {
sinks++;
sinks++;
}
} else {
sinks++;
}
}
return sinks;
}
public static void checkEquipmentByTechLevel(Entity unit) {
Vector<Mounted> toRemove = new Vector<Mounted>();
for (Mounted m : unit.getEquipment()) {
EquipmentType etype = m.getType();
if (UnitUtil.isArmorOrStructure(etype)
|| UnitUtil.isHeatSink(etype) || UnitUtil.isJumpJet(etype)) {
continue;
}
if (etype.hasFlag(MiscType.F_TSM)
|| etype.hasFlag(MiscType.F_INDUSTRIAL_TSM)
|| etype.hasFlag(MiscType.F_MASC)) {
continue;
}
if (!UnitUtil.isLegal(unit,
etype.getTechLevel(unit.getTechLevelYear()))) {
toRemove.add(m);
}
}
for (Mounted m : toRemove) {
UnitUtil.removeMounted(unit, m);
}
if (unit instanceof Infantry) {
Infantry pbi = (Infantry) unit;
if ((null != pbi.getPrimaryWeapon())
&& !UnitUtil.isLegal(unit, pbi.getPrimaryWeapon()
.getTechLevel(pbi.getTechLevelYear()))) {
UnitUtil.replaceMainWeapon((Infantry) unit,
(InfantryWeapon) EquipmentType
.get("Infantry Auto Rifle"), false);
}
if ((null != pbi.getSecondaryWeapon())
&& !UnitUtil.isLegal(unit, pbi.getSecondaryWeapon()
.getTechLevel(pbi.getTechLevelYear()))) {
UnitUtil.replaceMainWeapon((Infantry) unit, null, true);
}
}
}
public static void replaceMainWeapon(Infantry unit, InfantryWeapon weapon,
boolean secondary) {
Mounted existingInfantryMount = null;
for (Mounted m : unit.getWeaponList()) {
if ((m.getType() instanceof InfantryWeapon)
&& (m.getLocation() == Infantry.LOC_INFANTRY)) {
existingInfantryMount = m;
break;
}
}
if (null != existingInfantryMount) {
UnitUtil.removeMounted(unit, existingInfantryMount);
}
if (secondary) {
unit.setSecondaryWeapon(weapon);
} else {
unit.setPrimaryWeapon(weapon);
}
// if there is more than one secondary weapon per squad, then add that
// to the unit otherwise add the primary weapon (unless the secondary
// is TAG, in which case both are added.
if (unit.getSecondaryWeapon() != null && unit.getSecondaryWeapon().hasFlag(WeaponType.F_TAG)) {
try {
unit.addEquipment(unit.getPrimaryWeapon(),
Infantry.LOC_INFANTRY);
unit.addEquipment(unit.getSecondaryWeapon(),
Infantry.LOC_INFANTRY);
} catch (LocationFullException ex) {
}
} else if ((unit.getSecondaryN() < 2) || (null == unit.getSecondaryWeapon())) {
try {
unit.addEquipment(unit.getPrimaryWeapon(),
Infantry.LOC_INFANTRY);
} catch (LocationFullException ex) {
}
} else {
try {
unit.addEquipment(unit.getSecondaryWeapon(),
Infantry.LOC_INFANTRY);
} catch (LocationFullException ex) {
}
}
}
public static void replaceFieldGun(Infantry unit, WeaponType fieldGun, int num) {
List<Mounted> toRemove = unit.getEquipment().stream()
.filter(m -> m.getLocation() == Infantry.LOC_FIELD_GUNS)
.collect(Collectors.toList());
unit.getEquipment().removeAll(toRemove);
unit.getWeaponList().removeAll(toRemove);
unit.getAmmo().removeAll(toRemove);
final long munition;
if (fieldGun != null && num > 0) {
if (fieldGun.getAmmoType() == AmmoType.T_AC_LBX
|| fieldGun.getAmmoType() == AmmoType.T_AC_LBX_THB) {
munition = AmmoType.M_CLUSTER;
} else {
munition = AmmoType.M_STANDARD;
}
Optional<AmmoType> ammo = AmmoType.getMunitionsFor(fieldGun.getAmmoType()).stream()
.filter(eq -> ((AmmoType)eq).getMunitionType() == munition
&& ((AmmoType)eq).getRackSize() == fieldGun.getRackSize())
.findFirst();
if (!ammo.isPresent()) {
ammo = AmmoType.getMunitionsFor(fieldGun.getAmmoType())
.stream().findFirst();
}
for (int i = 0; i < num; i++) {
try {
unit.addEquipment(fieldGun, Infantry.LOC_FIELD_GUNS);
if (ammo.isPresent()) {
unit.addEquipment(ammo.get(), Infantry.LOC_FIELD_GUNS);
} else {
System.err.println("Could not find ammo for field gun " + fieldGun.getName());
}
} catch (LocationFullException ex) {
ex.printStackTrace();
}
}
}
}
public static String trimInfantryWeaponNames(String wname) {
return wname.replace("Infantry ", "");
}
public static void resetInfantryArmor(Infantry unit) {
unit.setArmorEncumbering(false);
unit.setSpaceSuit(false);
unit.setDEST(false);
unit.setSneakCamo(false);
unit.setSneakECM(false);
unit.setSneakIR(false);
unit.setDamageDivisor(1.0);
}
public static void removeOmniArmActuators(Mech mech) {
if ((mech instanceof BipedMech) || (mech instanceof TripodMech)) {
boolean leftACGaussPPC = false;
boolean rightACGaussPPC = false;
for (Mounted weapon : mech.getWeaponList()) {
if ((weapon.getLocation() == Mech.LOC_LARM)
&& ((weapon.getType() instanceof ACWeapon)
|| (weapon.getType() instanceof GaussWeapon)
|| (weapon.getType() instanceof LBXACWeapon)
|| (weapon.getType() instanceof UACWeapon) || (weapon
.getType() instanceof PPCWeapon))) {
leftACGaussPPC = true;
}
if ((weapon.getLocation() == Mech.LOC_RARM)
&& ((weapon.getType() instanceof ACWeapon)
|| (weapon.getType() instanceof GaussWeapon)
|| (weapon.getType() instanceof LBXACWeapon)
|| (weapon.getType() instanceof UACWeapon) || (weapon
.getType() instanceof PPCWeapon))) {
rightACGaussPPC = true;
}
}
if (leftACGaussPPC) {
removeArm(mech, Mech.LOC_LARM);
UnitUtil.compactCriticals(mech, Mech.LOC_LARM);
}
if (rightACGaussPPC) {
removeArm(mech, Mech.LOC_RARM);
UnitUtil.compactCriticals(mech, Mech.LOC_RARM);
}
}
}
public static void removeHand(Mech mech, int location) {
if (mech.hasSystem(Mech.ACTUATOR_HAND, location)) {
mech.setCritical(location, 3, null);
}
}
public static void removeArm(Mech mech, int location) {
if (mech.hasSystem(Mech.ACTUATOR_LOWER_ARM, location)) {
mech.setCritical(location, 2, null);
// Only remove the next slot of it actually is a hand
if (mech.hasSystem(Mech.ACTUATOR_HAND, location)) {
removeHand(mech, location);
}
}
}
}