Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow locking the base chassis of an Omnimek #1712

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions megameklab/resources/megameklab/resources/Views.properties
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ MekChassisView.spnTonnage.text=Tonnage:
MekChassisView.spnTonnage.tooltip=Value between 10 and 200 in 5 ton increments. Superheavy (over 100) is only available with IS/Advanced.
MekChassisView.chkOmni.text=Omni
MekChassisView.chkOmni.tooltip=Omni units mount equipment in pods that can be swapped quickly. Not available to Industrial or LAMs.
MekChassisView.chkOmniLock.text=Lock Chassis
MekChassisView.chkOmniLock.tooltip=Make the base chassis read-only so you can only modify Omni pods.
MekChassisView.cbBaseType.text=Base Type:
MekChassisView.cbBaseType.tooltip=<html>LAMs are only available to an IS tech base. QuadVees are only available to a Clan tech base.<br/>Resets unit except when switching between standard and industrial.</html>
MekChassisView.cbMotiveType.text=Motive Type:
Expand Down
16 changes: 16 additions & 0 deletions megameklab/src/megameklab/ui/EntitySource.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,20 @@ default void createNewUnit(long entitytype, Entity oldUnit) {
* of tech.
*/
ITechManager getTechManager();

/**
* Mark the entity provided by this EntitySource a having a read-only chassis, so that the only changes that can be made are to omni pod space.
* No enforcement is provided, you should call {@link EntitySource#canModifyBaseChassis} to determine if non-modifications should be allowed.
* @param locked {@code true} if the entity's base chassis should be treated as read-only
*/
default void setBaseChassisModifiable(boolean locked) {}

/**
* Determine if you should allow modifications to an omni-unit's base chassis.
* This is not enforced, be careful.
* @return {@code true} if you can modify the entity's base chassis.
*/
default boolean canModifyBaseChassis() {
return true;
}
}
33 changes: 30 additions & 3 deletions megameklab/src/megameklab/ui/generalUnit/AbstractEquipmentTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import java.util.List;

import javax.swing.BorderFactory;
Expand All @@ -30,6 +31,8 @@
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;

import megamek.client.ui.swing.util.UIUtil;
Expand Down Expand Up @@ -61,8 +64,10 @@
* @author neoancient
* @author Simon (Juliez)
*/
public abstract class AbstractEquipmentTab extends ITab {
public abstract class AbstractEquipmentTab extends ITab implements ListSelectionListener {
private static final MMLogger logger = MMLogger.create(AbstractEquipmentTab.class);
private final JButton removeButton;
private final JButton removeAllButton;

private RefreshListener refresh;

Expand Down Expand Up @@ -95,9 +100,9 @@ public AbstractEquipmentTab(EntitySource eSource) {
equipmentScroll.setViewportView(loadOutTable);
getLoadOut().forEach(loadOutModel::addCrit);

JButton removeButton = new JButton("Remove");
removeButton = new JButton("Remove");
removeButton.setMnemonic('R');
JButton removeAllButton = new JButton("Remove All");
removeAllButton = new JButton("Remove All");
removeAllButton.setMnemonic('l');
removeButton.addActionListener(this::removeSelectedEquipment);
removeAllButton.addActionListener(this::removeAllEquipment);
Expand Down Expand Up @@ -129,6 +134,8 @@ public Dimension getMinimumSize() {
pane.setOneTouchExpandable(true);
setLayout(new BorderLayout());
add(pane, BorderLayout.CENTER);

loadOutTable.getSelectionModel().addListSelectionListener(this);
}

public void addRefreshedListener(RefreshListener l) {
Expand Down Expand Up @@ -201,6 +208,12 @@ public void refresh() {
loadOutModel.removeAllCrits();
getLoadOut().forEach(loadOutModel::addCrit);
fireTableRefresh();

if (!eSource.canModifyBaseChassis()) {
removeAllButton.setEnabled(
loadOutModel.getCrits().stream().allMatch(Mounted::isOmniPodMounted)
);
}
}

public void refreshTable() {
Expand All @@ -222,4 +235,18 @@ private void refreshOtherTabs() {
refresh.refreshSummary();
}
}

@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getSource() == loadOutTable.getSelectionModel()) {
if (eSource.canModifyBaseChassis()) {
return;
}

var hasFixed = Arrays.stream(loadOutTable.getSelectionModel().getSelectedIndices())
.mapToObj(i -> loadOutModel.getCrits().get(i))
.anyMatch(m -> !m.isOmniPodMounted());
removeButton.setEnabled(!hasFixed);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,12 @@ public void showPatchwork(boolean show) {
public void armorPointsChanged(int location, int front, int rear) {
listeners.forEach(l -> l.armorPointsChanged(location, front, rear));
}

public void omniLock(boolean unlocked) {
for (ArmorLocationView locView : locationViews) {
locView.omniLock(unlocked);
}
btnAutoAllocate.setEnabled(unlocked);
panLocations.setEnabled(unlocked);
}
}
41 changes: 23 additions & 18 deletions megameklab/src/megameklab/ui/generalUnit/ArmorLocationView.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
/**
* Panel used to set armor value for a single location. Optionally used for rear location as well,
* and can be used to set the armor type for units with patchwork armor.
*
*
* @author Neoancient
*/
public class ArmorLocationView extends BuildView implements ChangeListener {
Expand All @@ -46,22 +46,22 @@ public void addListener(ArmorLocationListener l) {
public void removeListener(ArmorLocationListener l) {
listeners.remove(l);
}

private final SpinnerNumberModel spnPointsModel = new SpinnerNumberModel(0, 0, null, 1);
private final SpinnerNumberModel spnPointsRearModel = new SpinnerNumberModel(0, 0, null, 1);
private final JSpinner spnPoints = new JSpinner(spnPointsModel);
private final JSpinner spnPointsRear = new JSpinner(spnPointsRearModel);
private final JLabel lblRear = new JLabel();
private final JLabel lblMaxPoints = new JLabel();

private final int location;
private final String maxFormat;
private Integer maxPoints;
private boolean hasRear = false;

ArmorLocationView(int location) {
this.location = location;

ResourceBundle resourceMap = ResourceBundle.getBundle("megameklab.resources.Views");
lblRear.setText(resourceMap.getString("ArmorLocationView.lblRear.text"));
maxFormat = resourceMap.getString("ArmorLocationView.lblMax.format");
Expand All @@ -80,12 +80,12 @@ public void removeListener(ArmorLocationListener l) {
add(spnPointsRear, gbc);
gbc.gridy++;
gbc.weighty = 1.0;
add(lblMaxPoints, gbc);
add(lblMaxPoints, gbc);
}

/**
* Changes the location name in the title and whether it has a rear armor location.
*
*
* @param locName
* @param rear
*/
Expand All @@ -98,18 +98,18 @@ public void updateLocation(String locName, boolean rear) {
spnPointsRear.setValue(0);
}
}

/**
* @return The index (LOC_* constant) of the location managed by this view.
*/
public int getLocationIndex() {
return location;
}

/**
* Sets the maximum number of armor points that can be assigned to this location.
* A value of null indicates that there is no maximum.
*
*
* @param max
*/
public void setMaxPoints(@Nullable Integer max) {
Expand All @@ -123,17 +123,17 @@ public void setMaxPoints(@Nullable Integer max) {
lblMaxPoints.setText(String.format(maxFormat, max));
}
}

public void setMinimum(int minimum) {
spnPointsModel.setMinimum(minimum);
if (getPoints() < minimum) {
spnPointsModel.setValue(minimum);
}
}

/**
* Sets the number of points for this location. If the location has rear armor, this sets only the front.
*
*
* @param points
*/
public void setPoints(int points) {
Expand All @@ -148,7 +148,7 @@ public void setPoints(int points) {
}
spnPoints.addChangeListener(this);
}

/**
* @return The number of points of armor for this location (front).
*/
Expand All @@ -158,7 +158,7 @@ public int getPoints() {

/**
* Sets the number of points of armor for this location in the rear.
*
*
* @param points
*/
public void setPointsRear(int points) {
Expand All @@ -173,17 +173,22 @@ public void setPointsRear(int points) {
}
spnPointsRear.addChangeListener(this);
}

/**
* @return The number of points of rear armor in this location.
*/
public int getPointsRear() {
return spnPointsRearModel.getNumber().intValue();
}

@Override
public void stateChanged(ChangeEvent e) {
listeners.forEach(l -> l.armorPointsChanged(location, getPoints(), getPointsRear()));
}

public void omniLock(boolean unlocked) {
spnPoints.setEnabled(unlocked);
spnPointsRear.setEnabled(unlocked);
}

}
8 changes: 8 additions & 0 deletions megameklab/src/megameklab/ui/generalUnit/BasicInfoView.java
Original file line number Diff line number Diff line change
Expand Up @@ -619,4 +619,12 @@ private void openMUL() {
JOptionPane.showMessageDialog(this, ex.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE);
}
}

public void omniLock(boolean unlocked) {
// If we allow these to change, they might change to a value where the base chassis is invalid, forcing it to change.
// By locking them when the base chassis is locked, we force the user to be careful around these fields.
txtYear.setEnabled(unlocked);
cbTechBase.setEnabled(unlocked);
cbTechLevel.setEnabled(unlocked);
}
}
5 changes: 5 additions & 0 deletions megameklab/src/megameklab/ui/generalUnit/HeatSinkView.java
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,9 @@ private void reportChange() {
}
}

public void omniLock(boolean unlocked) {
cbHSType.setEnabled(unlocked);
spnBaseCount.setEnabled(unlocked);
}

}
8 changes: 8 additions & 0 deletions megameklab/src/megameklab/ui/generalUnit/MVFArmorView.java
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,12 @@ public void actionPerformed(ActionEvent e) {
}
}

public void omniLock(boolean unlocked) {
cbArmorType.setEnabled(unlocked);
spnTonnage.setEnabled(unlocked);
chkPatchwork.setEnabled(unlocked);
btnMaximize.setEnabled(unlocked);
btnUseRemaining.setEnabled(unlocked);
}

}
31 changes: 26 additions & 5 deletions megameklab/src/megameklab/ui/generalUnit/MovementView.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ public void removeListener(BuildListener l) {
private String[] runNames;
private String[] jumpNames;

private int baseChassisJets;
private int lockedMinJump = 0;

public MovementView(ITechManager techManager) {
this.techManager = techManager;
initUI();
Expand Down Expand Up @@ -184,12 +187,14 @@ public void setFromEntity(Entity en) {
industrial = (en instanceof Mek) && ((Mek) en).isIndustrial();
refresh();

Optional<MiscType> jj = en.getMisc().stream().map(Mounted::getType)
.filter(eq -> eq.hasFlag(MiscType.F_JUMP_JET) || eq.hasFlag(MiscType.F_UMU)
|| eq.hasFlag(MiscType.F_JUMP_BOOSTER)).findAny();
if (jj.isPresent()) {
List<MiscMounted> jets = en.getMisc().stream().filter(m -> {
var type = m.getType();
return type.hasFlag(MiscType.F_JUMP_JET) || type.hasFlag(MiscType.F_UMU) || type.hasFlag(MiscType.F_JUMP_BOOSTER);
}).toList();

if (!jets.isEmpty()) {
cbJumpType.removeActionListener(this);
cbJumpType.setSelectedItem(jj.get());
cbJumpType.setSelectedItem(jets.get(0).getType());
cbJumpType.addActionListener(this);
}
// LAMs have a minimum jump MP of 3, which implies a minimum walk
Expand Down Expand Up @@ -289,6 +294,7 @@ public void setFromEntity(Entity en) {
jump = jump0;
}
}
minJump = Math.max(minJump, lockedMinJump);
spnJumpModel.setMinimum(minJump);
spnJumpModel.setMaximum(maxJump);
spnJump.setValue(Math.max(minJump, jump0));
Expand Down Expand Up @@ -316,6 +322,8 @@ public void setFromEntity(Entity en) {
} else if (jump0 < minJump) {
spnJump.setValue(spnJumpModel.getMinimum());
}

baseChassisJets = (int) jets.stream().filter(m -> !m.isOmniPodMounted()).count();
}

public void refresh() {
Expand Down Expand Up @@ -433,4 +441,17 @@ public void actionPerformed(ActionEvent e) {
listeners.forEach(l -> l.jumpTypeChanged(getJumpJet()));
}
}

public void omniLock(boolean unlocked) {
spnWalk.setEnabled(unlocked);
if (unlocked) {
lockedMinJump = 0;
spnJumpModel.setMinimum(0);
} else {
lockedMinJump = baseChassisJets;
spnJumpModel.setMinimum(baseChassisJets);
}
spnJump.setModel(spnJumpModel);
cbJumpType.setEnabled(unlocked || baseChassisJets == 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,10 @@ public void actionPerformed(ActionEvent e) {
}
}

public void omniLock(boolean unlocked) {
for (var i : combos) {
i.setEnabled(unlocked);
}
}

}
3 changes: 2 additions & 1 deletion megameklab/src/megameklab/ui/listeners/MekBuildListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@

/**
* Listener for views used by Meks.
*
*
* @author Neoancient
*
*/
public interface MekBuildListener extends BuildListener {
void tonnageChanged(double tonnage);
void omniChanged(boolean omni);
void omniLockChanged(boolean omniLock);
void typeChanged(int baseType, int motiveType, long etype);
void structureChanged(EquipmentType structure);
void engineChanged(Engine engine);
Expand Down
Loading