Skip to content

Commit 1285fe4

Browse files
ryanbarlow97claude
andcommitted
Add a faction vehicle pool and limit battles to faction vehicles
Leaders can put vehicles into the faction pool with /faction vehicle transfer pool, using the same right-click and owner consent as an installation transfer. Pool vehicles are owned by the leader in VehicleFramework, the faction bank pays their upkeep (shown as Vehicle Upkeep on the ledger), and the military menu lists them with artillery used against artillery slots. Vehicles that belong in an installation are refused, and so is artillery once every slot is used. In a campaign battle, pool vehicles are allowed for their own side, installation vehicles only when their installation is in play, and personal or unregistered vehicles never. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent a9fb685 commit 1285fe4

33 files changed

Lines changed: 1195 additions & 42 deletions

‎src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ public class SimpleFactions extends JavaPlugin{
165165
new InstallationVehicleOwnerSync(vehicleRegistry);
166166
private final InstallationVehicleService installationVehicleService =
167167
new InstallationVehicleService(vehicleRegistry, installationVehicleOwnerSync);
168+
private final net.tfminecraft.simplefactions.vehicles.pool.FactionVehiclePoolService factionVehiclePoolService =
169+
new net.tfminecraft.simplefactions.vehicles.pool.FactionVehiclePoolService(
170+
vehicleRegistry, installationVehicleOwnerSync);
168171
private final InstallationVehicleUnberthService installationVehicleUnberthService =
169172
new InstallationVehicleUnberthService(vehicleRegistry);
170173
private final VehicleTransferSessionManager vehicleTransferSessionManager =
@@ -176,8 +179,8 @@ public class SimpleFactions extends JavaPlugin{
176179
private final VehicleTransferConsentService vehicleTransferConsentService =
177180
new VehicleTransferConsentService(
178181
installationVehicleService,
179-
vehicleRegistry,
180-
vehicleTransferSessionManager);
182+
vehicleTransferSessionManager,
183+
factionVehiclePoolService);
181184
private final VehicleRegistryClaimService vehicleRegistryClaimService =
182185
new VehicleRegistryClaimService(vehicleRegistry);
183186
private final VehicleRegistryClaimListener vehicleRegistryClaimListener =
@@ -187,7 +190,8 @@ public class SimpleFactions extends JavaPlugin{
187190
vehicleTransferSessionManager,
188191
vehicleRegistry,
189192
installationVehicleService,
190-
vehicleTransferConsentService);
193+
vehicleTransferConsentService,
194+
factionVehiclePoolService);
191195
private final VehicleSpawnListener vehicleSpawnListener =
192196
new VehicleSpawnListener(installationVehicleOwnerSync);
193197
private final BattleVehicleEligibilityService.Listener battleVehicleEligibilityListener =

‎src/main/java/net/tfminecraft/simplefactions/guild/income/Cashflow.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public enum Cashflow {
4141
UPGRADES_UPKEEP("#c46054Upgrades Upkeep", true, false),
4242
PENALTIES("#c74c3fPenalties", true, false),
4343
INSTALLATIONS("#706964Installations", true, false),
44+
VEHICLE_UPKEEP("#a6659fVehicle Upkeep", true, false),
4445
MILITARY_UPKEEP("#a6659fMilitary Upkeep", true, false),
4546
NODES("#8a7a5cNodes", true, false);
4647

‎src/main/java/net/tfminecraft/simplefactions/guild/income/Ledger.java‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import java.util.Set;
88
import java.util.function.ToDoubleFunction;
99

10+
import net.tfminecraft.simplefactions.vehicles.pool.FactionVehiclePoolService;
11+
1012
import org.bukkit.Bukkit;
1113

1214
import net.tfminecraft.simplefactions.SimpleFactions;
@@ -56,6 +58,13 @@ public static void setNodeUpkeepLookup(ToDoubleFunction<Guild> lookup) {
5658
nodeUpkeepLookup = lookup == null ? guild -> 0.0 : lookup;
5759
}
5860

61+
// Pool vehicle upkeep is withdrawn in VehicleUpkeepService. This lookup is the ledger line.
62+
private static ToDoubleFunction<String> vehiclePoolUpkeep = FactionVehiclePoolService::dailyUpkeepOf;
63+
64+
public static void setVehiclePoolUpkeepForTests(ToDoubleFunction<String> lookup) {
65+
vehiclePoolUpkeep = lookup == null ? FactionVehiclePoolService::dailyUpkeepOf : lookup;
66+
}
67+
5968
public Ledger(Guild guild) {
6069
this.guild = guild;
6170
}
@@ -280,6 +289,12 @@ public double getIncome(Cashflow cashflow) {
280289
amount -= InstallationConfigLoader.getDailyUpkeep(installation.getKind());
281290
}
282291
break;
292+
case VEHICLE_UPKEEP:
293+
if (!guild.isBase()) {
294+
return 0;
295+
}
296+
amount = -vehiclePoolUpkeep.applyAsDouble(f.getId());
297+
break;
283298
case MILITARY_UPKEEP:
284299
if (guild.isBase() && f.getMilitary() != null) {
285300
amount = -f.getMilitary().getTotalUpkeep();
@@ -447,6 +462,7 @@ public double getNetIncome() {
447462
case TRADE_UPKEEP:
448463
case UPGRADES_UPKEEP:
449464
case INSTALLATIONS:
465+
case VEHICLE_UPKEEP:
450466
case MILITARY_UPKEEP:
451467
case NODES:
452468
case PENALTIES:
@@ -518,6 +534,7 @@ public double getDividendBase() {
518534
case TRADE_UPKEEP:
519535
case UPGRADES_UPKEEP:
520536
case INSTALLATIONS:
537+
case VEHICLE_UPKEEP:
521538
case MILITARY_UPKEEP:
522539
case NODES:
523540
case PENALTIES:
@@ -794,7 +811,8 @@ private void applySettlementFor(Cashflow cf, DailyGuildTransfers buffer) {
794811
// TRADE is paid here and nowhere else, so the bank moves by what the ledger shows.
795812
case TRADE:
796813
case TRADE_UPKEEP:
797-
// INSTALLATIONS: withdrawn in Faction.newDay(), so getIncome() is ledger GUI display only
814+
// INSTALLATIONS: withdrawn in Faction.newDay(), so getIncome() is ledger GUI display only.
815+
// VEHICLE_UPKEEP: withdrawn in VehicleUpkeepService for the faction pool.
798816
case UPGRADES_UPKEEP:
799817
case PENALTIES:
800818
case CITIZENS:

‎src/main/java/net/tfminecraft/simplefactions/managers/inventory/MilitaryCreator.java‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.ArrayList;
44
import java.util.List;
5+
import java.util.Set;
56

67
import org.bukkit.Material;
78
import org.bukkit.NamespacedKey;
@@ -18,6 +19,11 @@
1819
import net.tfminecraft.simplefactions.mercenary.company.MercenaryCompany;
1920
import net.tfminecraft.simplefactions.objects.Faction;
2021
import net.tfminecraft.simplefactions.utils.Formatter;
22+
import net.tfminecraft.simplefactions.vehicles.maintenance.VehicleMaintenanceStore;
23+
import net.tfminecraft.simplefactions.vehicles.pool.FactionVehiclePoolLore;
24+
import net.tfminecraft.simplefactions.vehicles.pool.FactionVehiclePoolService;
25+
import net.tfminecraft.simplefactions.vehicles.registry.PlayerVehicleRecord;
26+
import net.tfminecraft.simplefactions.vehicles.registry.PlayerVehicleRegistry;
2127
import net.tfminecraft.simplefactions.enums.FactionModifiers;
2228
import net.tfminecraft.simplefactions.keys.Keys;
2329
import net.tfminecraft.tlibs.TLibs;
@@ -182,4 +188,31 @@ public ItemStack createRegimentDecreaseButton(Faction f, Regiment r) {
182188
i.setItemMeta(meta);
183189
return i;
184190
}
191+
192+
@SuppressWarnings("deprecation")
193+
public ItemStack createVehiclePoolIcon(Faction f) {
194+
ItemStack item = new ItemStack(Material.MINECART, 1);
195+
ItemMeta meta = item.getItemMeta();
196+
meta.setDisplayName("§eVehicle Pool");
197+
PlayerVehicleRegistry registry = SimpleFactions.plugin == null
198+
? null
199+
: SimpleFactions.getVehicleRegistry();
200+
List<PlayerVehicleRecord> pool = registry == null || f == null
201+
? List.of()
202+
: registry.getPoolVehicles(f.getId());
203+
Set<String> unpaid = Set.of();
204+
SimpleFactions plugin = SimpleFactions.getInstance();
205+
if (plugin != null) {
206+
VehicleMaintenanceStore store = plugin.getVehicleMaintenanceStore();
207+
if (store != null) {
208+
unpaid = store.unpaidUuids();
209+
}
210+
}
211+
meta.setLore(FactionVehiclePoolLore.lines(
212+
pool,
213+
FactionVehiclePoolService.artillerySlots(f),
214+
unpaid));
215+
item.setItemMeta(meta);
216+
return item;
217+
}
185218
}

‎src/main/java/net/tfminecraft/simplefactions/managers/inventory/MilitaryView.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public void militaryView(Inventory i, Player player, Faction f, boolean open) {
7474
}
7575

7676
}
77+
i.setItem(49, creator.createVehiclePoolIcon(f));
7778
i.setItem(53, inv.createBackButton(SFGUI.MILITARY_VIEW));
7879
if(open) player.openInventory(i);
7980
}

‎src/main/java/net/tfminecraft/simplefactions/objects/request/VehicleTransferConsentRequest.java‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public final class VehicleTransferConsentRequest extends Request {
1212
private final String vehicleTypeId;
1313
private final UUID ownerUuid;
1414
private final UUID proposerLeaderUuid;
15+
private final boolean pool;
1516

1617
public VehicleTransferConsentRequest(
1718
Guild sender,
@@ -21,13 +22,27 @@ public VehicleTransferConsentRequest(
2122
String vehicleTypeId,
2223
UUID ownerUuid,
2324
UUID proposerLeaderUuid) {
25+
this(sender, installationId, installationName, vehicleUuid, vehicleTypeId,
26+
ownerUuid, proposerLeaderUuid, false);
27+
}
28+
29+
public VehicleTransferConsentRequest(
30+
Guild sender,
31+
String installationId,
32+
String installationName,
33+
String vehicleUuid,
34+
String vehicleTypeId,
35+
UUID ownerUuid,
36+
UUID proposerLeaderUuid,
37+
boolean pool) {
2438
super(sender);
2539
this.installationId = installationId;
2640
this.installationName = installationName;
2741
this.vehicleUuid = vehicleUuid;
2842
this.vehicleTypeId = vehicleTypeId;
2943
this.ownerUuid = ownerUuid;
3044
this.proposerLeaderUuid = proposerLeaderUuid;
45+
this.pool = pool;
3146
this.time = System.currentTimeMillis()
3247
+ InstallationConfigLoader.getTransferRequestTimeoutSeconds() * 1000L;
3348
}
@@ -55,4 +70,8 @@ public UUID getOwnerUuid() {
5570
public UUID getProposerLeaderUuid() {
5671
return proposerLeaderUuid;
5772
}
73+
74+
public boolean isPool() {
75+
return pool;
76+
}
5877
}

‎src/main/java/net/tfminecraft/simplefactions/utils/TabCompletion.java‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,12 @@ else if(cmd.getName().equalsIgnoreCase("faction")
237237
args.length >= 2 ? args[1] : "");
238238
}
239239
if(args.length == 3 && args[1].equalsIgnoreCase("transfer")) {
240-
return completeInstallationIds(p, args[2], false);
240+
List<String> ids = completeInstallationIds(p, args[2], false);
241+
String prefix = args[2] == null ? "" : args[2];
242+
if ("pool".startsWith(prefix.toLowerCase(java.util.Locale.ROOT))) {
243+
ids.add(0, "pool");
244+
}
245+
return ids;
241246
}
242247
if(args.length == 4 && args[1].equalsIgnoreCase("maintenance") && args[2].equalsIgnoreCase("pay")) {
243248
return VehicleTabCompletions.filter(List.of("bank"), args[3]);

‎src/main/java/net/tfminecraft/simplefactions/vehicles/VehicleFactionCommands.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import net.tfminecraft.simplefactions.vehicles.maintenance.VehicleMaintenancePayService.PaymentSource;
99

1010
import net.tfminecraft.simplefactions.vehicles.berth.VehicleTransferSessionManager.VehicleTransferSession;
11+
import net.tfminecraft.simplefactions.vehicles.pool.FactionVehiclePoolService;
1112
import net.tfminecraft.simplefactions.vehicles.berth.VehicleInstallationLockService;
1213
import net.tfminecraft.simplefactions.vehicles.berth.VehicleTransferMessages;
1314
import net.tfminecraft.simplefactions.vehicles.maintenance.VehicleMaintenancePaySessionManager.VehicleMaintenancePaySession;
@@ -34,6 +35,10 @@ public static void armTransfer(Player player, String installationId) {
3435
player.sendMessage(VehicleMaintenanceMessages.transferUsage());
3536
return;
3637
}
38+
if (FactionVehiclePoolService.isPoolTarget(installationId)) {
39+
armPoolTransfer(player);
40+
return;
41+
}
3742
Installation installation = faction.getInstallationHandler().getById(installationId);
3843
if (installation == null) {
3944
player.sendMessage(VehicleTransferMessages.unknownInstallation());
@@ -56,6 +61,16 @@ public static void armTransfer(Player player, String installationId) {
5661
player.sendMessage(VehicleTransferMessages.commandArmed(installation));
5762
}
5863

64+
private static void armPoolTransfer(Player player) {
65+
SimpleFactions plugin = SimpleFactions.getInstance();
66+
long timeoutMillis = InstallationConfigLoader.getTransferRequestTimeoutSeconds() * 1000L;
67+
plugin.getVehicleMaintenancePaySessionManager().clear(player.getUniqueId());
68+
plugin.getVehicleTransferSessionManager().put(
69+
player.getUniqueId(),
70+
new VehicleTransferSession(null, System.currentTimeMillis() + timeoutMillis, true));
71+
player.sendMessage(VehicleTransferMessages.poolCommandArmed());
72+
}
73+
5974
public static void armMaintenancePay(Player player) {
6075
armMaintenancePay(player, PaymentSource.POUCH);
6176
}

‎src/main/java/net/tfminecraft/simplefactions/vehicles/battle/BattleVehicleEligibilityService.java‎

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ public final class BattleVehicleEligibilityService {
3131
private BattleVehicleEligibilityService() {}
3232

3333
public static boolean isEligible(War war, String factionId, PlayerVehicleRecord record) {
34-
if (war == null || factionId == null || factionId.isBlank() || record == null) {
34+
if (war == null || factionId == null || factionId.isBlank()) {
3535
return true;
3636
}
37+
if (record == null) {
38+
return false;
39+
}
3740
return isEligible(war, factionId, record.getVehicleTypeId(), record);
3841
}
3942

@@ -42,17 +45,44 @@ public static boolean isEligible(
4245
if (war == null || factionId == null || factionId.isBlank()) {
4346
return true;
4447
}
45-
if (!VehicleCategoryRules.isBerthableType(vehicleTypeId)) {
46-
return true;
48+
return decide(war, factionId, vehicleTypeId, berthRecord) == BattleVehicleEligibilityResult.ALLOWED;
49+
}
50+
51+
static BattleVehicleEligibilityResult decide(
52+
War war, String factionId, String vehicleTypeId, PlayerVehicleRecord record) {
53+
if (record != null && record.getMode() == OwnershipMode.POOL) {
54+
return poolOnPlayerSide(war, factionId, record.getFactionId())
55+
? BattleVehicleEligibilityResult.ALLOWED
56+
: BattleVehicleEligibilityResult.DENIED_POOL_SIDE;
4757
}
48-
if (berthRecord == null || berthRecord.getMode() != OwnershipMode.INSTALLATION) {
58+
if (record != null && record.getMode() == OwnershipMode.INSTALLATION) {
59+
if (!VehicleCategoryRules.isBerthableType(vehicleTypeId)) {
60+
return BattleVehicleEligibilityResult.ALLOWED;
61+
}
62+
String installationId = record.getInstallationId();
63+
if (installationId == null || installationId.isBlank()) {
64+
return BattleVehicleEligibilityResult.DENIED_NOT_BERTHED;
65+
}
66+
if (!BattleInstallationInPlayService.isInPlay(war, factionId, installationId)) {
67+
return BattleVehicleEligibilityResult.DENIED_NOT_COMMITTED;
68+
}
69+
return BattleVehicleEligibilityResult.ALLOWED;
70+
}
71+
return BattleVehicleEligibilityResult.DENIED_NOT_FACTION_VEHICLE;
72+
}
73+
74+
private static boolean poolOnPlayerSide(War war, String playerFactionId, String vehicleFactionId) {
75+
if (vehicleFactionId == null || vehicleFactionId.isBlank()) {
4976
return false;
5077
}
51-
String installationId = berthRecord.getInstallationId();
52-
if (installationId == null || installationId.isBlank()) {
78+
Faction playerFaction = FactionManager.getByString(playerFactionId);
79+
Faction vehicleFaction = FactionManager.getByString(vehicleFactionId);
80+
if (playerFaction == null || vehicleFaction == null) {
5381
return false;
5482
}
55-
return BattleInstallationInPlayService.isInPlay(war, factionId, installationId);
83+
var playerSide = war.getSide(playerFaction);
84+
var vehicleSide = war.getSide(vehicleFaction);
85+
return playerSide != null && playerSide.equals(vehicleSide);
5686
}
5787

5888
public static BattleVehicleEligibilityResult check(
@@ -85,24 +115,8 @@ public static BattleVehicleEligibilityResult check(
85115
return BattleVehicleEligibilityResult.NOT_CAMPAIGN_BATTLE;
86116
}
87117

88-
String vehicleTypeId = vehicle.getId();
89-
if (!VehicleCategoryRules.isBerthableType(vehicleTypeId)) {
90-
return BattleVehicleEligibilityResult.ALLOWED;
91-
}
92-
93118
Optional<PlayerVehicleRecord> recordOpt = registry.getByVehicleUuid(vehicle.getUUID());
94-
PlayerVehicleRecord record = recordOpt.orElse(null);
95-
if (record == null || record.getMode() != OwnershipMode.INSTALLATION) {
96-
return BattleVehicleEligibilityResult.DENIED_NOT_BERTHED;
97-
}
98-
String installationId = record.getInstallationId();
99-
if (installationId == null || installationId.isBlank()) {
100-
return BattleVehicleEligibilityResult.DENIED_NOT_BERTHED;
101-
}
102-
if (!BattleInstallationInPlayService.isInPlay(war, faction.getId(), installationId)) {
103-
return BattleVehicleEligibilityResult.DENIED_NOT_COMMITTED;
104-
}
105-
return BattleVehicleEligibilityResult.ALLOWED;
119+
return decide(war, faction.getId(), vehicle.getId(), recordOpt.orElse(null));
106120
}
107121

108122
public static Player resolveNotifyPlayer(ActiveVehicle vehicle, PlayerVehicleRegistry registry) {
@@ -131,12 +145,16 @@ public enum BattleVehicleEligibilityResult {
131145
NOT_CAMPAIGN_BATTLE,
132146
DENIED_PRE_BATTLE_WARBAND,
133147
DENIED_NOT_BERTHED,
134-
DENIED_NOT_COMMITTED;
148+
DENIED_NOT_COMMITTED,
149+
DENIED_NOT_FACTION_VEHICLE,
150+
DENIED_POOL_SIDE;
135151

136152
public boolean isDenied() {
137153
return this == DENIED_PRE_BATTLE_WARBAND
138154
|| this == DENIED_NOT_BERTHED
139-
|| this == DENIED_NOT_COMMITTED;
155+
|| this == DENIED_NOT_COMMITTED
156+
|| this == DENIED_NOT_FACTION_VEHICLE
157+
|| this == DENIED_POOL_SIDE;
140158
}
141159
}
142160

@@ -154,6 +172,10 @@ public static String forResult(BattleVehicleEligibilityResult result) {
154172
"§cThis vehicle must be berthed at a committed installation for this battle.";
155173
case DENIED_NOT_COMMITTED ->
156174
"§cThis vehicle is berthed at an installation not committed for this battle.";
175+
case DENIED_NOT_FACTION_VEHICLE ->
176+
"§cOnly faction pool and installation vehicles can be used in a campaign battle.";
177+
case DENIED_POOL_SIDE ->
178+
"§cThis pool vehicle belongs to a faction that is not on your side.";
157179
case ALLOWED, NOT_CAMPAIGN_BATTLE -> null;
158180
};
159181
}

0 commit comments

Comments
 (0)