Skip to content

Commit

Permalink
Merge pull request MegaMek#1704 from psikomonkie/issue-1703-infantry-…
Browse files Browse the repository at this point in the history
…bays-cannot-be-added-to-aero-fighters

Issue 1703: Allow bays to be added to aerospace fighters
  • Loading branch information
pavelbraginskiy authored Feb 15, 2025
2 parents 9354cdb + 06144b9 commit b01428c
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 155 deletions.
169 changes: 15 additions & 154 deletions megameklab/src/megameklab/ui/combatVehicle/CVTransportView.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2022 - The MegaMek Team. All Rights Reserved.
* Copyright (c) 2018-2025 - The MegaMek Team. All Rights Reserved.
*
* 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
Expand All @@ -13,183 +13,44 @@
*/
package megameklab.ui.combatVehicle;

import megamek.common.Bay;
import megamek.common.Tank;
import megamek.common.verifier.BayData;
import megameklab.ui.generalUnit.BuildView;
import megameklab.ui.generalUnit.TransportView;
import megameklab.ui.listeners.BuildListener;
import megameklab.ui.listeners.CVBuildListener;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* Panel for combat vehicle cargo and troop space.
*
*
* @author Neoancient
*/
public class CVTransportView extends BuildView implements ChangeListener {
public class CVTransportView extends TransportView {
List<CVBuildListener> listeners = new CopyOnWriteArrayList<>();
public void addListener(CVBuildListener l) {
listeners.add(l);
}
public void removeListener(CVBuildListener l) {
listeners.remove(l);
}

private final SpinnerNumberModel spnFixedTroopModel = new SpinnerNumberModel(0.0, 0.0, null, 0.5);
private final SpinnerNumberModel spnPodTroopModel = new SpinnerNumberModel(0.0, 0.0, null, 0.5);
private final Map<BayData, SpinnerNumberModel> fixedSpinnerModels = new HashMap<>();
private final Map<BayData, SpinnerNumberModel> podSpinnerModels = new HashMap<>();

private final JSpinner spnFixedTroop = new JSpinner(spnFixedTroopModel);
private final JSpinner spnPodTroop = new JSpinner(spnPodTroopModel);
private final Map<BayData, JSpinner> fixedSpinners = new HashMap<>();
private final Map<BayData, JSpinner> podSpinners = new HashMap<>();

// Track unit tonnage to set max allowed carrying space.
private double tonnage;

public CVTransportView() {
super();
initUI();
}

private void initUI() {
ResourceBundle resourceMap = ResourceBundle.getBundle("megameklab.resources.Views");

setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();

gbc.gridx = 1;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
add(createLabel(resourceMap, "lblFixed", "CVTransportView.lblFixed.text"), gbc);

gbc.gridx = 2;
gbc.gridy = 0;
add(createLabel(resourceMap, "lblPod", "CVTransportView.lblPod.text"), gbc);

gbc.gridx = 0;
gbc.gridy = 1;
add(createLabel(resourceMap, "lblTroopSpace", "CVTransportView.lblTroopSpace.text"), gbc);

gbc.gridx = 1;
add(spnFixedTroop, gbc);
spnFixedTroop.addChangeListener(this);

gbc.gridx = 2;
add(spnPodTroop, gbc);
spnPodTroop.addChangeListener(this);

for (BayData bayType : BayData.values()) {
if (!bayType.isCargoBay()) {
continue;
}
String tooltip = String.format(resourceMap.getString("CVTransportView.bay.tooltipFormat"),
1 / bayType.getWeight());
gbc.gridx = 0;
gbc.gridy++;
final JLabel lblBayType = createLabel("lbl" + bayType.name(), bayType.getDisplayName());
lblBayType.setToolTipText(tooltip);
add(lblBayType, gbc);

gbc.gridx = 1;
SpinnerNumberModel model = new SpinnerNumberModel(0.0, 0.0, null, 0.5);
JSpinner spinner = new JSpinner(model);
spinner.setToolTipText(tooltip);
spinner.setName(bayType.toString());
fixedSpinnerModels.put(bayType, model);
fixedSpinners.put(bayType, spinner);
add(spinner, gbc);
spinner.addChangeListener(this);

gbc.gridx = 2;
model = new SpinnerNumberModel(0.0, 0.0, null, 0.5);
spinner = new JSpinner(model);
spinner.setToolTipText(tooltip);
spinner.setName(bayType.toString());
podSpinnerModels.put(bayType, model);
podSpinners.put(bayType, spinner);
add(spinner, gbc);
spinner.addChangeListener(this);
@Override
public void addListener(BuildListener l) {
if (l instanceof CVBuildListener listener) {
listeners.add(listener);
}
}

public void setFromEntity(Tank tank) {
double troops = tank.getTroopCarryingSpace();
double podTroops = tank.getPodMountedTroopCarryingSpace();
spnFixedTroop.setValue(troops - podTroops);
spnPodTroop.setValue(podTroops);

Map<BayData, Double> fixedCargo = new HashMap<>();
Map<BayData, Double> podCargo = new HashMap<>();
for (Bay b : tank.getTransportBays()) {
BayData bayType = BayData.getBayType(b);
if (null != bayType) {
if (tank.isPodMountedTransport(b)) {
podCargo.merge(bayType, b.getCapacity(), Double::sum);
} else {
fixedCargo.merge(bayType, b.getCapacity(), Double::sum);
}
}
}
for (Map.Entry<BayData, JSpinner> entry : fixedSpinners.entrySet()) {
entry.getValue().setValue(fixedCargo.getOrDefault(entry.getKey(), 0.0));
}
for (Map.Entry<BayData, JSpinner> entry : podSpinners.entrySet()) {
entry.getValue().setValue(podCargo.getOrDefault(entry.getKey(), 0.0));
}
setOmni(tank.isOmni());
}

public void setTonnage(double tonnage) {
this.tonnage = tonnage;
refresh();
}

public void setOmni(boolean omni) {
spnPodTroop.setEnabled(omni);
podSpinners.values().forEach(v -> v.setEnabled(omni));
if (!omni) {
clearPodSpace();
}
}

public void clearPodSpace() {
spnPodTroop.setValue(0.0);
podSpinners.values().forEach(v -> v.setValue(0.0));
}

public void refresh() {
resetMaxSize(spnFixedTroopModel, tonnage);
resetMaxSize(spnPodTroopModel, tonnage);
fixedSpinnerModels.values().forEach(m -> resetMaxSize(m, tonnage));
podSpinnerModels.values().forEach(m -> resetMaxSize(m, tonnage));
}

private void resetMaxSize(SpinnerNumberModel model, double max) {
model.setMaximum(max);
if (model.getNumber().doubleValue() > max) {
model.removeChangeListener(this);
model.setValue(max);
model.addChangeListener(this);
@Override
public void removeListener(BuildListener l) {
if (l instanceof CVBuildListener listener) {
listeners.remove(listener);
}
}

@Override
public void stateChanged(ChangeEvent e) {
if ((e.getSource() == spnFixedTroop)
|| (e.getSource() == spnPodTroop)) {
|| (e.getSource() == spnPodTroop)) {
listeners.forEach(l -> l.troopSpaceChanged(spnFixedTroopModel.getNumber().doubleValue(),
spnPodTroopModel.getNumber().doubleValue()));
spnPodTroopModel.getNumber().doubleValue()));
} else if (e.getSource() instanceof Component) {
BayData bayType = null;
for (BayData bay : BayData.values()) {
Expand Down
67 changes: 67 additions & 0 deletions megameklab/src/megameklab/ui/fighterAero/ASStructureTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.List;
import java.util.stream.Collectors;

import javax.swing.BorderFactory;
import javax.swing.Box;
Expand All @@ -32,6 +34,7 @@
import megamek.codeUtilities.MathUtility;
import megamek.common.*;
import megamek.common.equipment.ArmorType;
import megamek.common.verifier.BayData;
import megamek.common.verifier.TestAero;
import megamek.common.verifier.TestEntity;
import megameklab.ui.EntitySource;
Expand All @@ -54,6 +57,7 @@ public class ASStructureTab extends ITab implements AeroBuildListener, ArmorAllo
private SummaryView panSummary;
private ArmorAllocationView panArmorAllocation;
private PatchworkArmorView panPatchwork;
private ASTransportView panTransport;
private IconView iconView;

RefreshListener refresh = null;
Expand All @@ -76,6 +80,7 @@ private void setUpPanels() {
panHeat = new HeatSinkView(panInfo);
panArmorAllocation = new ArmorAllocationView(panInfo, Entity.ETYPE_AERO);
panPatchwork = new PatchworkArmorView(panInfo);
panTransport = new ASTransportView();
iconView = new IconView();
if (getAero().hasPatchworkArmor()) {
panArmorAllocation.showPatchwork(true);
Expand Down Expand Up @@ -119,6 +124,7 @@ private void setUpPanels() {
midPanel.add(Box.createHorizontalStrut(300));

rightPanel.add(panArmor);
rightPanel.add(panTransport);
rightPanel.add(panArmorAllocation);
rightPanel.add(panPatchwork);

Expand All @@ -143,6 +149,7 @@ private void setUpPanels() {
panArmor.setBorder(BorderFactory.createTitledBorder("Armor"));
panArmorAllocation.setBorder(BorderFactory.createTitledBorder("Armor Allocation"));
panPatchwork.setBorder(BorderFactory.createTitledBorder("Patchwork Armor"));
panTransport.setBorder(BorderFactory.createTitledBorder("Transport"));
}

public ITechManager getTechManager() {
Expand All @@ -167,6 +174,7 @@ public void refresh() {
panArmor.setFromEntity(getAero());
panArmorAllocation.setFromEntity(getAero());
panPatchwork.setFromEntity(getAero());
panTransport.setFromEntity(getAero());
iconView.setFromEntity(getEntity());

panHeat.setVisible(!getAero().hasETypeFlag(Entity.ETYPE_CONV_FIGHTER));
Expand Down Expand Up @@ -236,6 +244,7 @@ public void removeAllListeners() {
panArmor.removeListener(this);
panArmorAllocation.removeListener(this);
panPatchwork.removeListener(this);
panTransport.removeListener(this);
}

public void addAllListeners() {
Expand All @@ -247,6 +256,7 @@ public void addAllListeners() {
panArmor.addListener(this);
panArmorAllocation.addListener(this);
panPatchwork.addListener(this);
panTransport.addListener(this);
}

public void addRefreshedListener(RefreshListener l) {
Expand Down Expand Up @@ -509,6 +519,7 @@ public void omniChanged(boolean omni) {
getAero().getEngine().setBaseChassisHeatSinks(
omni? Math.max(0, panHeat.getBaseCount()) : -1);
panHeat.setFromAero(getAero());
panTransport.setOmni(omni);
refresh.refreshPreview();
}

Expand Down Expand Up @@ -557,9 +568,65 @@ public void cockpitChanged(int cockpitType) {
@Override
public void resetChassis() {
UnitUtil.resetBaseChassis(getAero());
panTransport.clearPodSpace();
refresh.scheduleRefresh();
}

/**
* Notify of a change in the size of any infantry compartment
*
* @param fixed The weight in tons of the infantry compartment
* @param pod The weight in tons of any pod-mounted infantry compartment
*/
@Override
public void troopSpaceChanged(double fixed, double pod) {
List<Transporter> toRemove = getAero().getTransports().stream()
.filter(t -> t instanceof InfantryCompartment).collect(Collectors.toList());
toRemove.forEach(t -> getAero().removeTransporter(t));
double troopTons = Math
.round((fixed) * 2) / 2.0;
if (troopTons > 0) {
getAero().addTransporter(new InfantryCompartment(troopTons), false);
}
troopTons = Math.round(pod * 2) / 2.0;
if (troopTons > 0) {
getAero().addTransporter(new InfantryCompartment(troopTons), true);
}
panSummary.refresh();
refresh.refreshStatus();
refresh.refreshPreview();
}

/**
* Notify of a change in the size of a cargo bay
*
* @param bayType The type of bay
* @param fixed The size of a fixed bay
* @param pod The size of a pod-mounted bay
*/
@Override
public void cargoSpaceChanged(BayData bayType, double fixed, double pod) {
List<Transporter> toRemove = getAero().getTransports().stream()
.filter(t -> (t instanceof Bay)
&& (bayType == BayData.getBayType((Bay) t)))
.collect(Collectors.toList());
toRemove.forEach(t -> getAero().removeTransporter(t));
double bayTons = Math
.round((fixed) * 2) / 2.0;
int lastBay = getAero().getTransportBays().stream().mapToInt(Bay::getBayNumber).max().orElse(0);
if (bayTons > 0) {
getAero().addTransporter(bayType.newBay(bayTons, lastBay + 1), false);
lastBay++;
}
bayTons = Math.round(pod * 2) / 2.0;
if (bayTons > 0) {
getAero().addTransporter(bayType.newBay(bayTons, lastBay + 1), true);
}
panSummary.refresh();
refresh.refreshStatus();
refresh.refreshPreview();
}

@Override
public void fuelTonnageChanged(double tonnage) {
double fuelTons = Math.round(tonnage * 2) / 2.0;
Expand Down
Loading

0 comments on commit b01428c

Please sign in to comment.