diff --git a/src/main/java/net/tfminecraft/simplefactions/enums/SFGUI.java b/src/main/java/net/tfminecraft/simplefactions/enums/SFGUI.java index bf242ed7..57619f1e 100644 --- a/src/main/java/net/tfminecraft/simplefactions/enums/SFGUI.java +++ b/src/main/java/net/tfminecraft/simplefactions/enums/SFGUI.java @@ -69,6 +69,8 @@ public enum SFGUI { TREATY_VIEW, INSTALLATIONS_VIEW, INSTALLATION_DETAIL_VIEW, + SUPPLY_HUB_VIEW, + HOSTED_SUPPLY_HUB_VIEW, PLAYER_LEDGER_VIEW, COMPANY_VIEW, COMPANY_SLOTS_VIEW, diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubCommands.java b/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubCommands.java index d2c862b7..f8fc1720 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubCommands.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubCommands.java @@ -378,7 +378,7 @@ private static double reachableTrade(Guild guild, Installation installation) { } /** Hub changes move trade power, so the map and incomes are brought up to date at once. */ - private static void recalculateTrade() { + public static void recalculateTrade() { if (FactionManager.getMap() != null && FactionManager.factions != null) { for (Faction faction : FactionManager.factions) { if (faction != null && faction.getRGB() != null) { diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java b/src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java index eb75b20d..370da93a 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java @@ -48,6 +48,7 @@ import net.tfminecraft.simplefactions.managers.inventory.GovernmentView; import net.tfminecraft.simplefactions.managers.inventory.GuildView; import net.tfminecraft.simplefactions.managers.inventory.InstallationView; +import net.tfminecraft.simplefactions.managers.inventory.SupplyHubView; import net.tfminecraft.simplefactions.managers.inventory.InventoryUpdater; import net.tfminecraft.simplefactions.managers.inventory.LawView; import net.tfminecraft.simplefactions.managers.inventory.LoanPayment; @@ -213,6 +214,7 @@ public void factionView(Player player, Faction f) { //Guilds public GuildView guildView = new GuildView(this); + public SupplyHubView supplyHubView = new SupplyHubView(this); public PlayerLedgerView playerLedgerView = new PlayerLedgerView(this); public void guildList(Player player) { guildView.guildList(player); @@ -757,6 +759,14 @@ public void confirmView(Player player, Faction f, String key, String data) { player.openInventory(i); } + public void confirmSupplyHub(Player player, Faction faction, String data) { + confirming.put(player, faction); + Inventory inventory = SimpleFactions.plugin.getServer().createInventory(null, 27, "§7Confirm Action"); + inventory.setItem(11, createButton("confirm", "supply_hub_action", data)); + inventory.setItem(15, createButton("cancel", "supply_hub_action", data)); + player.openInventory(inventory); + } + // Keep the existing legacy text representation, formatting, and exact-string comparisons. @SuppressWarnings("deprecation") public void openQueueCancelConfirm(Player player, Faction f, String payload, String title) { @@ -1086,6 +1096,16 @@ public void clickButton(InventoryClickEvent e) { guildList(p); } break; + case SUPPLY_HUB_VIEW: + if (g != null) { + guildView(p, g); + } + break; + case HOSTED_SUPPLY_HUB_VIEW: + if (f != null) { + installationView.installationDetailView(p, f, h.getSecondaryId()); + } + break; case MILITARY_VIEW: factionView(p, f); break; @@ -1276,6 +1296,9 @@ public void clickButton(InventoryClickEvent e) { } else if(h.getType() == SFGUI.INSTALLATIONS_VIEW || h.getType() == SFGUI.INSTALLATION_DETAIL_VIEW) { installationView.click(e, inv, p); + } else if (h.getType() == SFGUI.SUPPLY_HUB_VIEW + || h.getType() == SFGUI.HOSTED_SUPPLY_HUB_VIEW) { + supplyHubView.click(e, inv, p); } else if(h.getType() == SFGUI.GOVERNMENT_VIEW || h.getType() == SFGUI.PROPOSAL_VIEW || h.getType() == SFGUI.PROPOSALS @@ -1356,9 +1379,21 @@ public void clickButton(InventoryClickEvent e) { } if(!confirming.containsKey(p)) return; ItemStack item = e.getCurrentItem(); + if (item == null || !item.hasItemMeta()) { + return; + } ItemMeta m = item.getItemMeta(); - NamespacedKey key = new NamespacedKey(SimpleFactions.plugin, "queue_cancel"); + NamespacedKey key = new NamespacedKey(SimpleFactions.plugin, "supply_hub_action"); String data = m.getPersistentDataContainer().get(key, PersistentDataType.STRING); + if (data != null) { + boolean accepted = item.getType().equals(Material.GREEN_CONCRETE); + supplyHubView.confirm(p, data, accepted); + confirming.remove(p); + p.playSound(p, accepted ? Sound.ENTITY_PLAYER_LEVELUP : Sound.BLOCK_NOTE_BLOCK_BIT, 1f, 1f); + return; + } + key = new NamespacedKey(SimpleFactions.plugin, "queue_cancel"); + data = m.getPersistentDataContainer().get(key, PersistentDataType.STRING); if (data != null) { var parsed = QueueCancelPayload.parse(data); if (parsed.isEmpty()) { diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreator.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreator.java index af1bf526..7ee13e1b 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreator.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreator.java @@ -58,6 +58,16 @@ public class GuildCreator { FactionRanker r = new FactionRanker(); + @SuppressWarnings("deprecation") + public ItemStack createSupplyHubsItem() { + ItemStack item = new ItemStack(Material.CHEST); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(StringFormatter.formatHex("#b5835a§lSupply Hubs")); + meta.setLore(List.of("§7View your guild's supply hubs")); + item.setItemMeta(meta); + return item; + } + // Keep the existing legacy text representation, formatting, and exact-string comparisons. @SuppressWarnings("deprecation") public ItemStack createListItem(Player p, Guild guild) { diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildView.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildView.java index 2e35159f..2313ded8 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildView.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildView.java @@ -202,6 +202,8 @@ public void guildView(Player player, Guild guild, Inventory i) { } i.setItem(13, creator.createMenuItem(player, guild, MenuItemType.TRADE_BREAKDOWN)); i.setItem(14, creator.createLedgerItem(player, guild)); + // Slots 20 to 24 are the branch upgrade buttons, so this sits past Loans. + i.setItem(26, creator.createSupplyHubsItem()); if (!guild.isBase()) { i.setItem(17, creator.createDividendItem(player, guild)); } @@ -251,6 +253,14 @@ public void upgradeView(Player player, Guild guild, Inventory i) { public void click(InventoryClickEvent e, Inventory inventory, Player p) { if(!(inventory.getHolder() instanceof SFInventoryHolder)) return; SFInventoryHolder h = (SFInventoryHolder) inventory.getHolder(); + if (h.getType() == SFGUI.GUILD_VIEW && e.getSlot() == 26) { + e.setCancelled(true); + Guild guild = FactionManager.getGuildByString(h.getId()); + if (guild != null && guild.isMember(p)) { + inv.supplyHubView.guildView(p, guild); + } + return; + } if (e.getView().getTitle().equalsIgnoreCase("§7Guild List")) { e.setCancelled(true); 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 bea491e0..26b48927 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreator.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationCreator.java @@ -27,6 +27,16 @@ import net.tfminecraft.tlibs.utils.TimeFormatter; public class InstallationCreator { + + @SuppressWarnings("deprecation") + public ItemStack createSupplyHubsButton() { + ItemStack item = new ItemStack(Material.CHEST, 1); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName("§eSupply Hubs"); + meta.setLore(List.of("§7View guild hubs hosted here")); + item.setItemMeta(meta); + return item; + } // Keep the existing legacy text representation, formatting, and exact-string comparisons. @SuppressWarnings("deprecation") public ItemStack createSummary(Faction f) { diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationView.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationView.java index 908e54df..02146017 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationView.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationView.java @@ -129,7 +129,7 @@ public void installationDetailView(Player player, Faction f, String installation List berthed = SimpleFactions.getVehicleRegistry().getByInstallation(f.getId(), installation.getId()); berthed.sort(Comparator.comparing(PlayerVehicleRecord::getVehicleTypeId)); - for (int index = 0; index < berthed.size() && index < 43; index++) { + for (int index = 0; index < berthed.size() && index < 42; index++) { PlayerVehicleRecord record = berthed.get(index); Optional location = VehicleFramework.getVehicleManager() @@ -158,15 +158,27 @@ public void installationDetailView(Player player, Faction f, String installation inventory.setItem(13, new ItemStack(Material.AIR, 1)); } + if (!isPending && installation != null) { + int hubSlot = supplyHubSlot( + InstallationConfigLoader.getHubSlots(installation.getKind(), installation.getLevel())); + if (hubSlot >= 0) { + inventory.setItem(hubSlot, creator.createSupplyHubsButton()); + } + } + inventory.setItem(53, inv.createBackButton(SFGUI.INSTALLATION_DETAIL_VIEW)); if (open) player.openInventory(inventory); } static int berthedVehicleSlot(int index) { - if (index < 0 || index >= 43) { + if (index < 0 || index >= 42) { return -1; } - return index + (index >= 11 ? 1 : 0) + (index >= 12 ? 1 : 0); + return index + (index >= 11 ? 1 : 0) + (index >= 12 ? 1 : 0) + (index >= 13 ? 1 : 0); + } + + static int supplyHubSlot(int hubSlots) { + return hubSlots > 0 ? 15 : -1; } public void click(InventoryClickEvent event, Inventory inventory, Player player) { @@ -207,6 +219,14 @@ public void click(InventoryClickEvent event, Inventory inventory, Player player) if (holder.getType() == SFGUI.INSTALLATION_DETAIL_VIEW) { int slot = event.getSlot(); + if (slot == 15) { + Installation installation = f.getInstallationHandler().getById(holder.getSecondaryId()); + if (installation != null && InstallationConfigLoader.getHubSlots( + installation.getKind(), installation.getLevel()) > 0) { + inv.supplyHubView.hostedView(player, f, installation); + } + return; + } if (slot == 13) { ItemStack upgradeItem = event.getCurrentItem(); if (upgradeItem == null diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubCreator.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubCreator.java new file mode 100644 index 00000000..78b320ad --- /dev/null +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubCreator.java @@ -0,0 +1,52 @@ +package net.tfminecraft.simplefactions.managers.inventory; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.HubStanding; +import net.tfminecraft.simplefactions.utils.Formatter; + +public class SupplyHubCreator { + public static List guildHubLore( + String installationName, + String kind, + String factionName, + double upkeep, + HubStanding standing, + List connections, + double tradePower, + double production) { + List lore = new ArrayList<>(); + lore.add("§7Installation: §e" + installationName + " §8(" + kind + ")"); + lore.add("§7Owned by: §e" + factionName); + lore.add("§7Daily upkeep: §e" + Formatter.formatDouble(upkeep) + "d"); + lore.add(net.tfminecraft.simplefactions.guild.hub.SupplyHubService.statusText(standing)); + if (connections != null) { + lore.addAll(connections); + } + lore.add("§7Trade power here: §e" + Formatter.formatDouble(tradePower)); + lore.add("§7Production here: §e" + Formatter.formatDouble(production)); + return lore; + } + + public static List hostHubLore(String guildName, String factionName, HubStanding standing) { + List lore = new ArrayList<>(); + lore.add("§7Guild: §e" + guildName); + lore.add("§7Faction: §e" + factionName); + lore.add(net.tfminecraft.simplefactions.guild.hub.SupplyHubService.statusText(standing)); + return lore; + } + + public static ItemStack item(Material material, String name, List lore) { + ItemStack item = new ItemStack(material); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(name); + meta.setLore(lore); + item.setItemMeta(meta); + return item; + } +} diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubView.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubView.java new file mode 100644 index 00000000..c979db37 --- /dev/null +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubView.java @@ -0,0 +1,319 @@ +package net.tfminecraft.simplefactions.managers.inventory; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; + +import net.tfminecraft.simplefactions.SimpleFactions; +import net.tfminecraft.simplefactions.enums.SFGUI; +import net.tfminecraft.simplefactions.enums.GuildModifier; +import net.tfminecraft.simplefactions.guild.Guild; +import net.tfminecraft.simplefactions.guild.GuildModifierOverride; +import net.tfminecraft.simplefactions.guild.hub.SupplyHubCommands; +import net.tfminecraft.simplefactions.guild.hub.HubNetwork; +import net.tfminecraft.simplefactions.guild.hub.HubTransport.Link; +import net.tfminecraft.simplefactions.guild.hub.SupplyHub; +import net.tfminecraft.simplefactions.guild.hub.SupplyHubService; +import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.HubStanding; +import net.tfminecraft.simplefactions.installation.Installation; +import net.tfminecraft.simplefactions.keys.Keys; +import net.tfminecraft.simplefactions.loaders.InstallationConfigLoader; +import net.tfminecraft.simplefactions.managers.FactionManager; +import net.tfminecraft.simplefactions.managers.InventoryManager; +import net.tfminecraft.simplefactions.managers.ProvinceManager; +import net.tfminecraft.simplefactions.map.provinces.Province; +import net.tfminecraft.simplefactions.objects.Faction; +import net.tfminecraft.simplefactions.utils.Formatter; + +public class SupplyHubView { + private final InventoryManager inv; + + public SupplyHubView(InventoryManager inv) { + this.inv = inv; + } + + public void guildView(Player player, Guild guild) { + Inventory inventory = SimpleFactions.plugin.getServer().createInventory( + new net.tfminecraft.simplefactions.managers.holder.SFInventoryHolder( + guild.getId(), SFGUI.SUPPLY_HUB_VIEW), + 54, + "§7Supply Hubs"); + populateGuildView(player, guild, inventory); + player.openInventory(inventory); + } + + private void populateGuildView(Player player, Guild guild, Inventory inventory) { + inventory.clear(); + ProvinceManager provinces = SimpleFactions.getInstance().getProvinceManager(); + List hubs = SupplyHubService.oldestFirst(guild.getSupplyHubs()); + for (int index = 0; index < hubs.size() && index < 45; index++) { + SupplyHub hub = hubs.get(index); + Installation installation = SupplyHubService.findInstallation( + hub.ownerFactionId(), hub.installationId()); + Faction owner = FactionManager.getByString(hub.ownerFactionId()); + HubStanding standing = standing(guild, hub); + List connections = connectionLore(guild, hub, installation); + Province province = installation == null ? null : provinces.get(installation.getProvince()); + List lore = SupplyHubCreator.guildHubLore( + installation == null ? hub.installationId() : installation.getName(), + installation == null ? "Unknown" : installation.getKind().getDisplayName(), + owner == null ? hub.ownerFactionId() : owner.getName(), + SupplyHubService.upkeepOf(hub, hubs, SupplyHubService.baseUpkeep(), SupplyHubService.upkeepGrowth()), + standing, + connections, + province == null ? 0 : SupplyHubService.exportedTrade(province, guild.getId()), + province == null ? 0 : province.getGuildProduction(guild)); + ItemStack item = SupplyHubCreator.item( + Material.CHEST, + "§e" + (installation == null ? hub.installationId() : installation.getName()), + lore); + if (isLeader(guild, player)) { + ItemMeta meta = item.getItemMeta(); + meta.getPersistentDataContainer().set(Keys.STRING_KEY, PersistentDataType.STRING, + hub.ownerFactionId() + ":" + hub.installationId()); + lore.add("§cClick to remove this hub"); + meta.setLore(lore); + item.setItemMeta(meta); + } + inventory.setItem(index, item); + } + int limit = SupplyHubService.limit(guild); + inventory.setItem(49, SupplyHubCreator.item(Material.PAPER, "§eSupply Hub Information", List.of( + "§7Hubs: §e" + hubs.size() + "/" + limit, + "§7Daily upkeep: §e" + Formatter.formatDouble(SupplyHubService.dailyCost(guild)) + "d/day", + "§7Build with §e/guild hub build §7while standing in a port, airport or train station"))); + inventory.setItem(53, inv.createBackButton(SFGUI.SUPPLY_HUB_VIEW)); + } + + public void hostedView(Player player, Faction faction, Installation installation) { + Inventory inventory = SimpleFactions.plugin.getServer().createInventory( + new net.tfminecraft.simplefactions.managers.holder.SFInventoryHolder( + faction.getId(), SFGUI.HOSTED_SUPPLY_HUB_VIEW, installation.getId()), + 54, + "§7Hosted Supply Hubs"); + populateHostedView(player, faction, installation, inventory); + player.openInventory(inventory); + } + + private void populateHostedView(Player player, Faction faction, Installation installation, Inventory inventory) { + inventory.clear(); + List guilds = new ArrayList<>(); + for (Guild guild : SupplyHubService.allGuilds()) { + if (SupplyHubService.hasHub(guild.getSupplyHubs(), faction.getId(), installation.getId())) { + guilds.add(guild); + } + } + guilds.sort(Comparator.comparingLong(guild -> SupplyHubService.findHub( + guild.getSupplyHubs(), faction.getId(), installation.getId()).createdAt())); + int max = InstallationConfigLoader.getHubSlots(installation.getKind(), installation.getLevel()); + for (int index = 0; index < guilds.size() && index < 45; index++) { + Guild guild = guilds.get(index); + SupplyHub hub = SupplyHubService.findHub(guild.getSupplyHubs(), faction.getId(), installation.getId()); + HubStanding standing = standing(guild, hub); + Faction host = guild.getFaction(); + ItemStack item = SupplyHubCreator.item(Material.CHEST, + "§e" + guild.getName(), + SupplyHubCreator.hostHubLore(guild.getName(), host == null ? "Unknown" : host.getName(), standing)); + if (faction.getLeader().equalsIgnoreCase(player.getName())) { + ItemMeta meta = item.getItemMeta(); + meta.getPersistentDataContainer().set(Keys.STRING_KEY, PersistentDataType.STRING, guild.getId()); + List lore = new ArrayList<>(meta.getLore()); + lore.add("§cClick to evict this hub"); + meta.setLore(lore); + item.setItemMeta(meta); + } + inventory.setItem(index, item); + } + inventory.setItem(47, SupplyHubCreator.item(Material.PAPER, "§eHub Slots", + List.of("§7Used: §e" + guilds.size() + "/" + max))); + inventory.setItem(49, SupplyHubCreator.item(Material.WRITABLE_BOOK, "§eHub Permits", + permitLore(faction))); + inventory.setItem(53, inv.createBackButton(SFGUI.HOSTED_SUPPLY_HUB_VIEW)); + } + + public static List permitLore(Faction faction) { + List lore = new ArrayList<>(); + lore.add("§7Changed with §e/faction hubpermit "); + lore.add("§7Permitted guilds:"); + if (faction.getHubPermits().isEmpty()) { + lore.add("§8None"); + } else { + for (String permit : faction.getHubPermits()) { + lore.add("§e" + permit); + } + } + return lore; + } + + public static boolean removeGuildHub( + List hubs, String ownerFactionId, String installationId) { + SupplyHub hub = SupplyHubService.findHub(hubs, ownerFactionId, installationId); + return hub != null && hubs.remove(hub); + } + + public void click(InventoryClickEvent event, Inventory inventory, Player player) { + event.setCancelled(true); + if (!(inventory.getHolder() instanceof net.tfminecraft.simplefactions.managers.holder.SFInventoryHolder holder)) { + return; + } + if (holder.getType() == SFGUI.SUPPLY_HUB_VIEW) { + Guild guild = FactionManager.getGuildByString(holder.getId()); + if (guild == null) { + return; + } + if (event.getSlot() == 53) { + // InventoryManager's back button handling opens the guild menu. + return; + } + if (!isLeader(guild, player)) { + return; + } + ItemStack item = event.getCurrentItem(); + if (item == null || !item.hasItemMeta()) { + return; + } + String data = item.getItemMeta().getPersistentDataContainer() + .get(Keys.STRING_KEY, PersistentDataType.STRING); + if (data == null) { + return; + } + int separator = data.indexOf(':'); + if (separator < 0) { + return; + } + inv.confirmSupplyHub(player, guild.getFaction(), "guild|" + guild.getId() + "|" + + data.substring(0, separator) + "|" + data.substring(separator + 1)); + } else if (holder.getType() == SFGUI.HOSTED_SUPPLY_HUB_VIEW) { + Faction faction = FactionManager.getByString(holder.getId()); + if (faction == null) { + return; + } + Installation installation = faction.getInstallationHandler().getById(holder.getSecondaryId()); + if (event.getSlot() == 53) { + // InventoryManager's back button handling opens the installation menu. + return; + } + if (!faction.getLeader().equalsIgnoreCase(player.getName()) || installation == null) { + return; + } + ItemStack item = event.getCurrentItem(); + if (item == null || !item.hasItemMeta()) { + return; + } + String guildId = item.getItemMeta().getPersistentDataContainer() + .get(Keys.STRING_KEY, PersistentDataType.STRING); + if (guildId == null) { + return; + } + inv.confirmSupplyHub(player, faction, + "host|" + faction.getId() + "|" + installation.getId() + "|" + guildId); + } + player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BIT, 1f, 1f); + } + + public boolean confirm(Player player, String data, boolean accepted) { + String[] parts = data.split("\\|", -1); + if (parts.length == 4 && parts[0].equals("guild")) { + Guild guild = FactionManager.getGuildByString(parts[1]); + if (guild == null || !isLeader(guild, player)) { + return false; + } + if (accepted) { + if (removeGuildHub(guild.getSupplyHubs(), parts[2], parts[3])) { + SupplyHubCommands.recalculateTrade(); + } + } + guildView(player, guild); + return true; + } + if (parts.length == 4 && parts[0].equals("host")) { + Faction faction = FactionManager.getByString(parts[1]); + if (faction == null || !faction.getLeader().equalsIgnoreCase(player.getName())) { + return false; + } + if (accepted) { + Guild guild = FactionManager.getGuildByString(parts[3]); + if (guild != null && removeGuildHub(guild.getSupplyHubs(), parts[1], parts[2])) { + SupplyHubCommands.recalculateTrade(); + org.bukkit.entity.Player online = SimpleFactions.plugin.getServer() + .getPlayerExact(guild.getLeader()); + if (online != null) { + Installation site = faction.getInstallationHandler().getById(parts[2]); + online.sendMessage("§cYour supply hub at §f" + + (site == null ? parts[2] : site.getName()) + " §cwas evicted"); + } + } + } + Installation installation = faction.getInstallationHandler().getById(parts[2]); + if (installation != null) { + hostedView(player, faction, installation); + } + return true; + } + return false; + } + + /** A realm guild is led by its faction's leader, which {@link Guild#getLeader()} resolves. */ + private static boolean isLeader(Guild guild, Player player) { + return player.getName().equalsIgnoreCase(guild.getLeader()); + } + + private static HubStanding standing(Guild guild, SupplyHub hub) { + if (SupplyHubService.beyondGuildLimit(guild, hub)) { + return new HubStanding(false, SupplyHubService.DormantReason.BEYOND_GUILD_LIMIT); + } + Installation installation = SupplyHubService.findInstallation( + hub.ownerFactionId(), hub.installationId()); + Faction owner = FactionManager.getByString(hub.ownerFactionId()); + boolean allowed = SupplyHubService.ownerAllows( + guild.getFaction() == null ? null : guild.getFaction().getId(), hub.ownerFactionId(), + owner != null && owner.hasHubPermit(guild.getId())); + int slots = installation == null + ? 0 + : InstallationConfigLoader.getHubSlots(installation.getKind(), installation.getLevel()); + return SupplyHubService.standing(hub, installation != null, allowed, slots, + SupplyHubService.atInstallation( + hub.ownerFactionId(), hub.installationId(), SupplyHubService.allGuilds())); + } + + private static List connectionLore(Guild guild, SupplyHub hub, Installation installation) { + List result = new ArrayList<>(); + if (installation == null) { + return result; + } + double tradeBonus = GuildModifierOverride.resolve(guild, GuildModifier.HUB_TRADE); + double productionBonus = GuildModifierOverride.resolve(guild, GuildModifier.HUB_PRODUCTION); + for (Link link : HubNetwork.linksFor(guild)) { + if (link.fromProvince() != installation.getProvince() + || link.mode().getKind() != installation.getKind()) { + continue; + } + Installation destination = null; + for (SupplyHub other : guild.getSupplyHubs()) { + Installation candidate = SupplyHubService.findInstallation( + other.ownerFactionId(), other.installationId()); + if (candidate != null && candidate.getProvince() == link.toProvince() + && candidate.getKind() == link.mode().getKind()) { + destination = candidate; + break; + } + } + if (destination != null) { + result.add("§7Sends to §f" + destination.getName() + " §7by " + + link.mode().getKey() + " (" + Math.round(link.distance()) + " blocks): §e" + + Math.round(link.boostedTradeFactor(tradeBonus) * 100) + "% §7trade, §e" + + Math.round(link.boostedProductionFactor(productionBonus) * 100) + "% §7production"); + } + } + return result; + } +} diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationViewTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationViewTest.java index 70451732..cab0ef6c 100644 --- a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationViewTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/InstallationViewTest.java @@ -11,8 +11,11 @@ void berthedVehicleSlotsSkipControlSlots() { assertEquals(10, InstallationView.berthedVehicleSlot(10)); assertEquals(12, InstallationView.berthedVehicleSlot(11)); assertEquals(14, InstallationView.berthedVehicleSlot(12)); - assertEquals(44, InstallationView.berthedVehicleSlot(42)); + assertEquals(44, InstallationView.berthedVehicleSlot(41)); + assertEquals(-1, InstallationView.berthedVehicleSlot(42)); assertEquals(-1, InstallationView.berthedVehicleSlot(43)); + assertEquals(15, InstallationView.supplyHubSlot(1)); + assertEquals(-1, InstallationView.supplyHubSlot(0)); assertNotEquals(11, InstallationView.berthedVehicleSlot(11)); assertNotEquals(13, InstallationView.berthedVehicleSlot(12)); } diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubViewTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubViewTest.java new file mode 100644 index 00000000..1a27757b --- /dev/null +++ b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/SupplyHubViewTest.java @@ -0,0 +1,42 @@ +package net.tfminecraft.simplefactions.managers.inventory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import net.tfminecraft.simplefactions.guild.hub.SupplyHub; +import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.DormantReason; +import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.HubStanding; + +class SupplyHubViewTest { + @Test + void guildHubLoreShowsActiveAndDormantState() { + List active = SupplyHubCreator.guildHubLore( + "North Port", "Port", "Northland", 15, new HubStanding(true, null), + List.of("§7South Port: §esea §71000 blocks, trade 45%, production 30%"), 12, 4); + assertTrue(active.contains("§aActive")); + assertTrue(active.stream().anyMatch(line -> line.contains("South Port") && line.contains("trade 45%"))); + + List dormant = SupplyHubCreator.guildHubLore( + "North Port", "Port", "Northland", 15, + new HubStanding(false, DormantReason.NO_PERMIT), List.of(), 12, 4); + assertTrue(dormant.contains("§cDormant §7(the owning faction has not granted a hub permit)")); + } + + @Test + void removalOnlyRemovesTheRequestedGuildsHub() { + SupplyHub target = new SupplyHub("Northland", "North_Port", 10); + SupplyHub otherAtSite = new SupplyHub("Northland", "North_Port", 11); + SupplyHub otherSite = new SupplyHub("Southland", "South_Port", 12); + List hubs = new ArrayList<>(List.of(target, otherAtSite, otherSite)); + + assertTrue(SupplyHubView.removeGuildHub(hubs, "Northland", "North_Port")); + assertEquals(List.of(otherAtSite, otherSite), hubs); + assertFalse(SupplyHubView.removeGuildHub(hubs, "Missing", "North_Port")); + } +}