Skip to content

Commit 1dabb25

Browse files
Allow locking the base chassis of an Omni-mek
1 parent dc9c7c1 commit 1dabb25

23 files changed

+286
-52
lines changed

megameklab/resources/megameklab/resources/Views.properties

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ MekChassisView.spnTonnage.text=Tonnage:
3333
MekChassisView.spnTonnage.tooltip=Value between 10 and 200 in 5 ton increments. Superheavy (over 100) is only available with IS/Advanced.
3434
MekChassisView.chkOmni.text=Omni
3535
MekChassisView.chkOmni.tooltip=Omni units mount equipment in pods that can be swapped quickly. Not available to Industrial or LAMs.
36+
MekChassisView.chkOmniLock.text=Lock Chassis
37+
MekChassisView.chkOmniLock.tooltip=Make the base chassis read-only so you can only modify Omni pods.
3638
MekChassisView.cbBaseType.text=Base Type:
3739
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>
3840
MekChassisView.cbMotiveType.text=Motive Type:

megameklab/src/megameklab/ui/EntitySource.java

+16
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,20 @@ default void createNewUnit(long entitytype, Entity oldUnit) {
113113
* of tech.
114114
*/
115115
ITechManager getTechManager();
116+
117+
/**
118+
* 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.
119+
* No enforcement is provided, you should call {@link EntitySource#canModifyBaseChassis} to determine if non-modifications should be allowed.
120+
* @param locked {@code true} if the entity's base chassis should be treated as read-only
121+
*/
122+
default void setBaseChassisModifiable(boolean locked) {}
123+
124+
/**
125+
* Determine if you should allow modifications to an omni-unit's base chassis.
126+
* This is not enforced, be careful.
127+
* @return {@code true} if you can modify the entity's base chassis.
128+
*/
129+
default boolean canModifyBaseChassis() {
130+
return true;
131+
}
116132
}

megameklab/src/megameklab/ui/generalUnit/AbstractEquipmentTab.java

+30-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.awt.FlowLayout;
2121
import java.awt.GridLayout;
2222
import java.awt.event.ActionEvent;
23+
import java.util.Arrays;
2324
import java.util.List;
2425

2526
import javax.swing.BorderFactory;
@@ -30,6 +31,8 @@
3031
import javax.swing.JSplitPane;
3132
import javax.swing.JTable;
3233
import javax.swing.ListSelectionModel;
34+
import javax.swing.event.ListSelectionEvent;
35+
import javax.swing.event.ListSelectionListener;
3336
import javax.swing.table.TableColumn;
3437

3538
import megamek.client.ui.swing.util.UIUtil;
@@ -61,8 +64,10 @@
6164
* @author neoancient
6265
* @author Simon (Juliez)
6366
*/
64-
public abstract class AbstractEquipmentTab extends ITab {
67+
public abstract class AbstractEquipmentTab extends ITab implements ListSelectionListener {
6568
private static final MMLogger logger = MMLogger.create(AbstractEquipmentTab.class);
69+
private final JButton removeButton;
70+
private final JButton removeAllButton;
6671

6772
private RefreshListener refresh;
6873

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

98-
JButton removeButton = new JButton("Remove");
103+
removeButton = new JButton("Remove");
99104
removeButton.setMnemonic('R');
100-
JButton removeAllButton = new JButton("Remove All");
105+
removeAllButton = new JButton("Remove All");
101106
removeAllButton.setMnemonic('l');
102107
removeButton.addActionListener(this::removeSelectedEquipment);
103108
removeAllButton.addActionListener(this::removeAllEquipment);
@@ -129,6 +134,8 @@ public Dimension getMinimumSize() {
129134
pane.setOneTouchExpandable(true);
130135
setLayout(new BorderLayout());
131136
add(pane, BorderLayout.CENTER);
137+
138+
loadOutTable.getSelectionModel().addListSelectionListener(this);
132139
}
133140

134141
public void addRefreshedListener(RefreshListener l) {
@@ -201,6 +208,12 @@ public void refresh() {
201208
loadOutModel.removeAllCrits();
202209
getLoadOut().forEach(loadOutModel::addCrit);
203210
fireTableRefresh();
211+
212+
if (!eSource.canModifyBaseChassis()) {
213+
removeAllButton.setEnabled(
214+
loadOutModel.getCrits().stream().allMatch(Mounted::isOmniPodMounted)
215+
);
216+
}
204217
}
205218

206219
public void refreshTable() {
@@ -222,4 +235,18 @@ private void refreshOtherTabs() {
222235
refresh.refreshSummary();
223236
}
224237
}
238+
239+
@Override
240+
public void valueChanged(ListSelectionEvent e) {
241+
if (e.getSource() == loadOutTable.getSelectionModel()) {
242+
if (eSource.canModifyBaseChassis()) {
243+
return;
244+
}
245+
246+
var hasFixed = Arrays.stream(loadOutTable.getSelectionModel().getSelectedIndices())
247+
.mapToObj(i -> loadOutModel.getCrits().get(i))
248+
.anyMatch(m -> !m.isOmniPodMounted());
249+
removeButton.setEnabled(!hasFixed);
250+
}
251+
}
225252
}

megameklab/src/megameklab/ui/generalUnit/ArmorAllocationView.java

+8
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,12 @@ public void showPatchwork(boolean show) {
347347
public void armorPointsChanged(int location, int front, int rear) {
348348
listeners.forEach(l -> l.armorPointsChanged(location, front, rear));
349349
}
350+
351+
public void omniLock(boolean unlocked) {
352+
for (ArmorLocationView locView : locationViews) {
353+
locView.omniLock(unlocked);
354+
}
355+
btnAutoAllocate.setEnabled(unlocked);
356+
panLocations.setEnabled(unlocked);
357+
}
350358
}

megameklab/src/megameklab/ui/generalUnit/ArmorLocationView.java

+23-18
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
/**
3333
* Panel used to set armor value for a single location. Optionally used for rear location as well,
3434
* and can be used to set the armor type for units with patchwork armor.
35-
*
35+
*
3636
* @author Neoancient
3737
*/
3838
public class ArmorLocationView extends BuildView implements ChangeListener {
@@ -46,22 +46,22 @@ public void addListener(ArmorLocationListener l) {
4646
public void removeListener(ArmorLocationListener l) {
4747
listeners.remove(l);
4848
}
49-
49+
5050
private final SpinnerNumberModel spnPointsModel = new SpinnerNumberModel(0, 0, null, 1);
5151
private final SpinnerNumberModel spnPointsRearModel = new SpinnerNumberModel(0, 0, null, 1);
5252
private final JSpinner spnPoints = new JSpinner(spnPointsModel);
5353
private final JSpinner spnPointsRear = new JSpinner(spnPointsRearModel);
5454
private final JLabel lblRear = new JLabel();
5555
private final JLabel lblMaxPoints = new JLabel();
56-
56+
5757
private final int location;
5858
private final String maxFormat;
5959
private Integer maxPoints;
6060
private boolean hasRear = false;
61-
61+
6262
ArmorLocationView(int location) {
6363
this.location = location;
64-
64+
6565
ResourceBundle resourceMap = ResourceBundle.getBundle("megameklab.resources.Views");
6666
lblRear.setText(resourceMap.getString("ArmorLocationView.lblRear.text"));
6767
maxFormat = resourceMap.getString("ArmorLocationView.lblMax.format");
@@ -80,12 +80,12 @@ public void removeListener(ArmorLocationListener l) {
8080
add(spnPointsRear, gbc);
8181
gbc.gridy++;
8282
gbc.weighty = 1.0;
83-
add(lblMaxPoints, gbc);
83+
add(lblMaxPoints, gbc);
8484
}
85-
85+
8686
/**
8787
* Changes the location name in the title and whether it has a rear armor location.
88-
*
88+
*
8989
* @param locName
9090
* @param rear
9191
*/
@@ -98,18 +98,18 @@ public void updateLocation(String locName, boolean rear) {
9898
spnPointsRear.setValue(0);
9999
}
100100
}
101-
101+
102102
/**
103103
* @return The index (LOC_* constant) of the location managed by this view.
104104
*/
105105
public int getLocationIndex() {
106106
return location;
107107
}
108-
108+
109109
/**
110110
* Sets the maximum number of armor points that can be assigned to this location.
111111
* A value of null indicates that there is no maximum.
112-
*
112+
*
113113
* @param max
114114
*/
115115
public void setMaxPoints(@Nullable Integer max) {
@@ -123,17 +123,17 @@ public void setMaxPoints(@Nullable Integer max) {
123123
lblMaxPoints.setText(String.format(maxFormat, max));
124124
}
125125
}
126-
126+
127127
public void setMinimum(int minimum) {
128128
spnPointsModel.setMinimum(minimum);
129129
if (getPoints() < minimum) {
130130
spnPointsModel.setValue(minimum);
131131
}
132132
}
133-
133+
134134
/**
135135
* Sets the number of points for this location. If the location has rear armor, this sets only the front.
136-
*
136+
*
137137
* @param points
138138
*/
139139
public void setPoints(int points) {
@@ -148,7 +148,7 @@ public void setPoints(int points) {
148148
}
149149
spnPoints.addChangeListener(this);
150150
}
151-
151+
152152
/**
153153
* @return The number of points of armor for this location (front).
154154
*/
@@ -158,7 +158,7 @@ public int getPoints() {
158158

159159
/**
160160
* Sets the number of points of armor for this location in the rear.
161-
*
161+
*
162162
* @param points
163163
*/
164164
public void setPointsRear(int points) {
@@ -173,17 +173,22 @@ public void setPointsRear(int points) {
173173
}
174174
spnPointsRear.addChangeListener(this);
175175
}
176-
176+
177177
/**
178178
* @return The number of points of rear armor in this location.
179179
*/
180180
public int getPointsRear() {
181181
return spnPointsRearModel.getNumber().intValue();
182182
}
183-
183+
184184
@Override
185185
public void stateChanged(ChangeEvent e) {
186186
listeners.forEach(l -> l.armorPointsChanged(location, getPoints(), getPointsRear()));
187187
}
188188

189+
public void omniLock(boolean unlocked) {
190+
spnPoints.setEnabled(unlocked);
191+
spnPointsRear.setEnabled(unlocked);
192+
}
193+
189194
}

megameklab/src/megameklab/ui/generalUnit/BasicInfoView.java

+8
Original file line numberDiff line numberDiff line change
@@ -619,4 +619,12 @@ private void openMUL() {
619619
JOptionPane.showMessageDialog(this, ex.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE);
620620
}
621621
}
622+
623+
public void omniLock(boolean unlocked) {
624+
// If we allow these to change, they might change to a value where the base chassis is invalid, forcing it to change.
625+
// By locking them when the base chassis is locked, we force the user to be careful around these fields.
626+
txtYear.setEnabled(unlocked);
627+
cbTechBase.setEnabled(unlocked);
628+
cbTechLevel.setEnabled(unlocked);
629+
}
622630
}

megameklab/src/megameklab/ui/generalUnit/HeatSinkView.java

+5
Original file line numberDiff line numberDiff line change
@@ -391,4 +391,9 @@ private void reportChange() {
391391
}
392392
}
393393

394+
public void omniLock(boolean unlocked) {
395+
cbHSType.setEnabled(unlocked);
396+
spnBaseCount.setEnabled(unlocked);
397+
}
398+
394399
}

megameklab/src/megameklab/ui/generalUnit/MVFArmorView.java

+8
Original file line numberDiff line numberDiff line change
@@ -375,4 +375,12 @@ public void actionPerformed(ActionEvent e) {
375375
}
376376
}
377377

378+
public void omniLock(boolean unlocked) {
379+
cbArmorType.setEnabled(unlocked);
380+
spnTonnage.setEnabled(unlocked);
381+
chkPatchwork.setEnabled(unlocked);
382+
btnMaximize.setEnabled(unlocked);
383+
btnUseRemaining.setEnabled(unlocked);
384+
}
385+
378386
}

megameklab/src/megameklab/ui/generalUnit/MovementView.java

+26-5
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ public void removeListener(BuildListener l) {
8989
private String[] runNames;
9090
private String[] jumpNames;
9191

92+
private int baseChassisJets;
93+
private int lockedMinJump = 0;
94+
9295
public MovementView(ITechManager techManager) {
9396
this.techManager = techManager;
9497
initUI();
@@ -184,12 +187,14 @@ public void setFromEntity(Entity en) {
184187
industrial = (en instanceof Mek) && ((Mek) en).isIndustrial();
185188
refresh();
186189

187-
Optional<MiscType> jj = en.getMisc().stream().map(Mounted::getType)
188-
.filter(eq -> eq.hasFlag(MiscType.F_JUMP_JET) || eq.hasFlag(MiscType.F_UMU)
189-
|| eq.hasFlag(MiscType.F_JUMP_BOOSTER)).findAny();
190-
if (jj.isPresent()) {
190+
List<MiscMounted> jets = en.getMisc().stream().filter(m -> {
191+
var type = m.getType();
192+
return type.hasFlag(MiscType.F_JUMP_JET) || type.hasFlag(MiscType.F_UMU) || type.hasFlag(MiscType.F_JUMP_BOOSTER);
193+
}).toList();
194+
195+
if (!jets.isEmpty()) {
191196
cbJumpType.removeActionListener(this);
192-
cbJumpType.setSelectedItem(jj.get());
197+
cbJumpType.setSelectedItem(jets.get(0).getType());
193198
cbJumpType.addActionListener(this);
194199
}
195200
// LAMs have a minimum jump MP of 3, which implies a minimum walk
@@ -289,6 +294,7 @@ public void setFromEntity(Entity en) {
289294
jump = jump0;
290295
}
291296
}
297+
minJump = Math.max(minJump, lockedMinJump);
292298
spnJumpModel.setMinimum(minJump);
293299
spnJumpModel.setMaximum(maxJump);
294300
spnJump.setValue(Math.max(minJump, jump0));
@@ -316,6 +322,8 @@ public void setFromEntity(Entity en) {
316322
} else if (jump0 < minJump) {
317323
spnJump.setValue(spnJumpModel.getMinimum());
318324
}
325+
326+
baseChassisJets = (int) jets.stream().filter(m -> !m.isOmniPodMounted()).count();
319327
}
320328

321329
public void refresh() {
@@ -433,4 +441,17 @@ public void actionPerformed(ActionEvent e) {
433441
listeners.forEach(l -> l.jumpTypeChanged(getJumpJet()));
434442
}
435443
}
444+
445+
public void omniLock(boolean unlocked) {
446+
spnWalk.setEnabled(unlocked);
447+
if (unlocked) {
448+
lockedMinJump = 0;
449+
spnJumpModel.setMinimum(0);
450+
} else {
451+
lockedMinJump = baseChassisJets;
452+
spnJumpModel.setMinimum(baseChassisJets);
453+
}
454+
spnJump.setModel(spnJumpModel);
455+
cbJumpType.setEnabled(unlocked || baseChassisJets == 0);
456+
}
436457
}

megameklab/src/megameklab/ui/generalUnit/PatchworkArmorView.java

+6
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,10 @@ public void actionPerformed(ActionEvent e) {
147147
}
148148
}
149149

150+
public void omniLock(boolean unlocked) {
151+
for (var i : combos) {
152+
i.setEnabled(unlocked);
153+
}
154+
}
155+
150156
}

megameklab/src/megameklab/ui/listeners/MekBuildListener.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818

1919
/**
2020
* Listener for views used by Meks.
21-
*
21+
*
2222
* @author Neoancient
2323
*
2424
*/
2525
public interface MekBuildListener extends BuildListener {
2626
void tonnageChanged(double tonnage);
2727
void omniChanged(boolean omni);
28+
void omniLockChanged(boolean omniLock);
2829
void typeChanged(int baseType, int motiveType, long etype);
2930
void structureChanged(EquipmentType structure);
3031
void engineChanged(Engine engine);

0 commit comments

Comments
 (0)