From dbd61f1341af7ae2a3a0e379cc7e00fd6bff3502 Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Tue, 27 Oct 2026 21:53:49 +0100 Subject: [PATCH 1/3] feat: show resource-pack vehicle icons in installation and pool menus Berthed vehicles now use the configurable icon vehicle_, the vehicle pool uses vehicle_pool, and installations use installation_; each falls back to the old material when no icon is configured. The default config lists COAL 7400-7421 for every VehicleFramework vehicle, the Field Artillery regiment uses the field artillery icon, and vehicles.yml gains the missing flat_car. Co-Authored-By: Claude Opus 5.5 --- .../inventory/InstallationCreator.java | 18 ++++++--- .../managers/inventory/MilitaryCreator.java | 2 +- src/main/resources/config.yml | 26 ++++++++++++ src/main/resources/regiments.yml | 4 +- src/main/resources/vehicles.yml | 5 +++ .../inventory/ArmyMenusCoverageTest.java | 10 ++++- .../inventory/InstallationCreatorTest.java | 27 +++++++++++++ .../InstallationMenusCoverageTest.java | 40 +++++++++++++++++++ 8 files changed, 123 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreator.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreator.java index faa0a413..6ad8c670 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreator.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreator.java @@ -73,7 +73,7 @@ public ItemStack createSummary(Faction f) { // Keep the existing legacy text representation, formatting, and exact-string comparisons. @SuppressWarnings("deprecation") public ItemStack createInstallationIcon(Installation installation) { - ItemStack item = new ItemStack(iconFor(installation.getKind()), 1); + ItemStack item = iconFor(installation.getKind()); ItemMeta meta = item.getItemMeta(); meta.setDisplayName(installation.getName()); meta.setLore(createOperationalLore(installation)); @@ -125,7 +125,7 @@ public ItemStack createDetailItem(Installation installation) { @SuppressWarnings("deprecation") public ItemStack createDetailItem( Installation installation, InstallationConstruction pendingUpgrade) { - ItemStack item = new ItemStack(iconFor(installation.getKind()), 1); + ItemStack item = iconFor(installation.getKind()); ItemMeta meta = item.getItemMeta(); meta.setDisplayName(installation.getName()); meta.setLore(createOperationalLore(installation, pendingUpgrade)); @@ -272,15 +272,23 @@ static String categoryDisplayName(String categoryId) { return categoryId.replace('_', ' '); } - private Material iconFor(InstallationKind kind) { - return kind == InstallationKind.TRAIN_STATION ? Material.MINECART : Material.GREEN_CONCRETE; + static ItemStack iconFor(InstallationKind kind) { + Material fallback = kind == InstallationKind.TRAIN_STATION ? Material.MINECART : Material.GREEN_CONCRETE; + return IconGetter.getIconOrDefault("installation_" + kind.getCommandName(), fallback); + } + + static ItemStack vehicleIcon(String vehicleTypeId) { + if (vehicleTypeId == null || vehicleTypeId.isBlank()) { + return new ItemStack(Material.MINECART, 1); + } + return IconGetter.getIconOrDefault("vehicle_" + vehicleTypeId.trim(), Material.MINECART); } // Keep the existing legacy text representation, formatting, and exact-string comparisons. @SuppressWarnings("deprecation") public ItemStack createBerthedVehicleIcon( PlayerVehicleRecord record, Optional location, boolean leader) { - ItemStack item = new ItemStack(Material.MINECART, 1); + ItemStack item = vehicleIcon(record.getVehicleTypeId()); ItemMeta meta = item.getItemMeta(); String displayName = VehicleFindMessages.resolveVehicleName(record.getVehicleUuid()); meta.setDisplayName("§e" + displayName); diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/MilitaryCreator.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/MilitaryCreator.java index 0083c30e..465869dc 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/MilitaryCreator.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/MilitaryCreator.java @@ -190,7 +190,7 @@ public ItemStack createRegimentDecreaseButton(Faction f, Regiment r) { @SuppressWarnings("deprecation") public ItemStack createVehiclePoolIcon(Faction f) { - ItemStack item = new ItemStack(Material.MINECART, 1); + ItemStack item = IconGetter.getIconOrDefault("vehicle_pool", Material.MINECART); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§eVehicle Pool"); PlayerVehicleRegistry registry = SimpleFactions.plugin == null diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e884c74e..5c75799e 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -243,6 +243,32 @@ icons: - guild_elevate(black_dye.4) - guild_loans(black_dye.17) - muster(black_dye.11) + - vehicle_aa_turret(coal.7400) + - vehicle_anti_air(coal.7401) + - vehicle_behemoth(coal.7402) + - vehicle_biplane(coal.7403) + - vehicle_bomber(coal.7404) + - vehicle_cloudskimmer(coal.7405) + - vehicle_coal_car(coal.7406) + - vehicle_cruiser(coal.7407) + - vehicle_field_artillery(coal.7408) + - vehicle_fixed_artillery(coal.7409) + - vehicle_flat_car(coal.7410) + - vehicle_gunboat(coal.7411) + - vehicle_gyrobomber(coal.7412) + - vehicle_horse_cart(coal.7413) + - vehicle_ironclad(coal.7414) + - vehicle_monoplane(coal.7415) + - vehicle_passenger_car(coal.7416) + - vehicle_simple_locomotive(coal.7417) + - vehicle_sloop(coal.7418) + - vehicle_small_car(coal.7419) + - vehicle_torpedoboat(coal.7420) + - vehicle_wooden_cart(coal.7421) + - vehicle_pool(coal.7414) + - installation_port(coal.7418) + - installation_airport(coal.7403) + - installation_train_station(coal.7417) base-effects: favoured_guilds: diff --git a/src/main/resources/regiments.yml b/src/main/resources/regiments.yml index 5ce0a879..accd0755 100644 --- a/src/main/resources/regiments.yml +++ b/src/main/resources/regiments.yml @@ -17,8 +17,8 @@ artillery: upkeep: 6.0 expansion-time: 21600 item: - material: BLACK_DYE - model-data: 12 + material: COAL + model-data: 7408 description: - "#c4a35aField Artillery #a89977slots hold guns." - "#a89977Each slot lets the faction keep one" diff --git a/src/main/resources/vehicles.yml b/src/main/resources/vehicles.yml index a1a6e90e..7ed02de4 100644 --- a/src/main/resources/vehicles.yml +++ b/src/main/resources/vehicles.yml @@ -61,6 +61,11 @@ categories: size: 1 ignore-limit: true per-person: 3 + flat_car: + upkeep: 1 + size: 1 + ignore-limit: true + per-person: 3 ships: show-on-upcoming-battle-icon: true gunboat: diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/ArmyMenusCoverageTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/ArmyMenusCoverageTest.java index 7af1e2df..34ac871b 100644 --- a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/ArmyMenusCoverageTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/ArmyMenusCoverageTest.java @@ -73,6 +73,7 @@ class ArmyMenusCoverageTest { private MilitaryView military; private java.util.Map oldForming; private final List> scopes = new ArrayList<>(); + private MockedStatic icons; private MockedStatic scoped(Class type) { MockedStatic s = mockStatic(type); @@ -95,7 +96,10 @@ void setup() { military = new MilitaryView(navigation); oldForming = TitleManager.isFormingTitle; TitleManager.isFormingTitle = new HashMap<>(); - scoped(IconGetter.class); + icons = scoped(IconGetter.class); + icons + .when(() -> IconGetter.getIconOrDefault(anyString(), any())) + .thenAnswer(call -> new ItemStack((Material) call.getArgument(1))); ItemAPI api = mock(ItemAPI.class); ItemCreator items = mock(ItemCreator.class); when(api.getCreator()).thenReturn(items); @@ -579,7 +583,11 @@ void vehiclePoolIconShowsTheRegistryAndOutstandingMaintenance() { try (MockedStatic plugin = mockStatic(SimpleFactions.class, CALLS_REAL_METHODS)) { plugin.when(SimpleFactions::getVehicleRegistry).thenReturn(registry); + icons + .when(() -> IconGetter.getIconOrDefault("vehicle_pool", Material.MINECART)) + .thenAnswer(call -> new ItemStack(Material.COAL)); ItemStack item = military.creator.createVehiclePoolIcon(faction); + assertEquals(Material.COAL, item.getType()); assertEquals("Vehicle Pool", name(item)); assertTrue(lore(item).contains("Unpaid: 1 of 1")); assertTrue(lore(item).contains("wagon")); diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreatorTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreatorTest.java index 7b9c3e5b..ed30c2cd 100644 --- a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreatorTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreatorTest.java @@ -1,10 +1,15 @@ package net.tfminecraft.simplefactions.managers.inventory; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -42,4 +47,26 @@ void categoryDisplayNameUsesConfiguredNameOrReadableId() { assertEquals("Static Emplacements", InstallationCreator.categoryDisplayName("static_emplacements")); assertEquals("land vehicles", InstallationCreator.categoryDisplayName("land_vehicles")); } + + @Test + void everyDefaultVehicleHasADefaultIcon() { + List icons = YamlConfiguration + .loadConfiguration(Path.of("src/main/resources/config.yml").toFile()) + .getStringList("icons"); + ConfigurationSection categories = YamlConfiguration + .loadConfiguration(Path.of("src/main/resources/vehicles.yml").toFile()) + .getConfigurationSection("categories"); + int vehicles = 0; + for (String category : categories.getKeys(false)) { + ConfigurationSection section = categories.getConfigurationSection(category); + for (String vehicle : section.getKeys(false)) { + if (!section.isConfigurationSection(vehicle)) continue; + vehicles++; + String prefix = "vehicle_" + vehicle + "(coal."; + assertTrue(icons.stream().anyMatch(icon -> icon.startsWith(prefix)), vehicle); + } + } + assertEquals(22, vehicles); + assertTrue(icons.contains("vehicle_pool(coal.7414)")); + } } diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationMenusCoverageTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationMenusCoverageTest.java index 94d2df54..e7b14fcb 100644 --- a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationMenusCoverageTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationMenusCoverageTest.java @@ -13,6 +13,7 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; +import net.tfminecraft.simplefactions.Cache; import net.tfminecraft.simplefactions.SimpleFactions; import net.tfminecraft.simplefactions.enums.SFGUI; import net.tfminecraft.simplefactions.espionage.EspionageService; @@ -26,6 +27,7 @@ import net.tfminecraft.simplefactions.managers.InventoryManager; import net.tfminecraft.simplefactions.managers.holder.SFInventoryHolder; import net.tfminecraft.simplefactions.objects.Faction; +import net.tfminecraft.simplefactions.util.LegacyModelData; import net.tfminecraft.simplefactions.testsupport.FactionDomainFixture; import net.tfminecraft.simplefactions.vehicles.berth.InstallationVehicleUnberthService; import net.tfminecraft.simplefactions.vehicles.registry.OwnershipMode; @@ -184,6 +186,44 @@ void installationListSummarizesAllKindsAndSortsOperationalRecords() { assertEquals("AIRPORT", ((SFInventoryHolder) top().getHolder()).getSecondaryId()); } + @Test + void configuredIconsDressInstallationsAndBerthedVehiclesWithoutChangingTheirData() { + HashMap previous = Cache.icons; + Cache.icons = new HashMap<>(); + Cache.icons.put("installation_train_station", "coal.7417"); + Cache.icons.put("vehicle_sloop", "coal.7418"); + try { + InstallationCreator creator = new InstallationCreator(); + Installation station = installation("station", InstallationKind.TRAIN_STATION); + ItemStack listed = creator.createInstallationIcon(station); + assertEquals(Material.COAL, listed.getType()); + assertEquals(7417, LegacyModelData.get(listed.getItemMeta())); + assertEquals("station", name(listed)); + assertEquals("station", data(listed, Keys.STRING_KEY)); + ItemStack detail = creator.createDetailItem(station); + assertEquals(Material.COAL, detail.getType()); + assertEquals("station", data(detail, Keys.STRING_KEY)); + assertEquals( + Material.GREEN_CONCRETE, InstallationCreator.iconFor(InstallationKind.PORT).getType()); + Cache.icons.remove("installation_train_station"); + assertEquals( + Material.MINECART, InstallationCreator.iconFor(InstallationKind.TRAIN_STATION).getType()); + + PlayerVehicleRecord sloop = berth("boat-1", "Sloop", station); + ItemStack vehicle = creator.createBerthedVehicleIcon(sloop, Optional.empty(), true); + assertEquals(Material.COAL, vehicle.getType()); + assertEquals(7418, LegacyModelData.get(vehicle.getItemMeta())); + assertEquals("boat-1", data(vehicle, Keys.STRING_KEY)); + assertTrue(lore(vehicle).contains("Type: Sloop")); + assertTrue(lore(vehicle).contains("Click to take")); + assertEquals(Material.MINECART, InstallationCreator.vehicleIcon("unknown").getType()); + assertEquals(Material.MINECART, InstallationCreator.vehicleIcon(null).getType()); + assertEquals(Material.MINECART, InstallationCreator.vehicleIcon(" ").getType()); + } finally { + Cache.icons = previous; + } + } + @Test void listCapacityAndRefreshKeepControlsAndRemoveVanishedInstallations() { for (int n = 0; n < 35; n++) { From 4113e3121a172e50f815d931752a30ac466f9ea3 Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:07:18 +0100 Subject: [PATCH 2/3] Leave flat_car in vehicles.yml to #197 Co-Authored-By: Claude Opus 5.5 --- src/main/resources/vehicles.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/resources/vehicles.yml b/src/main/resources/vehicles.yml index 7ed02de4..a1a6e90e 100644 --- a/src/main/resources/vehicles.yml +++ b/src/main/resources/vehicles.yml @@ -61,11 +61,6 @@ categories: size: 1 ignore-limit: true per-person: 3 - flat_car: - upkeep: 1 - size: 1 - ignore-limit: true - per-person: 3 ships: show-on-upcoming-battle-icon: true gunboat: From d4a79849c50b8b4799fad361225fa7f8a27faa1b Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:13:43 +0100 Subject: [PATCH 3/3] Check the 22 vehicle icons by id, not by counting vehicles.yml Co-Authored-By: Claude Opus 5.5 --- .../inventory/InstallationCreatorTest.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreatorTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreatorTest.java index ed30c2cd..cc87a328 100644 --- a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreatorTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreatorTest.java @@ -8,7 +8,6 @@ import java.nio.file.Path; import java.util.List; -import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.jupiter.api.AfterEach; @@ -49,24 +48,22 @@ void categoryDisplayNameUsesConfiguredNameOrReadableId() { } @Test - void everyDefaultVehicleHasADefaultIcon() { + void everyVehicleHasItsResourcePackIcon() { List icons = YamlConfiguration .loadConfiguration(Path.of("src/main/resources/config.yml").toFile()) .getStringList("icons"); - ConfigurationSection categories = YamlConfiguration - .loadConfiguration(Path.of("src/main/resources/vehicles.yml").toFile()) - .getConfigurationSection("categories"); - int vehicles = 0; - for (String category : categories.getKeys(false)) { - ConfigurationSection section = categories.getConfigurationSection(category); - for (String vehicle : section.getKeys(false)) { - if (!section.isConfigurationSection(vehicle)) continue; - vehicles++; - String prefix = "vehicle_" + vehicle + "(coal."; - assertTrue(icons.stream().anyMatch(icon -> icon.startsWith(prefix)), vehicle); - } + // The pack draws one icon per vehicle on COAL 7400-7421, in vehicle id order. + List vehicles = List.of( + "aa_turret", "anti_air", "behemoth", "biplane", + "bomber", "cloudskimmer", "coal_car", "cruiser", + "field_artillery", "fixed_artillery", "flat_car", "gunboat", + "gyrobomber", "horse_cart", "ironclad", "monoplane", + "passenger_car", "simple_locomotive", "sloop", "small_car", + "torpedoboat", "wooden_cart"); + for (int n = 0; n < vehicles.size(); n++) { + String icon = "vehicle_" + vehicles.get(n) + "(coal." + (7400 + n) + ")"; + assertTrue(icons.contains(icon), icon); } - assertEquals(22, vehicles); assertTrue(icons.contains("vehicle_pool(coal.7414)")); } }