Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,15 @@ private static void addBayAmmoList(WeaponMounted mounted, TableElement wpnTable)

static boolean hideMisc(MiscMounted mounted, Entity entity) {
String name = mounted.getName();
// Check if this is a cockpit modification that should always be shown (e.g., DNI, EI Interface)
boolean isCockpitModification = mounted.getType().hasFlag(MiscType.F_DNI_COCKPIT_MOD)
|| mounted.getType().hasFlag(MiscType.F_EI_INTERFACE)
|| mounted.getType().hasFlag(MiscType.F_DAMAGE_INTERRUPT_CIRCUIT);
return (((mounted.getLocation() == Entity.LOC_NONE)
// Meks can have zero-slot equipment in LOC_NONE that needs to be shown.
&& (!(entity instanceof Mek) || mounted.getNumCriticalSlots() > 0)))
// Also show cockpit modifications for all unit types.
&& (!(entity instanceof Mek) || mounted.getNumCriticalSlots() > 0)
&& !isCockpitModification))
|| name.contains("Jump Jet")
|| (name.contains("CASE") && !name.contains("II") && entity.isClan())
|| (name.contains("Heat Sink") && !name.contains("Radical"))
Expand Down
8 changes: 1 addition & 7 deletions megamek/src/megamek/common/cost/MekCostCalculator.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
public class MekCostCalculator {

public static double calculateCost(Mek mek, CalculationReport costReport, boolean ignoreAmmo) {
double[] costs = new double[18 + mek.locations()];
double[] costs = new double[17 + mek.locations()];
int i = 0;

double cockpitCost = switch (mek.getCockpitType()) {
Expand Down Expand Up @@ -119,12 +119,6 @@ public static double calculateCost(Mek mek, CalculationReport costReport, boolea
// cost of sinks
costs[i++] = sinkCost * (mek.heatSinks() - freeSinks);
costs[i++] = mek.hasFullHeadEject() ? 1725000 : 0;
// Damage Interrupt Circuit (IO p.39) costs 150 C-bills per pilot seat
int dicCost = 0;
if (mek.hasDamageInterruptCircuit() && (mek.getCrew() != null)) {
dicCost = 150 * mek.getCrew().getCrewType().getCrewSlots();
}
costs[i++] = dicCost;
// armored components
int armoredCrits = 0;
for (int j = 0; j < mek.locations(); j++) {
Expand Down
15 changes: 12 additions & 3 deletions megamek/src/megamek/common/equipment/MiscType.java
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,13 @@ public double getCost(Entity entity, boolean isArmored, int loc, double size) {
costValue = size * 10000;
} else if (hasFlag(F_RAM_PLATE)) {
costValue = getTonnage(entity, loc) * 10000;
} else if (hasFlag(F_DAMAGE_INTERRUPT_CIRCUIT)) {
// DIC costs 150 C-bills per pilot seat (IO p.39)
if (entity.getCrew() != null) {
costValue = 150 * entity.getCrew().getCrewType().getCrewSlots();
} else {
costValue = 150; // Default to 1 seat if no crew assigned
}
}

if (isArmored) {
Expand Down Expand Up @@ -4726,7 +4733,8 @@ public static MiscType createDNICockpitModification() {
.setISAdvancement(3052, 3055, DATE_NONE, DATE_NONE, DATE_NONE)
.setISApproximate(false, false, false, false, false)
.setPrototypeFactions(Faction.FS)
.setProductionFactions(Faction.WB);
.setProductionFactions(Faction.WB)
.setStaticTechLevel(SimpleTechLevel.ADVANCED);
return misc;
}

Expand All @@ -4742,7 +4750,7 @@ public static MiscType createDamageInterruptCircuit() {

misc.tonnage = 0;
misc.criticalSlots = 0;
misc.cost = 150; // 150 C-bills per pilot seat (handled in cost calculator)
misc.cost = EquipmentType.COST_VARIABLE; // 150 C-bills per pilot seat (IO p.39)
misc.hittable = false;
misc.flags = misc.flags.or(F_MEK_EQUIPMENT, F_DAMAGE_INTERRUPT_CIRCUIT);

Expand All @@ -4754,7 +4762,8 @@ public static MiscType createDamageInterruptCircuit() {
.setAvailability(AvailabilityValue.X, AvailabilityValue.X, AvailabilityValue.F, AvailabilityValue.F)
.setISAdvancement(3055, DATE_NONE, DATE_NONE, DATE_NONE, DATE_NONE)
.setISApproximate(true, false, false, false, false)
.setPrototypeFactions(Faction.LC);
.setPrototypeFactions(Faction.LC)
.setStaticTechLevel(SimpleTechLevel.EXPERIMENTAL);
return misc;
}

Expand Down
18 changes: 11 additions & 7 deletions megamek/src/megamek/common/interfaces/ITechManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,22 @@ && unofficialNoYear()) {
Faction faction = getTechFaction();
boolean clanTech = useClanTechBase();

// Use getGameYear() for availability checks - this respects the "Use Game Year" setting
// which allows equipment available by the configured game year, not just the unit's intro year
int yearForAvailability = getGameYear();

int isIntroDate = tech.getIntroductionDate(false);
int clanIntroDate = tech.getIntroductionDate(true);
boolean introducedIS = (isIntroDate != ITechnology.DATE_NONE) && (isIntroDate <= getTechIntroYear());
boolean introducedClan = (clanIntroDate != ITechnology.DATE_NONE) && (clanIntroDate <= getTechIntroYear());
boolean extinctIS = tech.isExtinct(getTechIntroYear(), false);
boolean extinctClan = tech.isExtinct(getTechIntroYear(), true);
boolean introducedIS = (isIntroDate != ITechnology.DATE_NONE) && (isIntroDate <= yearForAvailability);
boolean introducedClan = (clanIntroDate != ITechnology.DATE_NONE) && (clanIntroDate <= yearForAvailability);
boolean extinctIS = tech.isExtinct(yearForAvailability, false);
boolean extinctClan = tech.isExtinct(yearForAvailability, true);
// A little bit of hard-coded universe detail
if ((faction == Faction.CS)
&& extinctIS && (isIntroDate != ITechnology.DATE_NONE)
&& (tech.getBaseAvailability(ITechnology.getTechEra(getTechIntroYear())).getIndex()
&& (tech.getBaseAvailability(ITechnology.getTechEra(yearForAvailability)).getIndex()
< AvailabilityValue.X.getIndex())
&& isIntroDate <= getTechIntroYear()) {
&& isIntroDate <= yearForAvailability) {
// ComStar has access to Star League tech that is otherwise extinct in the Inner Sphere as if TH,
// unless it has an availability of X (which is SLDF Royal equipment).
extinctIS = false;
Expand All @@ -138,7 +142,7 @@ && unofficialNoYear()) {
if (useMixedTech()) {
if ((!introducedIS && !introducedClan)
|| (!showExtinct()
&& (tech.isExtinct(getTechIntroYear())))) {
&& (tech.isExtinct(yearForAvailability)))) {
return false;
} else if (useVariableTechLevel()) {
// If using tech progression with mixed tech, we pass if either IS or Clan meets the required level
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public Entity getEntity() throws EntityLoadingException {

a.setArmorTonnage(a.getArmorWeight());
loadQuirks(a);
loadSlotlessEquipment(a);
return a;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ public Entity getEntity() throws EntityLoadingException {
}
t.setArmorTonnage(t.getArmorWeight());
loadQuirks(t);
loadSlotlessEquipment(t);
return t;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ public Entity getEntity() throws EntityLoadingException {

a.setArmorTonnage(a.getArmorWeight());
loadQuirks(a);
loadSlotlessEquipment(a);
return a;
}

Expand Down
52 changes: 52 additions & 0 deletions megamek/src/megamek/common/loaders/BLKFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public class BLKFile {
private static final int TECH_CLAN_BASE = 1 << 1;

static final String BLK_EXTRA_SEATS = "extra_seats";
static final String BLK_SLOTLESS_EQUIPMENT = "slotless_equipment";

/**
* If a vehicular grenade launcher does not have a facing provided, assign a default facing. The vehicle location
Expand Down Expand Up @@ -346,6 +347,44 @@ protected void loadEquipment(Entity t, String sName, int nLoc) throws EntityLoad
}
}

/**
* Loads slotless equipment (equipment at LOC_NONE) such as cockpit modifications.
* This equipment has no location but still needs to be saved and loaded.
*
* @param t the entity to load equipment into
* @throws EntityLoadingException if the equipment cannot be loaded
*/
protected void loadSlotlessEquipment(Entity t) throws EntityLoadingException {
String[] saEquip = dataFile.getDataAsString(BLK_SLOTLESS_EQUIPMENT);
if (saEquip == null) {
return;
}

String prefix = t.isClan() ? "Clan " : "IS ";

for (String s : saEquip) {
if (s == null || s.isBlank()) {
continue;
}
String equipName = s.trim();

EquipmentType etype = EquipmentType.get(equipName);
if (etype == null) {
etype = EquipmentType.get(prefix + equipName);
}

if (etype != null) {
try {
t.addEquipment(etype, Entity.LOC_NONE);
} catch (LocationFullException ex) {
throw new EntityLoadingException(ex.getMessage());
}
} else if (!equipName.isBlank()) {
t.addFailedEquipment(equipName);
}
}
}

protected void loadSVArmor(Entity sv) throws EntityLoadingException {
boolean patchworkArmor = dataFile.exists("armor_type") &&
dataFile.getDataAsInt("armor_type")[0] == EquipmentType.T_ARMOR_PATCHWORK;
Expand Down Expand Up @@ -875,6 +914,19 @@ public static BuildingBlock getBlock(Entity t) throws EntitySavingException {
for (int i = 0; i < numLocs; i++) {
blk.writeBlockData(t.getLocationName(i) + " Equipment", eq.get(i));
}

// Write slotless equipment (LOC_NONE) - e.g., cockpit modifications like DNI
Vector<String> slotlessEquipment = new Vector<>();
for (Mounted<?> m : t.getEquipment()) {
if (m.getLocation() == Entity.LOC_NONE && !m.isWeaponGroup() && !m.isAPMMounted()
&& !(m.getType() instanceof InfantryAttack)
&& !(m.getType() instanceof BayWeapon)) {
slotlessEquipment.add(encodeEquipmentLine(m));
}
}
if (!slotlessEquipment.isEmpty()) {
blk.writeBlockData(BLK_SLOTLESS_EQUIPMENT, slotlessEquipment);
}
if (!t.hasPatchworkArmor() && ArmorType.forEntity(t).hasFlag(MiscType.F_SUPPORT_VEE_BAR_ARMOR)) {
blk.writeBlockData("barrating", t.getBARRating(1));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ public Entity getEntity() throws EntityLoadingException {
}

loadQuirks(a);
loadSlotlessEquipment(a);
return a;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ public Entity getEntity() throws EntityLoadingException {
}
}
loadQuirks(t);
loadSlotlessEquipment(t);

resetCrew(t);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ public Entity getEntity() throws EntityLoadingException {
}
}
loadQuirks(t);
loadSlotlessEquipment(t);

resetCrew(t);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ public Entity getEntity() throws EntityLoadingException {
t.setBaseChassisFireConWeight((dataFile.getDataAsDouble("baseChassisFireConWeight")[0]));
}
loadQuirks(t);
loadSlotlessEquipment(t);
return t;
}

Expand Down
1 change: 1 addition & 0 deletions megamek/src/megamek/common/loaders/BLKTankFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ public Entity getEntity() throws EntityLoadingException {
}
t.recalculateTechAdvancement();
loadQuirks(t);
loadSlotlessEquipment(t);

resetCrew(t);

Expand Down
1 change: 1 addition & 0 deletions megamek/src/megamek/common/loaders/BLKVTOLFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ public Entity getEntity() throws EntityLoadingException {
t.setBaseChassisSponsonPintleWeight(dataFile.getDataAsDouble("baseChassisSponsonPintleWeight")[0]);
}
loadQuirks(t);
loadSlotlessEquipment(t);
return t;
}
}
12 changes: 12 additions & 0 deletions megamek/src/megamek/common/templates/AeroTROView.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import megamek.common.Messages;
import megamek.common.equipment.AmmoMounted;
import megamek.common.equipment.AmmoType;
import megamek.common.equipment.EquipmentType;
import megamek.common.equipment.MiscType;
import megamek.common.equipment.Mounted;
import megamek.common.equipment.WeaponMounted;
Expand Down Expand Up @@ -327,4 +328,15 @@ protected void addCrewEntry(String stringKey, int count) {
}
}

@Override
protected boolean skipMount(Mounted<?> mount, boolean includeAmmo) {
if (mount.getLocation() == Entity.LOC_NONE) {
// Skip armor, structure, and CASE. Show cockpit modifications like DNI.
return mount.getType().hasFlag(MiscType.F_CASE)
|| EquipmentType.isArmorType(mount.getType())
|| EquipmentType.isStructureType(mount.getType());
}
return super.skipMount(mount, includeAmmo);
}

}
10 changes: 9 additions & 1 deletion megamek/src/megamek/common/templates/BattleArmorTROView.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,17 @@ private int addBAEquipment() {
int nameWidth = 30;
for (final Mounted<?> m : ba.getEquipment()) {
if (m.isAPMMounted() || (m.getType() instanceof InfantryAttack)
|| (m.getType() == armor) || (m.getLocation() == BattleArmor.LOC_NONE)) {
|| (m.getType() == armor)) {
continue;
}
// Skip LOC_NONE equipment except for cockpit modifications like DNI and EI
if (m.getLocation() == BattleArmor.LOC_NONE) {
if (!(m.getType() instanceof MiscType) ||
(!m.getType().hasFlag(MiscType.F_DNI_COCKPIT_MOD) &&
!m.getType().hasFlag(MiscType.F_EI_INTERFACE))) {
continue;
}
}
if ((m.getType() instanceof MiscType) && m.getType().hasFlag(MiscType.F_BA_MANIPULATOR)) {
continue;
}
Expand Down
11 changes: 11 additions & 0 deletions megamek/src/megamek/common/templates/SupportVeeTROView.java
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,15 @@ protected int addEquipment(Entity entity, boolean includeAmmo) {
return nameWidth;
}

@Override
protected boolean skipMount(Mounted<?> mount, boolean includeAmmo) {
if (mount.getLocation() == Entity.LOC_NONE) {
// Skip armor, structure, and CASE. Show cockpit modifications like DNI.
return mount.getType().hasFlag(MiscType.F_CASE)
|| EquipmentType.isArmorType(mount.getType())
|| EquipmentType.isStructureType(mount.getType());
}
return super.skipMount(mount, includeAmmo);
}

}
13 changes: 13 additions & 0 deletions megamek/src/megamek/common/templates/VehicleTROView.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
import java.util.Map;

import megamek.common.Messages;
import megamek.common.equipment.EquipmentType;
import megamek.common.equipment.GunEmplacement;
import megamek.common.equipment.MiscType;
import megamek.common.equipment.Mounted;
import megamek.common.equipment.Transporter;
import megamek.common.units.Entity;
Expand Down Expand Up @@ -211,4 +213,15 @@ protected int addEquipment(Entity entity, boolean includeAmmo) {
}
return retVal;
}

@Override
protected boolean skipMount(Mounted<?> mount, boolean includeAmmo) {
if (mount.getLocation() == Entity.LOC_NONE) {
// Skip armor, structure, and CASE. Show cockpit modifications like DNI.
return mount.getType().hasFlag(MiscType.F_CASE)
|| EquipmentType.isArmorType(mount.getType())
|| EquipmentType.isStructureType(mount.getType());
}
return super.skipMount(mount, includeAmmo);
}
}
Loading