Skip to content

Commit 6c117a7

Browse files
authored
Merge pull request #8484 from IllianiBird/bayRentalFix
Fix #8481: Fixed Multiple Bay Rental Dialog Bugs
2 parents e37da63 + 9908f7d commit 6c117a7

File tree

5 files changed

+132
-59
lines changed

5 files changed

+132
-59
lines changed

MekHQ/resources/mekhq/resources/FacilityRentals.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ ContractStartRentalDialog.inCharacter={0}, {1} is offering us access to several
5555
ContractStartRentalDialog.inCharacter.bay={0}, I''ve reached out to the relevant parties. We''re looking at a surcharge\
5656
\ of {1} C-Bills per week if we want to go ahead with this bay rental.\
5757
<p>Let me know if you still want to go ahead.</p>
58+
UnitBayRental.wrongContractType=You cannot rent Maintenance Bays or Factory Condition bays \
59+
unless you are off-contract, or on a garrison-type contract.\
60+
<p>Only Temporary Retainers and contracts ending in 'duty' (except Relief Duty) are classified as garrison \
61+
contracts.</p>
62+
UnitBayRental.inSpace=You cannot rent Maintenance Bays or Factory Condition bays unless you are planetside.
5863
ContractStartRentalDialog.button.cancel=Cancel
5964
ContractStartRentalDialog.button.confirm=Confirm
6065
ContractStartRentalDialog.outOfCharacter.bay=Better bays allow you to perform more complex refits (as per Campaign \

MekHQ/src/mekhq/campaign/mission/rentals/FacilityRentals.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,20 @@
4444

4545
import java.time.LocalDate;
4646
import java.util.ArrayList;
47+
import java.util.Arrays;
4748
import java.util.Collection;
4849
import java.util.List;
50+
import java.util.function.Predicate;
4951

52+
import megamek.common.units.Entity;
5053
import mekhq.MekHQ;
5154
import mekhq.campaign.Campaign;
5255
import mekhq.campaign.campaignOptions.CampaignOptions;
5356
import mekhq.campaign.events.RepairStatusChangedEvent;
5457
import mekhq.campaign.finances.Finances;
5558
import mekhq.campaign.finances.Money;
5659
import mekhq.campaign.finances.enums.TransactionType;
60+
import mekhq.campaign.mission.AtBContract;
5761
import mekhq.campaign.mission.Contract;
5862
import mekhq.campaign.mission.Mission;
5963
import mekhq.campaign.unit.Unit;
@@ -455,4 +459,72 @@ private static boolean performRentalTransaction(Finances finances, LocalDate tod
455459
return finances.debit(TransactionType.RENT, today, rentalCost,
456460
getFormattedTextAt(RESOURCE_BUNDLE, "FacilityRentals.rental." + rentalType, rentalCost.toAmountString()));
457461
}
462+
463+
/**
464+
* Processes a request to change bay assignments for the specified units, offering bay rental opportunities
465+
* if allowed by the current campaign state.
466+
* <p>
467+
* Bay rentals are only permitted if the campaign is either off-contract or on a garrison-type contract,
468+
* and the campaign's location is planetside. If these conditions are not met, a dialog is shown to the user
469+
* indicating that no facilities are available.
470+
* </p>
471+
*
472+
* @param campaign the current campaign context
473+
* @param selectedUnits the units for which bay changes are requested
474+
* @param bayType the type of bay being requested (e.g., {@link Unit#SITE_FACILITY_MAINTENANCE}, {@link Unit#SITE_FACTORY_CONDITIONS})
475+
* @return {@code true} if the bay change process can proceed; {@code false} if not allowed (e.g., due to contract or location restrictions)
476+
*/
477+
public static boolean processBayChangeRequest(Campaign campaign, Unit[] selectedUnits, int bayType) {
478+
List<AtBContract> activeAtBContracts = campaign.getActiveAtBContracts();
479+
boolean isBayRentalAllowed = activeAtBContracts.isEmpty();
480+
481+
for (AtBContract atBContract : activeAtBContracts) {
482+
if (atBContract.getContractType().isGarrisonType()) {
483+
isBayRentalAllowed = true;
484+
break;
485+
}
486+
}
487+
488+
if (!isBayRentalAllowed || !campaign.getLocation().isOnPlanet()) {
489+
BayRentalDialog.showNoFacilitiesAvailableDialog(campaign);
490+
return false;
491+
}
492+
493+
// This counts how many units are eligible and how many are eligible and a large craft. We handle
494+
// it this way to allow us to fetch the counts in a single pass
495+
Predicate<Unit> eligibleForBayRental =
496+
unit -> !FacilityRentals.shouldBeIgnoredByBayRentals(unit);
497+
long[] counts = Arrays.stream(selectedUnits)
498+
.filter(eligibleForBayRental)
499+
.collect(() -> new long[2], (results, unit) -> {
500+
if (!unit.isDeployed()) {
501+
results[0]++; // eligible
502+
Entity entity = unit.getEntity();
503+
if (entity != null && entity.isLargeCraft()) {
504+
results[1]++; // large vessel
505+
}
506+
}
507+
}, (a, b) -> {
508+
a[0] += b[0];
509+
a[1] += b[1];
510+
});
511+
512+
int eligibleUnitCount = (int) counts[0];
513+
int eligibleLargeVesselCount = (int) counts[1];
514+
515+
ContractRentalType rentalType = switch (bayType) {
516+
case Unit.SITE_FACILITY_MAINTENANCE -> ContractRentalType.MAINTENANCE_BAYS;
517+
case Unit.SITE_FACTORY_CONDITIONS -> ContractRentalType.FACTORY_CONDITIONS;
518+
default -> null; // Should never happen as we're already filtering out invalid sites
519+
};
520+
521+
if (rentalType == null) {
522+
return true; // We don't want a bug preventing bay changes.
523+
}
524+
525+
return FacilityRentals.offerBayRentalOpportunity(campaign,
526+
eligibleUnitCount,
527+
eligibleLargeVesselCount,
528+
rentalType);
529+
}
458530
}

MekHQ/src/mekhq/gui/adapter/ServicedUnitsTableMouseAdapter.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
*/
3333
package mekhq.gui.adapter;
3434

35+
import static mekhq.campaign.unit.Unit.SITE_FIELD_WORKSHOP;
36+
3537
import java.awt.event.ActionEvent;
3638
import java.util.Optional;
3739
import javax.swing.JCheckBoxMenuItem;
@@ -41,11 +43,14 @@
4143
import javax.swing.JPopupMenu;
4244
import javax.swing.JTable;
4345

46+
import megamek.codeUtilities.MathUtility;
4447
import megamek.common.equipment.AmmoType;
4548
import mekhq.MekHQ;
4649
import mekhq.Utilities;
50+
import mekhq.campaign.Campaign;
4751
import mekhq.campaign.events.RepairStatusChangedEvent;
4852
import mekhq.campaign.events.units.UnitChangedEvent;
53+
import mekhq.campaign.mission.rentals.FacilityRentals;
4954
import mekhq.campaign.parts.equipment.AmmoBin;
5055
import mekhq.campaign.unit.Unit;
5156
import mekhq.campaign.unit.actions.IUnitAction;
@@ -93,11 +98,22 @@ public void actionPerformed(ActionEvent action) {
9398
MekHQ.triggerEvent(new UnitChangedEvent(selectedUnit));
9499
}
95100
} else if (command.contains("CHANGE_SITE")) {
96-
for (Unit unit : units) {
97-
if (!unit.isDeployed()) {
98-
String sel = command.split(":")[1];
99-
int selected = Integer.parseInt(sel);
100-
if ((selected > -1) && (selected < Unit.SITE_UNKNOWN)) {
101+
int selected = MathUtility.parseInt(command.split(":")[1], SITE_FIELD_WORKSHOP);
102+
boolean selectedIsValid = selected > -1 && selected < Unit.SITE_UNKNOWN;
103+
if (!selectedIsValid) {
104+
return;
105+
}
106+
107+
boolean wasSiteChangeSuccessful = true;
108+
Campaign campaign = gui.getCampaign();
109+
if (selected >= Unit.SITE_FACILITY_MAINTENANCE &&
110+
campaign.getCampaignOptions().getRentedFacilitiesCostRepairBays() > 0) {
111+
wasSiteChangeSuccessful = FacilityRentals.processBayChangeRequest(campaign, units, selected);
112+
}
113+
114+
if (wasSiteChangeSuccessful) {
115+
for (Unit unit : units) {
116+
if (!unit.isDeployed()) {
101117
unit.setSite(selected);
102118
MekHQ.triggerEvent(new RepairStatusChangedEvent(unit));
103119
}
@@ -157,7 +173,7 @@ protected Optional<JPopupMenu> createPopupMenu() {
157173
JCheckBoxMenuItem cbMenuItem;
158174
// **let's fill the pop-up menu**//
159175
// change the location
160-
JMenu menu = new JMenu("Change site");
176+
JMenu menu = new JMenu("Change Site");
161177
int i;
162178
for (i = 0; i < Unit.SITE_UNKNOWN; i++) {
163179
cbMenuItem = new JCheckBoxMenuItem(Unit.getSiteName(i));

MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import static mekhq.campaign.enums.DailyReportType.TECHNICAL;
4242
import static mekhq.campaign.market.personnelMarket.enums.PersonnelMarketStyle.MEKHQ;
4343
import static mekhq.campaign.personnel.PersonUtility.overrideSkills;
44+
import static mekhq.campaign.unit.Unit.SITE_FIELD_WORKSHOP;
4445

4546
import java.awt.event.ActionEvent;
4647
import java.awt.event.MouseEvent;
@@ -49,13 +50,11 @@
4950
import java.io.OutputStream;
5051
import java.io.PrintStream;
5152
import java.util.ArrayList;
52-
import java.util.Arrays;
5353
import java.util.List;
5454
import java.util.Optional;
5555
import java.util.ResourceBundle;
5656
import java.util.UUID;
5757
import java.util.Vector;
58-
import java.util.function.Predicate;
5958
import java.util.stream.Stream;
6059
import javax.swing.JCheckBoxMenuItem;
6160
import javax.swing.JMenu;
@@ -68,6 +67,7 @@
6867
import megamek.client.ui.dialogs.UnitEditorDialog;
6968
import megamek.client.ui.dialogs.abstractDialogs.BVDisplayDialog;
7069
import megamek.client.ui.dialogs.iconChooser.CamoChooserDialog;
70+
import megamek.codeUtilities.MathUtility;
7171
import megamek.common.annotations.Nullable;
7272
import megamek.common.enums.SkillLevel;
7373
import megamek.common.equipment.AmmoType;
@@ -99,7 +99,6 @@
9999
import mekhq.campaign.finances.Money;
100100
import mekhq.campaign.finances.enums.TransactionType;
101101
import mekhq.campaign.mission.Scenario;
102-
import mekhq.campaign.mission.rentals.ContractRentalType;
103102
import mekhq.campaign.mission.rentals.FacilityRentals;
104103
import mekhq.campaign.parts.Part;
105104
import mekhq.campaign.parts.Refit;
@@ -321,59 +320,26 @@ public void actionPerformed(ActionEvent action) {
321320
MekHQ.triggerEvent(new UnitChangedEvent(selectedUnit));
322321
}
323322
} else if (command.contains(COMMAND_CHANGE_SITE)) {
324-
try {
325-
int selected = Integer.parseInt(command.split(":")[1]);
326-
boolean selectedIsValid = selected > -1 && selected < Unit.SITE_UNKNOWN;
327-
if (!selectedIsValid) {
328-
return;
329-
}
323+
int selected = MathUtility.parseInt(command.split(":")[1], SITE_FIELD_WORKSHOP);
324+
boolean selectedIsValid = selected > -1 && selected < Unit.SITE_UNKNOWN;
325+
if (!selectedIsValid) {
326+
return;
327+
}
330328

331-
boolean wasSiteChangeSuccessful = true;
332-
333-
if (selected >= Unit.SITE_FACILITY_MAINTENANCE) {
334-
Campaign campaign = gui.getCampaign();
335-
336-
// This counts how many units are eligible and how many are eligible and a large craft. We handle
337-
// it this way to allow us to fetch the counts in a single pass
338-
Predicate<Unit> eligibleForBayRental =
339-
u -> !FacilityRentals.shouldBeIgnoredByBayRentals(u);
340-
long[] counts = Arrays.stream(units)
341-
.filter(eligibleForBayRental)
342-
.collect(() -> new long[2], (arr, u) -> {
343-
arr[0]++; // eligible
344-
if (u.getEntity().isLargeCraft()) {
345-
arr[1]++; // large vessel
346-
}
347-
}, (a, b) -> {
348-
a[0] += b[0];
349-
a[1] += b[1];
350-
});
351-
352-
int eligibleUnitCount = (int) counts[0];
353-
int eligibleLargeVesselCount = (int) counts[1];
354-
355-
ContractRentalType rentalType = switch (selected) {
356-
case Unit.SITE_FACILITY_MAINTENANCE -> ContractRentalType.MAINTENANCE_BAYS;
357-
case Unit.SITE_FACTORY_CONDITIONS -> ContractRentalType.FACTORY_CONDITIONS;
358-
default -> null; // Should never happen as we're already filtering out invalid sites
359-
};
360-
361-
wasSiteChangeSuccessful = FacilityRentals.offerBayRentalOpportunity(campaign,
362-
eligibleUnitCount,
363-
eligibleLargeVesselCount,
364-
rentalType);
365-
}
329+
boolean wasSiteChangeSuccessful = true;
330+
Campaign campaign = gui.getCampaign();
331+
if (selected >= Unit.SITE_FACILITY_MAINTENANCE &&
332+
campaign.getCampaignOptions().getRentedFacilitiesCostRepairBays() > 0) {
333+
wasSiteChangeSuccessful = FacilityRentals.processBayChangeRequest(campaign, units, selected);
334+
}
366335

367-
if (wasSiteChangeSuccessful) {
368-
for (Unit unit : units) {
369-
if (!unit.isDeployed()) {
370-
unit.setSite(selected);
371-
MekHQ.triggerEvent(new RepairStatusChangedEvent(unit));
372-
}
336+
if (wasSiteChangeSuccessful) {
337+
for (Unit unit : units) {
338+
if (!unit.isDeployed()) {
339+
unit.setSite(selected);
340+
MekHQ.triggerEvent(new RepairStatusChangedEvent(unit));
373341
}
374342
}
375-
} catch (Exception e) {
376-
LOGGER.error("", e);
377343
}
378344
} else if (command.equals(COMMAND_SALVAGE)) {
379345
for (Unit unit : units) {
@@ -852,7 +818,7 @@ protected Optional<JPopupMenu> createPopupMenu() {
852818
// endregion Determine if to Display
853819

854820
// change the location
855-
menu = new JMenu("Change site");
821+
menu = new JMenu("Change Site");
856822
boolean allSameSite = StaticChecks.areAllSameSite(units);
857823

858824
for (int i = 0; i < Unit.SITE_UNKNOWN; i++) {

MekHQ/src/mekhq/gui/dialog/BayRentalDialog.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
import mekhq.campaign.Campaign;
4141
import mekhq.campaign.finances.Money;
42+
import mekhq.gui.baseComponents.immersiveDialogs.ImmersiveDialogNotification;
4243
import mekhq.gui.baseComponents.immersiveDialogs.ImmersiveDialogSimple;
4344
import mekhq.gui.baseComponents.immersiveDialogs.ImmersiveDialogWidth;
4445

@@ -91,4 +92,17 @@ private static List<String> getButtons() {
9192
private static String getOutOfCharacterMessage() {
9293
return getFormattedTextAt(RESOURCE_BUNDLE, "ContractStartRentalDialog.outOfCharacter.bay");
9394
}
95+
96+
public static void showNoFacilitiesAvailableDialog(Campaign campaign) {
97+
boolean isInSpace = !campaign.getLocation().isOnPlanet();
98+
99+
String message;
100+
if (isInSpace) {
101+
message = getTextAt(RESOURCE_BUNDLE, "UnitBayRental.inSpace");
102+
} else {
103+
message = getTextAt(RESOURCE_BUNDLE, "UnitBayRental.wrongContractType");
104+
}
105+
106+
new ImmersiveDialogNotification(campaign, message, true);
107+
}
94108
}

0 commit comments

Comments
 (0)