diff --git a/src/main/java/net/tfminecraft/simplefactions/enums/GuildModifier.java b/src/main/java/net/tfminecraft/simplefactions/enums/GuildModifier.java index 567fec79..d71bef02 100644 --- a/src/main/java/net/tfminecraft/simplefactions/enums/GuildModifier.java +++ b/src/main/java/net/tfminecraft/simplefactions/enums/GuildModifier.java @@ -6,6 +6,9 @@ public enum GuildModifier { TRADE_UPKEEP("#d6645aTrade Upkeep", false), PRODUCTION("#f2c94cProduction", true), TRADE_CARRY("#86d1b0Trade Carry", true), + HUB_LIMIT("#c99b70Supply Hub Limit", true), + HUB_TRADE("#b5835aHub Trade Transfer", true), + HUB_PRODUCTION("#a9744fHub Production Transfer", true), DIPLOMATIC_CAPACITY("#56ccf2Diplomatic Capacity", true), ADMIN_POWER("#ebde54Administrative Power", true), ADMIN_POWER_GAIN("#d1b347Administrative Power Gain", true), @@ -32,4 +35,3 @@ public boolean isPositive() { return positive; } } - diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/hub/HubNetwork.java b/src/main/java/net/tfminecraft/simplefactions/guild/hub/HubNetwork.java index 5fb07488..b552a696 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/hub/HubNetwork.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/hub/HubNetwork.java @@ -12,6 +12,7 @@ import net.tfminecraft.simplefactions.Cache; import net.tfminecraft.simplefactions.SimpleFactions; import net.tfminecraft.simplefactions.guild.Guild; +import net.tfminecraft.simplefactions.guild.GuildModifierOverride; import net.tfminecraft.simplefactions.guild.hub.HubTransport.Link; import net.tfminecraft.simplefactions.guild.hub.HubTransport.Mode; import net.tfminecraft.simplefactions.guild.hub.HubTransport.Rates; @@ -22,6 +23,7 @@ import net.tfminecraft.simplefactions.managers.ProvinceManager; import net.tfminecraft.simplefactions.map.SeaConnectivity; import net.tfminecraft.simplefactions.objects.Faction; +import net.tfminecraft.simplefactions.enums.GuildModifier; /** * The connections between each guild's active supply hubs. @@ -101,6 +103,7 @@ public static double potentialTrade( if (guild == null || target == null || provinces == null) { return 0; } + double bonus = GuildModifierOverride.resolve(guild, GuildModifier.HUB_TRADE); double best = 0; for (Installation source : activeSites(guild, SupplyHubService.allGuilds())) { Link link = connect(source, target, provinces); @@ -108,7 +111,7 @@ public static double potentialTrade( continue; } double power = provinces.get(source.getProvince()).getRawGuildTrade(guild); - best = Math.max(best, power * link.tradeFactor()); + best = Math.max(best, power * link.boostedTradeFactor(bonus)); } return best; } @@ -176,6 +179,9 @@ private static List activeSites(Guild guild, List guilds) { List sites = new ArrayList<>(); String guildFactionId = guild.getFaction() == null ? null : guild.getFaction().getId(); for (SupplyHub hub : guild.getSupplyHubs()) { + if (SupplyHubService.beyondGuildLimit(guild, hub)) { + continue; + } Installation installation = SupplyHubService.findInstallation(hub.ownerFactionId(), hub.installationId()); if (installation == null) { diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/hub/HubTransport.java b/src/main/java/net/tfminecraft/simplefactions/guild/hub/HubTransport.java index 24ecd145..dea5a89d 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/hub/HubTransport.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/hub/HubTransport.java @@ -63,6 +63,16 @@ public record Link( double distance, double tradeFactor, double productionFactor) { + + /** The boosted share is capped before the cached distance loss is applied. */ + public double boostedTradeFactor(double bonus) { + return boostedFactor(tradeFactor, mode, distance, bonus, true); + } + + /** The boosted share is capped before the cached distance loss is applied. */ + public double boostedProductionFactor(double bonus) { + return boostedFactor(productionFactor, mode, distance, bonus, false); + } } private static final Map DEFAULTS = new EnumMap<>(Mode.class); @@ -143,6 +153,21 @@ public static Link link(int fromProvince, int toProvince, Mode mode, double dist delivered(modeRates.production(), modeRates.keptPer1000(), distance)); } + private static double boostedFactor(double factor, Mode mode, double distance, double bonus, boolean trade) { + Rates modeRates = rates(mode); + double baseShare = trade ? modeRates.trade() : modeRates.production(); + if (factor <= 0 || baseShare <= 0) { + return 0; + } + double distanceLoss = delivered(1.0, modeRates.keptPer1000(), distance); + if (distanceLoss <= 0) { + return 0; + } + double unboostedShare = Math.min(MAX_SHARE, factor / distanceLoss); + double boostedShare = Math.min(MAX_SHARE, unboostedShare * (1.0 + Math.max(0, bonus))); + return delivered(boostedShare, modeRates.keptPer1000(), distance); + } + private static double share(double value) { if (!Double.isFinite(value) || value < 0) { return 0; 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 1920438c..fd71c414 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubCommands.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubCommands.java @@ -11,6 +11,8 @@ import net.tfminecraft.simplefactions.Cache; import net.tfminecraft.simplefactions.SimpleFactions; import net.tfminecraft.simplefactions.guild.Guild; +import net.tfminecraft.simplefactions.guild.GuildModifierOverride; +import net.tfminecraft.simplefactions.enums.GuildModifier; import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.BuildFailure; import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.HubStanding; import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.PlacedCandidate; @@ -208,7 +210,7 @@ private static boolean build(Player player) { chosen.hubSlots(), SupplyHubService.hasHub(guild.getSupplyHubs(), chosen.ownerFactionId(), installation.getId()), guild.getSupplyHubs().size(), - SupplyHubService.baseLimit(), + SupplyHubService.limit(guild), SupplyHubService.countLoaded(chosen.ownerFactionId(), installation.getId()), Math.max( tradeInProvince(installation.getProvince(), guild.getId()), @@ -216,7 +218,7 @@ private static boolean build(Player player) { SupplyHubService.ownerAllows(guildFactionId, chosen.ownerFactionId(), permit)); if (failure != null) { player.sendMessage(SupplyHubService.buildFailureMessage( - failure, installation.getKind().getDisplayName(), SupplyHubService.baseLimit())); + failure, installation.getKind().getDisplayName(), SupplyHubService.limit(guild))); return true; } guild.getSupplyHubs().add(new SupplyHub( @@ -233,11 +235,11 @@ private static boolean list(Player player) { return true; } List hubs = SupplyHubService.oldestFirst(guild.getSupplyHubs()); + player.sendMessage("§eSupply hubs §7(" + hubs.size() + "/" + SupplyHubService.limit(guild) + ")"); if (hubs.isEmpty()) { player.sendMessage("§7Your guild has no supply hubs"); return true; } - player.sendMessage("§eSupply hubs §7(" + hubs.size() + ")"); for (SupplyHub hub : hubs) { Installation installation = SupplyHubService.findInstallation( hub.ownerFactionId(), hub.installationId()); @@ -300,6 +302,10 @@ private static void listPermits(Player player, Faction faction) { } private static String statusOf(Guild guild, SupplyHub hub, Installation installation) { + if (SupplyHubService.beyondGuildLimit(guild, hub)) { + return SupplyHubService.statusText(new HubStanding( + false, SupplyHubService.DormantReason.BEYOND_GUILD_LIMIT)); + } String guildFactionId = guild.getFaction() == null ? null : guild.getFaction().getId(); Faction owner = FactionManager.getByString(hub.ownerFactionId()); boolean permit = owner != null && owner.hasHubPermit(guild.getId()); @@ -385,6 +391,8 @@ private static List connectionLines(Guild guild, Installation installati if (installation == null) { return lines; } + double tradeBonus = GuildModifierOverride.resolve(guild, GuildModifier.HUB_TRADE); + double productionBonus = GuildModifierOverride.resolve(guild, GuildModifier.HUB_PRODUCTION); for (HubTransport.Link link : HubNetwork.linksFor(guild)) { if (link.fromProvince() != installation.getProvince() || link.mode().getKind() != installation.getKind()) { @@ -392,8 +400,10 @@ private static List connectionLines(Guild guild, Installation installati } lines.add("§7Sends to §f" + hubNameIn(guild, link) + " §7by " + link.mode().getKey() + " (" + Math.round(link.distance()) + " blocks): §e" - + Math.round(link.tradeFactor() * 100) + "% §7trade, §e" - + Math.round(link.productionFactor() * 100) + "% §7production"); + + Math.round(link.boostedTradeFactor(tradeBonus) * 100) + + "% §7trade, §e" + + Math.round(link.boostedProductionFactor(productionBonus) * 100) + + "% §7production"); } return lines; } diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubService.java b/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubService.java index e545c591..cec1cbb3 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubService.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubService.java @@ -11,6 +11,8 @@ import net.tfminecraft.simplefactions.Cache; import net.tfminecraft.simplefactions.database.SupplyHubData; import net.tfminecraft.simplefactions.guild.Guild; +import net.tfminecraft.simplefactions.guild.GuildModifierOverride; +import net.tfminecraft.simplefactions.enums.GuildModifier; import net.tfminecraft.simplefactions.installation.Installation; import net.tfminecraft.simplefactions.installation.InstallationKind; import net.tfminecraft.simplefactions.managers.FactionManager; @@ -48,7 +50,8 @@ public enum BuildFailure { public enum DormantReason { INSTALLATION_GONE, NO_PERMIT, - BEYOND_HUB_SLOTS + BEYOND_HUB_SLOTS, + BEYOND_GUILD_LIMIT } public record HubStanding(boolean active, DormantReason reason) { @@ -104,6 +107,23 @@ public static double upkeepGrowth() { return Cache.supplyHubUpkeepGrowth; } + public static int limit(Guild guild) { + if (guild == null) { + return baseLimit(); + } + return Math.max(0, baseLimit() + (int) Math.floor( + GuildModifierOverride.resolve(guild, GuildModifier.HUB_LIMIT))); + } + + /** Hubs above the guild limit are dormant newest first, and remain in the saved list. */ + public static boolean beyondGuildLimit(Guild guild, SupplyHub hub) { + if (guild == null || hub == null) { + return false; + } + List ordered = oldestFirst(guild.getSupplyHubs()); + return indexOfHub(ordered, hub) >= limit(guild); + } + /** * Table used when {@code hub-slots} is absent for that kind and level. * Anything not listed is 0. @@ -262,6 +282,8 @@ public static String dormantReasonText(DormantReason reason) { return "the owning faction has not granted a hub permit"; case BEYOND_HUB_SLOTS: return "it is beyond this installation's hub slots"; + case BEYOND_GUILD_LIMIT: + return "it is beyond your guild's supply hub limit"; default: return "dormant"; } diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java b/src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java index 8800139c..20ec1966 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java @@ -167,21 +167,22 @@ private void carryTradeThroughHubs(Guild guild) { ProvinceDataEntry entry = province.getAllData().get(guild.getId()); if (entry != null) walked.put(province.getId(), entry.getDistance()); } - deliverTradeThroughHubs(guild, links); + deliverTradeThroughHubs( + guild, links, GuildModifierOverride.resolve(guild, GuildModifier.HUB_TRADE)); for (Map.Entry distance : walked.entrySet()) { ProvinceDataEntry entry = provinces.get(distance.getKey()).getAllData().get(guild.getId()); if (entry != null) entry.setDistance(distance.getValue()); } } - private void deliverTradeThroughHubs(Guild guild, List links) { + private void deliverTradeThroughHubs(Guild guild, List links, double bonus) { for (int pass = 0; pass <= links.size(); pass++) { boolean moved = false; for (Link link : links) { Province from = provinces.get(link.fromProvince()); Province to = provinces.get(link.toProvince()); if (from == null || to == null) continue; - double delivered = from.getRawGuildTrade(guild) * link.tradeFactor(); + double delivered = from.getRawGuildTrade(guild) * link.boostedTradeFactor(bonus); if (delivered < 0.5 || delivered <= to.getRawGuildTrade(guild)) continue; to.seedTrade(this, guild, delivered); moved = true; @@ -192,13 +193,14 @@ private void deliverTradeThroughHubs(Guild guild, List links) { private void carryProductionThroughHubs(Guild guild) { List links = HubNetwork.linksFor(guild); + double bonus = GuildModifierOverride.resolve(guild, GuildModifier.HUB_PRODUCTION); for (int pass = 0; pass <= links.size(); pass++) { boolean moved = false; for (Link link : links) { Province from = provinces.get(link.fromProvince()); Province to = provinces.get(link.toProvince()); if (from == null || to == null) continue; - double delivered = from.getGuildProduction(guild) * link.productionFactor(); + double delivered = from.getGuildProduction(guild) * link.boostedProductionFactor(bonus); if (delivered < 0.1 || delivered <= to.getGuildProduction(guild)) continue; to.seedProduction(this, guild, delivered); moved = true; diff --git a/src/main/resources/Guilds/branches.yml b/src/main/resources/Guilds/branches.yml index 148da1cd..ef484bde 100644 --- a/src/main/resources/Guilds/branches.yml +++ b/src/main/resources/Guilds/branches.yml @@ -65,3 +65,36 @@ storehouses: - trade_carry 0.25 - trade_upkeep 0 0.02 - prestige 0 5 +supply_lines: + name: "#b5835aSupply Lines" + icon: "rail.0" + group: 3 + description: + - "#b5835aSupply Lines #d4c9aestrengthen the" + - "#d4c9aeguild's network of supply hubs," + - "#d4c9aemaking more hubs available and" + - "#d4c9aepassing more trade power between them." + allowed-types: + - guild + - realm + modifiers: + - hub_limit 0 0.5 + - hub_trade 0 0.03 + - trade_upkeep 0 0.01 + - prestige 0 5 +freight_yards: + name: "#a9744fFreight Yards" + icon: "chest_minecart.0" + group: 4 + description: + - "#a9744fFreight Yards #d4c9aeimprove the" + - "#d4c9aehandover of production between" + - "#d4c9aehubs, helping workshops prosper" + - "#d4c9aefar beyond the guild's capital." + allowed-types: + - guild + - realm + modifiers: + - hub_production 0 0.08 + - trade_upkeep 0 0.01 + - prestige 0 5 diff --git a/src/test/java/net/tfminecraft/simplefactions/guild/hub/HubTransportTest.java b/src/test/java/net/tfminecraft/simplefactions/guild/hub/HubTransportTest.java index d6314f80..ccd87f6a 100644 --- a/src/test/java/net/tfminecraft/simplefactions/guild/hub/HubTransportTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/guild/hub/HubTransportTest.java @@ -64,6 +64,20 @@ void linkCarriesBothFactors() { assertEquals(0.30 * 0.85, link.productionFactor(), 1e-9); } + @Test + void modifierBoostIsAppliedBeforeTheShareCapAndDistanceLoss() { + Link link = HubTransport.link(3, 9, Mode.RAIL, 1000); + + assertEquals(0.70 * 1.3 * 0.90, link.boostedTradeFactor(0.30), 1e-9); + assertEquals(0.25 * 1.3 * 0.90, link.boostedProductionFactor(0.30), 1e-9); + + YamlConfiguration config = new YamlConfiguration(); + config.set("supply-hubs.transport.rail.trade", 0.90); + HubTransport.loadConfig(config); + Link capped = HubTransport.link(3, 9, Mode.RAIL, 0); + assertEquals(HubTransport.MAX_SHARE, capped.boostedTradeFactor(0.5), 1e-9); + } + @Test void maxRangeZeroMeansNoLimit() { assertTrue(HubTransport.inRange(HubTransport.rates(Mode.RAIL), 50_000)); diff --git a/src/test/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubServiceTest.java b/src/test/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubServiceTest.java index d736bf61..e731bd40 100644 --- a/src/test/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubServiceTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubServiceTest.java @@ -29,6 +29,7 @@ import net.tfminecraft.simplefactions.database.JsonUtil; import net.tfminecraft.simplefactions.database.SupplyHubData; import net.tfminecraft.simplefactions.guild.Guild; +import net.tfminecraft.simplefactions.enums.GuildModifier; import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.BuildFailure; import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.DormantReason; import net.tfminecraft.simplefactions.guild.hub.SupplyHubService.HubCandidate; @@ -187,6 +188,27 @@ void standing_eachDormantReason_andActive() { assertTrue(SupplyHubService.statusText(noPermit).contains("hub permit")); } + @Test + void guildLimitIncludesWholeBranchBonus_andExcessHubsAreNewestFirst() { + Cache.supplyHubBaseLimit = 2; + Guild guild = mock(Guild.class); + List hubs = new ArrayList<>(List.of( + hub("rome", "old", 1), hub("rome", "middle", 2), hub("rome", "new", 3))); + when(guild.getSupplyHubs()).thenReturn(hubs); + when(guild.getModifier(GuildModifier.HUB_LIMIT)).thenReturn(1.5); + + assertEquals(3, SupplyHubService.limit(guild)); + assertFalse(SupplyHubService.beyondGuildLimit(guild, hubs.get(0))); + assertFalse(SupplyHubService.beyondGuildLimit(guild, hubs.get(1))); + assertFalse(SupplyHubService.beyondGuildLimit(guild, hubs.get(2))); + + when(guild.getModifier(GuildModifier.HUB_LIMIT)).thenReturn(0.5); + assertEquals(2, SupplyHubService.limit(guild)); + assertTrue(SupplyHubService.beyondGuildLimit(guild, hubs.get(2))); + assertTrue(SupplyHubService.statusText(new HubStanding( + false, DormantReason.BEYOND_GUILD_LIMIT)).contains("guild's supply hub limit")); + } + @Test void dormantHub_stillCostsUpkeepAndOccupiesASlot() { SupplyHub first = hub("rome", "harbour", 1); diff --git a/src/test/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreviewTest.java b/src/test/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreviewTest.java index 0e2b5853..12c71f6c 100644 --- a/src/test/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreviewTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreviewTest.java @@ -29,6 +29,9 @@ import net.tfminecraft.simplefactions.guild.Guild; import net.tfminecraft.simplefactions.guild.GuildModifierOverride; import net.tfminecraft.simplefactions.guild.branch.Branch; +import net.tfminecraft.simplefactions.guild.hub.HubNetwork; +import net.tfminecraft.simplefactions.guild.hub.HubTransport.Link; +import net.tfminecraft.simplefactions.guild.hub.HubTransport.Mode; import net.tfminecraft.simplefactions.managers.FactionManager; import net.tfminecraft.simplefactions.managers.ProvinceManager; import net.tfminecraft.simplefactions.map.provinces.Province; @@ -89,6 +92,7 @@ void setUp() { @AfterEach void tearDown() { GuildModifierOverride.clear(); + HubNetwork.setLinksForTests(null); Cache.provincesEnabled = savedProvincesEnabled; if (savedPlainsCarry == null) { Cache.tradeCarry.remove(Terrain.PLAINS); @@ -165,4 +169,18 @@ void concurrentPreviewsStayOnTheirOwnModifiers() throws Exception { assertEquals(2, branch.getLevel()); assertEquals(12.5, capital.getProsperity()); } + + @Test + void branchPreviewUsesHypotheticalHubTradeLevel() { + HubNetwork.setLinksForTests(Map.of("fields", List.of( + new Link(capital.getId(), neighbour.getId(), Mode.RAIL, 0, 0.9, 0)))); + YamlConfiguration yaml = new YamlConfiguration(); + yaml.set("name", "Supply Lines"); + yaml.set("group", 3); + yaml.set("modifiers", List.of("HUB_TRADE 0 0.2")); + Branch supplyLines = new Branch(new Branch("supply_lines", yaml), 0); + + assertTrue(live.previewUpgradeIncomeExact(guild, supplyLines) > 0); + assertEquals(0, supplyLines.getLevel()); + } } diff --git a/src/test/java/net/tfminecraft/simplefactions/loaders/SupplyHubBranchesTest.java b/src/test/java/net/tfminecraft/simplefactions/loaders/SupplyHubBranchesTest.java new file mode 100644 index 00000000..4059e630 --- /dev/null +++ b/src/test/java/net/tfminecraft/simplefactions/loaders/SupplyHubBranchesTest.java @@ -0,0 +1,69 @@ +package net.tfminecraft.simplefactions.loaders; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import net.tfminecraft.simplefactions.enums.GuildModifier; +import net.tfminecraft.simplefactions.guild.Guild; +import net.tfminecraft.simplefactions.guild.GuildType; +import net.tfminecraft.simplefactions.guild.branch.Branch; + +class SupplyHubBranchesTest { + private final Map savedBranches = new HashMap<>(BranchLoader.map); + private final Map savedTypes = new HashMap<>(GuildLoader.map); + + @AfterEach + void restore() { + BranchLoader.map.clear(); + BranchLoader.map.putAll(savedBranches); + GuildLoader.map.clear(); + GuildLoader.map.putAll(savedTypes); + } + + @Test + void bundledBranchesLoadForGuildsAndRealms() { + GuildLoader.map.clear(); + YamlConfiguration types = new YamlConfiguration(); + types.set("guild.name", "Guild"); + types.set("realm.name", "Realm"); + GuildLoader.map.put("guild", new GuildType("guild", types.getConfigurationSection("guild"))); + GuildLoader.map.put("realm", new GuildType("realm", types.getConfigurationSection("realm"))); + + YamlConfiguration config = YamlConfiguration.loadConfiguration( + new File("src/main/resources/Guilds/branches.yml")); + BranchLoader.map.clear(); + for (String key : config.getKeys(false)) { + BranchLoader.map.put(key, new Branch(key, config.getConfigurationSection(key))); + } + Guild guildType = mock(Guild.class); + when(guildType.getType()).thenReturn(GuildLoader.getByString("guild")); + Guild realmType = mock(Guild.class); + when(realmType.getType()).thenReturn(GuildLoader.getByString("realm")); + + Branch supplyGuild = BranchLoader.getByGroup(guildType, 3); + Branch supplyRealm = BranchLoader.getByGroup(realmType, 3); + Branch freightGuild = BranchLoader.getByGroup(guildType, 4); + Branch freightRealm = BranchLoader.getByGroup(realmType, 4); + + assertNotNull(supplyGuild); + assertNotNull(supplyRealm); + assertNotNull(freightGuild); + assertNotNull(freightRealm); + assertEquals("supply_lines", supplyGuild.getId()); + assertEquals("freight_yards", freightRealm.getId()); + assertEquals(0.5, supplyGuild.getModifier(GuildModifier.HUB_LIMIT).getPerLevel()); + assertEquals(0.08, freightRealm.getModifier(GuildModifier.HUB_PRODUCTION).getPerLevel()); + assertTrue(!supplyGuild.getDescription().isEmpty()); + } +} diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/ProvinceManagerHubTransportTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/ProvinceManagerHubTransportTest.java index e9c15ac0..7465b007 100644 --- a/src/test/java/net/tfminecraft/simplefactions/managers/ProvinceManagerHubTransportTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/managers/ProvinceManagerHubTransportTest.java @@ -67,6 +67,7 @@ void setUp() { when(guild.getModifier(GuildModifier.TRADE_POWER)).thenReturn(20.0); when(guild.getModifier(GuildModifier.TRADE_CARRY)).thenReturn(1.0); when(guild.getModifier(GuildModifier.PRODUCTION)).thenReturn(10.0); + when(guild.getModifier(GuildModifier.HUB_TRADE)).thenReturn(0.0); titles = mockStatic(TitleManager.class); titles.when(() -> TitleManager.getByProvince(anyInt())).thenReturn(null); @@ -106,6 +107,16 @@ void hubDeliversAShareAndItSpreadsFromThere() { assertEquals(0, trade(12)); } + @Test + void hubTradeModifierBoostsTheShareDuringRecalculation() { + when(guild.getModifier(GuildModifier.HUB_TRADE)).thenReturn(0.30); + link(new Link(1, 22, Mode.RAIL, 0, 0.7, 0.25)); + + recalculate(); + + assertEquals(18.2, trade(22), 1e-9); + } + @Test void tradeOnlyDeliveryKeepsTheWalkedDistanceForProductionThatWalked() { // Province 4 is three steps from the capital. An air link brings more trade there but no