From 4e84a95668d9af38dd6ed2a707b5ed5805f9f931 Mon Sep 17 00:00:00 2001 From: Drefvelin <54400154+Drefvelin@users.noreply.github.com> Date: Sun, 4 Oct 2026 19:11:30 +0000 Subject: [PATCH] Remove Infrastructure Province infrastructure, effective terrain, the realm Infrastructure branch and its upkeep, track counting as infrastructure, its map export and the installation build estimate are gone. Terrain reads the raw province terrain again, and /faction construct builds straight away. Co-Authored-By: Claude Opus 5.5 (1M context) Co-authored-by: Cursor --- .../net/tfminecraft/simplefactions/Cache.java | 10 - .../simplefactions/SimpleFactions.java | 10 +- .../simplefactions/enums/GuildModifier.java | 2 - .../guild/hub/InfrastructureMenuCopy.java | 55 ---- .../guild/hub/InfrastructurePreview.java | 112 -------- .../hub/VehicleFrameworkTrackProvinces.java | 2 +- .../guild/income/BranchIncomePreview.java | 93 +------ .../simplefactions/guild/income/Cashflow.java | 3 +- .../simplefactions/guild/income/Ledger.java | 8 - .../simplefactions/loaders/ConfigLoader.java | 14 - .../managers/CommandManager.java | 11 +- .../managers/InventoryManager.java | 14 - .../managers/ProvinceManager.java | 58 ---- .../inventory/BranchIncomePreviewService.java | 10 +- .../managers/inventory/GuildCreator.java | 12 +- .../inventory/InstallationBuildConfirm.java | 190 ------------- .../simplefactions/map/Compiler.java | 17 -- .../map/infra/EffectiveTerrain.java | 11 - .../map/infra/InfrastructureSources.java | 50 ---- .../map/infra/InfrastructureSpread.java | 49 ---- .../map/infra/TrackProvinceCache.java | 4 +- .../map/provinces/Province.java | 20 +- src/main/resources/Guilds/branches.yml | 21 -- src/main/resources/config.yml | 12 - .../GuildSavedInfrastructureBranchTest.java | 107 ++++++++ .../guild/hub/InfrastructureMenuCopyTest.java | 42 --- .../guild/hub/SupplyHubServiceTest.java | 22 -- .../guild/income/BranchIncomePreviewTest.java | 68 ----- .../ConfigLoaderInfrastructureTest.java | 130 --------- .../ProvinceLoaderInfrastructureTest.java | 37 +++ .../loaders/SupplyHubBranchesTest.java | 15 +- .../inventory/GuildCreatorIncomeLineTest.java | 22 -- .../map/CompilerProvinceExportTest.java | 93 +------ .../map/infra/EffectiveTerrainTest.java | 39 --- .../map/infra/InfrastructureSourcesTest.java | 175 ------------ .../map/infra/InfrastructureSpreadTest.java | 135 ---------- .../map/infra/ProvinceInfrastructureTest.java | 249 ------------------ .../map/infra/TrackProvinceCacheTest.java | 2 +- .../map/provinces/ProvinceTradeCarryTest.java | 105 ++++++++ 39 files changed, 285 insertions(+), 1744 deletions(-) delete mode 100644 src/main/java/net/tfminecraft/simplefactions/guild/hub/InfrastructureMenuCopy.java delete mode 100644 src/main/java/net/tfminecraft/simplefactions/guild/hub/InfrastructurePreview.java delete mode 100644 src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationBuildConfirm.java delete mode 100644 src/main/java/net/tfminecraft/simplefactions/map/infra/EffectiveTerrain.java delete mode 100644 src/main/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSources.java delete mode 100644 src/main/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSpread.java create mode 100644 src/test/java/net/tfminecraft/simplefactions/guild/GuildSavedInfrastructureBranchTest.java delete mode 100644 src/test/java/net/tfminecraft/simplefactions/guild/hub/InfrastructureMenuCopyTest.java delete mode 100644 src/test/java/net/tfminecraft/simplefactions/loaders/ConfigLoaderInfrastructureTest.java create mode 100644 src/test/java/net/tfminecraft/simplefactions/loaders/ProvinceLoaderInfrastructureTest.java delete mode 100644 src/test/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreatorIncomeLineTest.java delete mode 100644 src/test/java/net/tfminecraft/simplefactions/map/infra/EffectiveTerrainTest.java delete mode 100644 src/test/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSourcesTest.java delete mode 100644 src/test/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSpreadTest.java delete mode 100644 src/test/java/net/tfminecraft/simplefactions/map/infra/ProvinceInfrastructureTest.java create mode 100644 src/test/java/net/tfminecraft/simplefactions/map/provinces/ProvinceTradeCarryTest.java diff --git a/src/main/java/net/tfminecraft/simplefactions/Cache.java b/src/main/java/net/tfminecraft/simplefactions/Cache.java index b8f21dfd..d24bbbd0 100644 --- a/src/main/java/net/tfminecraft/simplefactions/Cache.java +++ b/src/main/java/net/tfminecraft/simplefactions/Cache.java @@ -174,16 +174,6 @@ public static boolean requireProvinces(org.bukkit.command.CommandSender sender) /** Share of a sea or rail delivery left in the provinces along the way. */ public static double supplyHubCorridorShare = 0.5; - public static double infrastructureFull = 20; - public static double infrastructureTarget = 0.75; - public static double infrastructureWildernessSpread = 0.25; - public static double infrastructureSpreadFloor = 0.5; - public static double infrastructureStation = 10; - public static double infrastructurePort = 10; - public static double infrastructureAirport = 5; - public static double infrastructureTrack = 10; - public static int infrastructureTrackRefreshSeconds = 300; - public static Map baseEffects = new HashMap<>(); public static Map tradeCarry = new HashMap<>(); diff --git a/src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java b/src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java index f4fd0ff0..e3c0f832 100644 --- a/src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java +++ b/src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java @@ -171,7 +171,8 @@ public class SimpleFactions extends JavaPlugin{ new net.tfminecraft.simplefactions.mercenary.contract.AttendanceService.Hook(); private ProvinceManager provinceSnapshot = new ProvinceManager(); private ProvinceGrid provinceGrid; - private BukkitTask trackInfrastructureRefreshTask; + private static final long TRACK_REFRESH_TICKS = 300L * 20L; + private BukkitTask trackRefreshTask; private final PlayerVehicleRegistry vehicleRegistry = new PlayerVehicleRegistry(); private VehicleRegistryPersistence vehicleRegistryPersistence; private final InstallationVehicleOwnerSync installationVehicleOwnerSync = @@ -364,16 +365,15 @@ public void onEnable() { if (Cache.supplyHubAutoAccept) { getLogger().info("Hub auto-accept is on."); } - long refreshTicks = Cache.infrastructureTrackRefreshSeconds * 20L; - trackInfrastructureRefreshTask = getServer().getScheduler().runTaskTimer( - this, () -> { refreshTrackProvinces(); }, refreshTicks, refreshTicks); + trackRefreshTask = getServer().getScheduler().runTaskTimer( + this, () -> { refreshTrackProvinces(); }, TRACK_REFRESH_TICKS, TRACK_REFRESH_TICKS); } inventoryManager.start(); vehicleMaintenanceDecayTask.start(); } @Override public void onDisable() { - if (trackInfrastructureRefreshTask != null) trackInfrastructureRefreshTask.cancel(); + if (trackRefreshTask != null) trackRefreshTask.cancel(); net.tfminecraft.simplefactions.integration.rpcharacters.chat.RpCharactersChatIntegration.unregister(); MercenaryStatService.clearAll(); net.tfminecraft.simplefactions.mercenary.company.MercenaryEligibility.reset(); diff --git a/src/main/java/net/tfminecraft/simplefactions/enums/GuildModifier.java b/src/main/java/net/tfminecraft/simplefactions/enums/GuildModifier.java index 63ba9796..13e2491a 100644 --- a/src/main/java/net/tfminecraft/simplefactions/enums/GuildModifier.java +++ b/src/main/java/net/tfminecraft/simplefactions/enums/GuildModifier.java @@ -5,8 +5,6 @@ public enum GuildModifier { TRADE_POWER("#92d665Trade Power", true), TRADE_UPKEEP("#d6645aTrade Upkeep", false), PRODUCTION("#f2c94cProduction", true), - INFRASTRUCTURE("#86d1b0Infrastructure", true), - INFRASTRUCTURE_UPKEEP("#86d1b0Infrastructure Upkeep", false), TRADE_CARRY("#86d1b0Trade Carry", true), HUB_LIMIT("#c99b70Supply Hub Limit", true), HUB_UPKEEP("#d6645aSupply Hub Upkeep", false), diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/hub/InfrastructureMenuCopy.java b/src/main/java/net/tfminecraft/simplefactions/guild/hub/InfrastructureMenuCopy.java deleted file mode 100644 index a81d32bd..00000000 --- a/src/main/java/net/tfminecraft/simplefactions/guild/hub/InfrastructureMenuCopy.java +++ /dev/null @@ -1,55 +0,0 @@ -package net.tfminecraft.simplefactions.guild.hub; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -/** Lore for the realm headline and the installation confirmation. */ -public final class InfrastructureMenuCopy { - private InfrastructureMenuCopy() {} - - public static String headline(double worth) { - return "§7Infrastructure is worth about " + signed(worth) + " a day to your realm"; - } - - public static String headlineUnknown() { - return "§7Infrastructure worth is worked out once a day."; - } - - public static String calculating() { - return "§7Working out what this would earn..."; - } - - public static List installationPreview(InfrastructurePreview.InstallationPreview preview) { - if (preview == null) { - return List.of(calculating()); - } - List lines = new ArrayList<>(); - if (preview.infrastructureHere() > 0) { - lines.add("§a" + signed(preview.infrastructureHere()) + " infrastructure here"); - } else { - lines.add("§7This adds no infrastructure here"); - } - lines.add("§7about " + signed(preview.realmPerDay()) + " a day for your realm"); - if (preview.upkeep() > 0) { - lines.add("§7upkeep " + compact(preview.upkeep())); - } - return lines; - } - - static String signed(double value) { - String number = compact(Math.abs(value)); - if (value < 0) { - return "-" + number; - } - return "+" + number; - } - - static String compact(double value) { - double rounded = Math.round(value * 100.0) / 100.0; - if (Math.abs(rounded - Math.rint(rounded)) < 0.001) { - return Long.toString((long) Math.rint(rounded)); - } - return String.format(Locale.ROOT, "%.2f", rounded); - } -} diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/hub/InfrastructurePreview.java b/src/main/java/net/tfminecraft/simplefactions/guild/hub/InfrastructurePreview.java deleted file mode 100644 index e4d85dbe..00000000 --- a/src/main/java/net/tfminecraft/simplefactions/guild/hub/InfrastructurePreview.java +++ /dev/null @@ -1,112 +0,0 @@ -package net.tfminecraft.simplefactions.guild.hub; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import net.tfminecraft.simplefactions.Cache; -import net.tfminecraft.simplefactions.guild.Guild; -import net.tfminecraft.simplefactions.guild.income.EconomicPreview; -import net.tfminecraft.simplefactions.installation.InstallationKind; -import net.tfminecraft.simplefactions.loaders.InstallationConfigLoader; -import net.tfminecraft.simplefactions.managers.ProvinceManager; -import net.tfminecraft.simplefactions.map.provinces.Province; -import net.tfminecraft.simplefactions.objects.Faction; - -/** What one new installation would earn the realm. Used by the build confirmation. */ -public final class InfrastructurePreview { - public record InstallationPreview(double infrastructureHere, double realmPerDay, double upkeep) { - public static final InstallationPreview NONE = new InstallationPreview(0, 0, 0); - } - - private InfrastructurePreview() { - } - - /** - * Adds this kind's infrastructure in the province and reads the realm's net change. - * Writes live trade breakdowns, the same way a law preview does. - */ - public static InstallationPreview previewInstallation( - ProvinceManager live, Faction realm, int provinceId, InstallationKind kind) { - if (live == null || realm == null || kind == null || !live.contains(provinceId)) { - return InstallationPreview.NONE; - } - Province province = live.get(provinceId); - if (province.isSea()) { - return InstallationPreview.NONE; - } - double added = sourceAmount(kind); - Map before = EconomicPreview.projectNets(copy(live)); - ProvinceManager after = copy(live); - if (added > 0) { - after.setExtraInfrastructure(Map.of(provinceId, added)); - } - Map next = EconomicPreview.projectNets(after); - return new InstallationPreview(added, realmDelta(realm, before, next), levelOneUpkeep(kind)); - } - - private static double sourceAmount(InstallationKind kind) { - if (kind == null) { - return 0; - } - return switch (kind) { - case TRAIN_STATION -> Cache.infrastructureStation; - case PORT -> Cache.infrastructurePort; - case AIRPORT -> Cache.infrastructureAirport; - default -> 0; - }; - } - - private static double levelOneUpkeep(InstallationKind kind) { - if (kind == null) { - return 0; - } - try { - return InstallationConfigLoader.getDailyUpkeep(kind, 1); - } catch (IllegalStateException ex) { - return switch (kind) { - case TRAIN_STATION -> 5; - case PORT -> 15; - case AIRPORT -> 20; - case FORT -> 30; - }; - } - } - - private static double realmDelta(Faction realm, Map before, Map after) { - double sum = 0; - Set guilds = new HashSet<>(); - guilds.addAll(before.keySet()); - guilds.addAll(after.keySet()); - for (Guild guild : guilds) { - if (guild == null || !sameRealm(realm, guild.getFaction())) { - continue; - } - sum += after.getOrDefault(guild, 0.0) - before.getOrDefault(guild, 0.0); - } - return round(sum); - } - - private static double round(double value) { - if (!Double.isFinite(value)) { - return 0; - } - return Math.round(value * 100.0) / 100.0; - } - - private static boolean sameRealm(Faction left, Faction right) { - if (left == null || right == null) { - return false; - } - if (left.getId() != null && left.getId().equalsIgnoreCase(right.getId())) { - return true; - } - return net.tfminecraft.simplefactions.managers.RelationManager.sameRealm(left, right); - } - - private static ProvinceManager copy(ProvinceManager live) { - ProvinceManager snapshot = live.createSnapshotShell(); - snapshot.copyAllDataFrom(live); - return snapshot; - } -} diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/hub/VehicleFrameworkTrackProvinces.java b/src/main/java/net/tfminecraft/simplefactions/guild/hub/VehicleFrameworkTrackProvinces.java index 22ddda76..03d9c1ca 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/hub/VehicleFrameworkTrackProvinces.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/hub/VehicleFrameworkTrackProvinces.java @@ -6,7 +6,7 @@ import net.tfminecraft.simplefactions.map.ProvinceGrid; import net.tfminecraft.simplefactions.map.provinces.Province; -/** Track infrastructure sampling uses the same VehicleFramework boundary as hub routes. */ +/** Provinces crossed by railway track. Hub routes use the same VehicleFramework boundary. */ public final class VehicleFrameworkTrackProvinces { private VehicleFrameworkTrackProvinces() {} diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreview.java b/src/main/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreview.java index c743c3af..275eebcf 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreview.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreview.java @@ -1,8 +1,6 @@ package net.tfminecraft.simplefactions.guild.income; -import java.util.ArrayList; import java.util.EnumMap; -import java.util.List; import java.util.Map; import net.tfminecraft.simplefactions.enums.GuildModifier; @@ -11,9 +9,7 @@ import net.tfminecraft.simplefactions.guild.GuildModifierOverride; import net.tfminecraft.simplefactions.guild.branch.Branch; import net.tfminecraft.simplefactions.guild.branch.BranchModifier; -import net.tfminecraft.simplefactions.managers.FactionManager; import net.tfminecraft.simplefactions.managers.ProvinceManager; -import net.tfminecraft.simplefactions.managers.RelationManager; import net.tfminecraft.simplefactions.objects.Faction; /** @@ -63,58 +59,10 @@ public static double estimate( ProvinceManager after = copy(prepared.snapshot); double baseline = income(before, guild, current); double changed = income(after, guild, hypothetical); - double upkeepChange = Math.max(0.0, hypothetical.getOrDefault(GuildModifier.INFRASTRUCTURE_UPKEEP, 0.0)) - - Math.max(0.0, current.getOrDefault(GuildModifier.INFRASTRUCTURE_UPKEEP, 0.0)); - double net = (changed - baseline) * (1.0 - taxFraction) - upkeepChange; + double net = (changed - baseline) * (1.0 - taxFraction); return Math.round(net * 100.0) / 100.0; } - /** - * The realm guild's infrastructure reaches every guild in the realm, so an - * up or down of that branch is their incomes added together. - */ - public static boolean showsRealm(Guild guild, Branch branch) { - return guild != null - && guild.isBase() - && branch != null - && branch.getModifier(GuildModifier.INFRASTRUCTURE) != null; - } - - /** Guilds and factions as they were on the server thread. The preview does not read the live lists. */ - public static List guildsNow() { - List guilds = new ArrayList<>(); - for (Guild guild : FactionManager.getAllGuilds()) { - if (guild != null) { - guilds.add(guild); - } - } - return List.copyOf(guilds); - } - - public static List factionsNow() { - List factions = new ArrayList<>(); - for (Faction faction : FactionManager.getCopy()) { - if (faction != null) { - factions.add(faction); - } - } - return List.copyOf(factions); - } - - public static double estimateRealm( - Prepared prepared, - Guild realmGuild, - Map current, - Map hypothetical, - List guilds, - List factions) { - double before = realmIncome(prepared, realmGuild, current, guilds, factions); - double after = realmIncome(prepared, realmGuild, hypothetical, guilds, factions); - double upkeepChange = Math.max(0.0, hypothetical.getOrDefault(GuildModifier.INFRASTRUCTURE_UPKEEP, 0.0)) - - Math.max(0.0, current.getOrDefault(GuildModifier.INFRASTRUCTURE_UPKEEP, 0.0)); - return Math.round((after - before - upkeepChange) * 100.0) / 100.0; - } - public static Map modifiers(Guild guild) { EnumMap amounts = new EnumMap<>(GuildModifier.class); for (GuildModifier modifier : GuildModifier.values()) { @@ -155,45 +103,6 @@ private static ProvinceManager copy(ProvinceManager source) { return snapshot; } - private static double realmIncome( - Prepared prepared, - Guild realmGuild, - Map amounts, - List guilds, - List factions) { - ProvinceManager snapshot = copy(prepared.snapshot); - GuildModifierOverride.use(realmGuild, amounts); - try { - snapshot.recalculateQuiet(guilds, factions); - double sum = 0; - Faction realm = realmGuild.getFaction(); - List present = guilds == null ? List.of() : guilds; - for (Guild guild : present) { - if (guild == null || !sameRealm(realm, guild.getFaction())) { - continue; - } - sum += snapshot.getIncome(guild, false) * (1.0 - taxFraction(guild)); - } - return sum; - } finally { - snapshot.clearPreviewLists(); - GuildModifierOverride.clear(); - } - } - - private static boolean sameRealm(Faction realm, Faction guildFaction) { - if (realm == null || guildFaction == null) { - return false; - } - if (realm == guildFaction) { - return true; - } - if (realm.getId() != null && realm.getId().equalsIgnoreCase(guildFaction.getId())) { - return true; - } - return RelationManager.sameRealm(realm, guildFaction); - } - private static double income(ProvinceManager snapshot, Guild guild, Map amounts) { GuildModifierOverride.use(guild, amounts); try { diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/income/Cashflow.java b/src/main/java/net/tfminecraft/simplefactions/guild/income/Cashflow.java index f5fb7834..465ebc2f 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/income/Cashflow.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/income/Cashflow.java @@ -52,8 +52,7 @@ public enum Cashflow { VEHICLE_UPKEEP("#a6659fVehicle Upkeep", true, false), MILITARY_UPKEEP("#a6659fMilitary Upkeep", true, false), NODES("#8a7a5cNodes", true, false), - SUPPLY_HUBS("#b5835aSupply hubs", true, false), - INFRASTRUCTURE_UPKEEP("#86d1b0Infrastructure", true, false); + SUPPLY_HUBS("#b5835aSupply hubs", true, false); private final String display; private final boolean affectsInflation; diff --git a/src/main/java/net/tfminecraft/simplefactions/guild/income/Ledger.java b/src/main/java/net/tfminecraft/simplefactions/guild/income/Ledger.java index 15602a90..06984318 100644 --- a/src/main/java/net/tfminecraft/simplefactions/guild/income/Ledger.java +++ b/src/main/java/net/tfminecraft/simplefactions/guild/income/Ledger.java @@ -14,7 +14,6 @@ import net.tfminecraft.simplefactions.SimpleFactions; import net.tfminecraft.simplefactions.guild.Guild; -import net.tfminecraft.simplefactions.guild.GuildModifierOverride; import net.tfminecraft.simplefactions.guild.hub.SupplyHub; import net.tfminecraft.simplefactions.guild.hub.HubTaxBreakdown; import net.tfminecraft.simplefactions.guild.hub.SupplyHubCommands; @@ -34,7 +33,6 @@ import net.tfminecraft.simplefactions.utils.Formatter; import net.tfminecraft.simplefactions.utils.PostSettlementPayouts.PlayerUuidLookup; import net.tfminecraft.simplefactions.enums.FactionModifiers; -import net.tfminecraft.simplefactions.enums.GuildModifier; import net.tfminecraft.simplefactions.enums.Rules; import net.tfminecraft.simplefactions.government.proposal.TaxTarget; import net.tfminecraft.simplefactions.mercenary.company.MercenaryCompany; @@ -377,9 +375,6 @@ public double getIncome(Cashflow cashflow) { case SUPPLY_HUBS: amount = -SupplyHubService.dailyCost(guild); break; - case INFRASTRUCTURE_UPKEEP: - amount = -Math.max(0, GuildModifierOverride.resolve(guild, GuildModifier.INFRASTRUCTURE_UPKEEP)); - break; //Mercenary contracts case MERCENARY_CONTRACT: amount = getAggregatedContractEarnings(); @@ -624,7 +619,6 @@ public double getNetIncome() { case MILITARY_UPKEEP: case NODES: case SUPPLY_HUBS: - case INFRASTRUCTURE_UPKEEP: case PENALTIES: case GUILD_PAYMENTS: case OVERLORD_TAX: @@ -703,7 +697,6 @@ public double getDividendBase() { case MILITARY_UPKEEP: case NODES: case SUPPLY_HUBS: - case INFRASTRUCTURE_UPKEEP: case PENALTIES: case GUILD_PAYMENTS: case OVERLORD_TAX: @@ -1129,7 +1122,6 @@ private void applySettlementFor(Cashflow cf, DailyGuildTransfers buffer) { case NODES: // Supply hubs are a sink. Unpaid hubs were already removed, newest first. case SUPPLY_HUBS: - case INFRASTRUCTURE_UPKEEP: buffer.addExternalDelta(guild, getIncome(cf)); return; diff --git a/src/main/java/net/tfminecraft/simplefactions/loaders/ConfigLoader.java b/src/main/java/net/tfminecraft/simplefactions/loaders/ConfigLoader.java index e7308386..e3bf4efb 100644 --- a/src/main/java/net/tfminecraft/simplefactions/loaders/ConfigLoader.java +++ b/src/main/java/net/tfminecraft/simplefactions/loaders/ConfigLoader.java @@ -73,20 +73,6 @@ public void loadConfig(File configFile) { Cache.mercenaryDefaultBreachRefund = config.getDouble("mercenary-default-breach-refund", 500.0); net.tfminecraft.simplefactions.guild.hub.SupplyHubService.loadConfig(config); net.tfminecraft.simplefactions.guild.hub.HubTransport.loadConfig(config); - double infrastructureFull = config.getDouble("infrastructure.full", 20); - Cache.infrastructureFull = Double.isFinite(infrastructureFull) && infrastructureFull > 0 - ? infrastructureFull : 20; - Cache.infrastructureTarget = config.getDouble("infrastructure.target", 0.75); - Cache.infrastructureWildernessSpread = config.getDouble("infrastructure.wilderness-spread", 0.25); - double infrastructureSpreadFloor = config.getDouble("infrastructure.spread-floor", 0.5); - Cache.infrastructureSpreadFloor = Double.isFinite(infrastructureSpreadFloor) && infrastructureSpreadFloor > 0 - ? infrastructureSpreadFloor : 0.5; - Cache.infrastructureStation = config.getDouble("infrastructure.station", 10); - Cache.infrastructurePort = config.getDouble("infrastructure.port", 10); - Cache.infrastructureAirport = config.getDouble("infrastructure.airport", 5); - Cache.infrastructureTrack = config.getDouble("infrastructure.track", 10); - Cache.infrastructureTrackRefreshSeconds = Math.max(30, - config.getInt("infrastructure.track-refresh-seconds", 300)); Cache.settlementLargePopulationThreshold = config.getInt("settlement-large-population-threshold", 8); Cache.portSeaProximityBlocks = config.getInt("port-sea-proximity-blocks", 20); diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/CommandManager.java b/src/main/java/net/tfminecraft/simplefactions/managers/CommandManager.java index f10c3697..f1816611 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/CommandManager.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/CommandManager.java @@ -40,7 +40,6 @@ import net.tfminecraft.simplefactions.installation.Installation; import net.tfminecraft.simplefactions.installation.InstallationKind; import net.tfminecraft.simplefactions.installation.handler.ConstructResult; -import net.tfminecraft.simplefactions.managers.inventory.InstallationBuildConfirm; import net.tfminecraft.simplefactions.settlement.handler.CapitalResult; import net.tfminecraft.simplefactions.tiers.Title; import net.tfminecraft.simplefactions.tiers.admin.TitleAdminCommand; @@ -628,15 +627,15 @@ private boolean dispatchCommand(CommandSender sender, Command cmd, String label, return true; } String name = String.join(" ", Arrays.copyOfRange(args, 2, args.length)); - if (InstallationBuildConfirm.open( - p, - f, + ConstructResult result = f.getInstallationHandler().construct( kind, name, province, p.getLocation().getBlockX(), - p.getLocation().getBlockZ())) { - p.playSound(p, Sound.BLOCK_NOTE_BLOCK_BIT, 1f, 1f); + p.getLocation().getBlockZ()); + p.sendMessage(result.getMessage()); + if(result.isSuccess()) { + p.playSound(p, Sound.BLOCK_ANVIL_USE, 1f, 1f); } return true; } else if(cmd.getName().equalsIgnoreCase(cmd1) && args[0].equalsIgnoreCase("deconstruct")) { diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java b/src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java index 94a584a7..8a11abb0 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java @@ -37,7 +37,6 @@ import net.tfminecraft.simplefactions.managers.holder.SFInventoryHolder; import net.tfminecraft.simplefactions.managers.holder.WarInventoryHolder; import net.tfminecraft.simplefactions.managers.inventory.CampaignInstallationPickView; -import net.tfminecraft.simplefactions.managers.inventory.InstallationBuildConfirm; import net.tfminecraft.simplefactions.managers.inventory.CampaignRaidLaunchView; import net.tfminecraft.simplefactions.managers.inventory.CampaignView; import net.tfminecraft.simplefactions.managers.inventory.CompanyView; @@ -1002,8 +1001,6 @@ public void clearPendingOnQuit(PlayerQuitEvent e) { if (pendingCompanyFounds.remove(e.getPlayer()) != null) { confirming.remove(e.getPlayer()); } - InstallationBuildConfirm.forget(e.getPlayer()); - confirming.remove(e.getPlayer()); slotChanges.remove(e.getPlayer()); } @@ -1488,17 +1485,6 @@ public void clickButton(InventoryClickEvent e) { } return; } - key = new NamespacedKey(SimpleFactions.plugin, "installation_build"); - data = m.getPersistentDataContainer().get(key, PersistentDataType.STRING); - if (data != null) { - if (item.getType().equals(Material.RED_CONCRETE)) { - InstallationBuildConfirm.cancel(p); - } else { - InstallationBuildConfirm.accept(p); - } - confirming.remove(p); - return; - } key = new NamespacedKey(SimpleFactions.plugin, "installation"); data = m.getPersistentDataContainer().get(key, PersistentDataType.STRING); if(data != null) { diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java b/src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java index e274c0b4..09387e0f 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java @@ -25,8 +25,6 @@ import net.tfminecraft.simplefactions.guild.network.TradeGraph; import net.tfminecraft.simplefactions.map.provinces.Province; import net.tfminecraft.simplefactions.map.provinces.ProvinceDataEntry; -import net.tfminecraft.simplefactions.map.infra.InfrastructureSources; -import net.tfminecraft.simplefactions.map.infra.InfrastructureSpread; import net.tfminecraft.simplefactions.objects.Bracket; import net.tfminecraft.simplefactions.objects.Faction; import net.tfminecraft.simplefactions.Cache; @@ -48,8 +46,6 @@ public class ProvinceManager { /** Set on a copy so a preview keeps the graph and hubs it was given, off the server thread. */ private HighwaySnapshot highwayCapture; private Map> installationAccess = new HashMap<>(); - private boolean infrastructureSuppressed; - private Map extraInfrastructure; private long stateVersion = 0; private long lastCalculatedVersion = -1; @@ -106,19 +102,6 @@ public void setHighwayOverride(TradeGraph graph, Map> hubbe hubbedByGuild == null ? Map.of() : hubbedByGuild); } - /** Snapshot-only. The next recalculation writes 0 infrastructure on every province. */ - public void setInfrastructureSuppressed(boolean suppressed) { - infrastructureSuppressed = suppressed; - } - - /** - * Snapshot-only source added before the spread. Sea and water provinces are ignored. - * A null or empty map adds nothing. Copies do not inherit this. - */ - public void setExtraInfrastructure(Map extra) { - extraInfrastructure = extra == null || extra.isEmpty() ? null : Map.copyOf(extra); - } - private HighwaySnapshot highway() { return highwayCapture != null ? highwayCapture : HighwaySnapshot.current(); } @@ -151,7 +134,6 @@ public void recalculate() { try { installationAccess = new HashMap<>(); HubNetwork.refreshIfLive(this); - recalculateInfrastructure(); dropMissingGuilds(); for(Guild g : FactionManager.getAllGuilds()) { if (!g.hasCapital()) continue; @@ -182,7 +164,6 @@ public void recalculateQuiet(List guilds, List factions) { try { installationAccess = new HashMap<>(); HubNetwork.refreshIfLive(this); - recalculateInfrastructure(); dropExcept(previewGuilds); for (Guild guild : previewGuilds) { if (guild != null && guild.hasCapital()) { @@ -213,7 +194,6 @@ public void recalculateForSingleGuild(Guild g, boolean save) { InstallationAccess.beginRecalculation(); try { installationAccess = new HashMap<>(); - recalculateInfrastructure(); if (!g.hasCapital()) return; HubNetwork.refreshIfLive(this); dropMissingGuilds(); @@ -230,43 +210,6 @@ public void recalculateForSingleGuild(Guild g, boolean save) { } } - private void recalculateInfrastructure() { - if (infrastructureSuppressed) { - for (Province province : provinces.values()) { - province.setInfrastructure(0); - } - return; - } - Map graph = new HashMap<>(); - for (Province province : provinces.values()) { - Faction realm = InstallationAccess.topRealm( - previewFactions != null ? ownerOf(province.getId()) : province.getOwner()); - String origin = realm == null ? null : realm.getId().toLowerCase(Locale.ROOT); - graph.put(province.getId(), new InfrastructureSpread.Node( - province.getTradeCarry(), !province.isSea(), origin, province.getNeighbours())); - } - Map sources = InfrastructureSources.collect( - provinces, - previewGuilds != null ? previewGuilds : FactionManager.getAllGuilds(), - previewFactions != null ? previewFactions : FactionManager.getCopy(), - net.tfminecraft.simplefactions.map.infra.TrackProvinceCache.live().provinces()); - if (extraInfrastructure != null) { - for (Map.Entry extra : extraInfrastructure.entrySet()) { - Province province = provinces.get(extra.getKey()); - if (province == null || province.isSea() || extra.getValue() == null || extra.getValue() <= 0) { - continue; - } - sources.merge(extra.getKey(), extra.getValue(), Double::sum); - } - } - Map infrastructure = InfrastructureSpread.spread( - graph, sources, Cache.infrastructureWildernessSpread, Cache.infrastructureSpreadFloor); - for (Province province : provinces.values()) { - InfrastructureSpread.Arrival arrival = infrastructure.get(province.getId()); - province.setInfrastructure(arrival == null ? 0 : arrival.amount()); - } - } - private void recalculateProsperity() { for (Province p : provinces.values()) { p.calculateProsperity(); @@ -489,7 +432,6 @@ public void copyAllDataFrom(ProvinceManager source) { } dst.setProsperity(src.getProsperity()); - dst.setInfrastructure(src.getInfrastructure()); } } diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/BranchIncomePreviewService.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/BranchIncomePreviewService.java index de29d974..90881048 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/BranchIncomePreviewService.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/BranchIncomePreviewService.java @@ -1,6 +1,5 @@ package net.tfminecraft.simplefactions.managers.inventory; -import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; @@ -16,7 +15,6 @@ import net.tfminecraft.simplefactions.enums.GuildModifier; import net.tfminecraft.simplefactions.guild.Guild; import net.tfminecraft.simplefactions.guild.branch.Branch; -import net.tfminecraft.simplefactions.objects.Faction; import net.tfminecraft.simplefactions.guild.income.BranchIncomePreview; import net.tfminecraft.simplefactions.keys.Keys; @@ -52,9 +50,6 @@ public static void schedule( Map current = BranchIncomePreview.modifiers(guild); Map hypothetical = BranchIncomePreview.adjust(current, branch, level, levelDelta); double taxFraction = BranchIncomePreview.taxFraction(guild); - boolean realmWide = BranchIncomePreview.showsRealm(guild, branch); - List realmGuilds = realmWide ? BranchIncomePreview.guildsNow() : List.of(); - List realmFactions = realmWide ? BranchIncomePreview.factionsNow() : List.of(); String guildId = guild.getId(); String branchId = branch.getId(); long token = TOKENS.incrementAndGet(); @@ -64,10 +59,7 @@ public static void schedule( Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { double delta; try { - delta = realmWide - ? BranchIncomePreview.estimateRealm( - prepared, guild, current, hypothetical, realmGuilds, realmFactions) - : BranchIncomePreview.estimate(prepared, guild, current, hypothetical, taxFraction); + delta = BranchIncomePreview.estimate(prepared, guild, current, hypothetical, taxFraction); } catch (RuntimeException ex) { plugin.getLogger().log( Level.WARNING, 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 7f1a30ad..a8484787 100644 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreator.java +++ b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreator.java @@ -27,7 +27,6 @@ import net.tfminecraft.simplefactions.SimpleFactions; import net.tfminecraft.simplefactions.guild.branch.Branch; import net.tfminecraft.simplefactions.guild.branch.BranchModifier; -import net.tfminecraft.simplefactions.guild.income.BranchIncomePreview; import net.tfminecraft.simplefactions.guild.income.Cashflow; import net.tfminecraft.simplefactions.guild.income.Ledger; import net.tfminecraft.simplefactions.guild.income.LedgerHistory; @@ -356,7 +355,7 @@ private List upgradeLore(Guild guild, Branch branch, Double delta) { lore.add(""); lore.add(StringFormatter.formatHex("#d4c9aeCurrent Net Trade Income: #7fbd73"+net.tfminecraft.simplefactions.utils.Formatter.formatDouble( guild.getTradeBreakdown().getNetTradeIncome() - guild.getHubTaxBreakdown().getTotalTax()))); - lore.add(incomeChangeLine(delta, BranchIncomePreview.showsRealm(guild, branch))); + lore.add(incomeChangeLine(delta)); } lore.add(""); lore.add(StringFormatter.formatHex("#50e846§lClick to Upgrade")); @@ -403,7 +402,7 @@ private List downgradeLore(Guild guild, Branch branch, Double delta) { } else { lore.add(StringFormatter.formatHex("#d4c9aeCurrent Net Trade Income: #7fbd73"+net.tfminecraft.simplefactions.utils.Formatter.formatDouble( guild.getTradeBreakdown().getNetTradeIncome() - guild.getHubTaxBreakdown().getTotalTax()))); - lore.add(incomeChangeLine(delta, BranchIncomePreview.showsRealm(guild, branch))); + lore.add(incomeChangeLine(delta)); } lore.add(""); lore.add(StringFormatter.formatHex( @@ -414,16 +413,15 @@ private List downgradeLore(Guild guild, Branch branch, Double delta) { return lore; } - String incomeChangeLine(Double delta, boolean realm) { - String label = realm ? "Estimated Realm Income Change" : "Estimated Income Change"; + private String incomeChangeLine(Double delta) { if (delta == null) { - return StringFormatter.formatHex("#f2e5c2" + label + "#d6cf69: #7a706aCalculating..."); + return StringFormatter.formatHex("#f2e5c2Estimated Income Change#d6cf69: #7a706aCalculating..."); } if (delta.isNaN()) { return StringFormatter.formatHex("#cf493aIncome estimate unavailable"); } return StringFormatter.formatHex( - "#f2e5c2" + label + "#d6cf69: " + "#f2e5c2Estimated Income Change#d6cf69: " + (delta >= 0 ? "#4fd945+" : "#cf493a") + String.format("%.2f", delta) + "d/day" diff --git a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationBuildConfirm.java b/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationBuildConfirm.java deleted file mode 100644 index d371aa7d..00000000 --- a/src/main/java/net/tfminecraft/simplefactions/managers/inventory/InstallationBuildConfirm.java +++ /dev/null @@ -1,190 +0,0 @@ -package net.tfminecraft.simplefactions.managers.inventory; - -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; - -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Sound; -import org.bukkit.entity.Player; -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.guild.hub.InfrastructureMenuCopy; -import net.tfminecraft.simplefactions.guild.hub.InfrastructurePreview; -import net.tfminecraft.simplefactions.installation.InstallationKind; -import net.tfminecraft.simplefactions.installation.handler.ConstructResult; -import net.tfminecraft.simplefactions.managers.FactionManager; -import net.tfminecraft.simplefactions.managers.InventoryManager; -import net.tfminecraft.simplefactions.managers.ProvinceManager; -import net.tfminecraft.simplefactions.objects.Faction; - -/** Confirmation chest for /faction construct, with the installation preview painted in. */ -public final class InstallationBuildConfirm { - private static final AtomicLong TOKENS = new AtomicLong(); - private static final Map PENDING = new ConcurrentHashMap<>(); - - private InstallationBuildConfirm() {} - - private record Pending( - Faction faction, - InstallationKind kind, - String name, - int province, - int blockX, - int blockZ, - long token) {} - - public static boolean open( - Player player, - Faction faction, - InstallationKind kind, - String name, - int province, - int blockX, - int blockZ) { - SimpleFactions plugin = SimpleFactions.plugin; - InventoryManager menus = FactionManager.getInv(); - if (player == null || faction == null || kind == null || plugin == null || !plugin.isEnabled() || menus == null) { - if (player != null) { - player.sendMessage("§cCould not open the construction confirmation."); - } - return false; - } - long token = TOKENS.incrementAndGet(); - PENDING.put(player.getUniqueId(), new Pending(faction, kind, name, province, blockX, blockZ, token)); - menus.confirming.put(player, faction); - - Inventory inventory = plugin.getServer().createInventory(null, 27, "§7Confirm Action"); - inventory.setItem(13, paper(plugin, name, token, List.of(InfrastructureMenuCopy.calculating()))); - inventory.setItem(11, menus.createButton("confirm", "installation_build", "build")); - inventory.setItem(15, menus.createButton("cancel", "installation_build", "build")); - player.openInventory(inventory); - - ProvinceManager live = plugin.getProvinceManager(); - ProvinceManager snapshot = null; - if (live != null) { - snapshot = live.createSnapshotShell(); - snapshot.copyAllDataFrom(live); - } - ProvinceManager provinces = snapshot; - Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { - List lore; - try { - InfrastructurePreview.InstallationPreview preview = InfrastructurePreview.previewInstallation( - provinces, faction, province, kind); - lore = InfrastructureMenuCopy.installationPreview(preview); - } catch (RuntimeException ex) { - plugin.getLogger().log( - Level.WARNING, - "Installation preview failed for " + kind + " in province " + province, - ex); - lore = List.of("§cIncome estimate unavailable"); - } - if (!plugin.isEnabled()) { - return; - } - List lines = lore; - Bukkit.getScheduler().runTask(plugin, () -> publish(player, token, lines)); - }); - return true; - } - - public static void accept(Player player) { - if (player == null) { - return; - } - Pending pending = PENDING.remove(player.getUniqueId()); - if (pending == null || pending.faction == null) { - player.sendMessage("§cThat construction is no longer waiting."); - player.closeInventory(); - return; - } - if (pending.faction.getLeader() == null - || !pending.faction.getLeader().equalsIgnoreCase(player.getName())) { - player.sendMessage("§cYou need to be a faction leader to construct installations"); - player.closeInventory(); - return; - } - if (pending.faction.getInstallationHandler() == null) { - player.sendMessage("§cCould not construct that installation."); - player.closeInventory(); - return; - } - ConstructResult result = pending.faction.getInstallationHandler().construct( - pending.kind, pending.name, pending.province, pending.blockX, pending.blockZ); - player.sendMessage(result.getMessage()); - player.playSound( - player, - result.isSuccess() ? Sound.BLOCK_ANVIL_USE : Sound.BLOCK_NOTE_BLOCK_BIT, - 1f, - 1f); - player.closeInventory(); - } - - public static void cancel(Player player) { - if (player == null) { - return; - } - PENDING.remove(player.getUniqueId()); - player.closeInventory(); - player.playSound(player, Sound.BLOCK_NOTE_BLOCK_BIT, 1f, 1f); - } - - public static void forget(Player player) { - if (player != null) { - PENDING.remove(player.getUniqueId()); - } - } - - @SuppressWarnings("deprecation") - private static void publish(Player player, long token, List lore) { - if (!player.isOnline() || player.getOpenInventory() == null) { - return; - } - if (!"§7Confirm Action".equals(player.getOpenInventory().getTitle())) { - return; - } - Inventory inventory = player.getOpenInventory().getTopInventory(); - ItemStack item = inventory.getItem(13); - if (!matches(item, token)) { - return; - } - ItemMeta meta = item.getItemMeta(); - meta.setLore(lore); - item.setItemMeta(meta); - inventory.setItem(13, item); - } - - @SuppressWarnings("deprecation") - private static ItemStack paper(SimpleFactions plugin, String name, long token, List lore) { - ItemStack item = new ItemStack(Material.PAPER); - ItemMeta meta = item.getItemMeta(); - meta.setDisplayName(name == null || name.isBlank() ? "§6Installation" : "§6" + name); - meta.setLore(lore); - meta.getPersistentDataContainer().set(tokenKey(plugin), PersistentDataType.LONG, token); - item.setItemMeta(meta); - return item; - } - - private static boolean matches(ItemStack item, long token) { - SimpleFactions plugin = SimpleFactions.plugin; - if (plugin == null || item == null || !item.hasItemMeta()) { - return false; - } - Long stamped = item.getItemMeta().getPersistentDataContainer().get(tokenKey(plugin), PersistentDataType.LONG); - return stamped != null && stamped == token; - } - - private static NamespacedKey tokenKey(SimpleFactions plugin) { - return new NamespacedKey(plugin, "installation_preview"); - } -} diff --git a/src/main/java/net/tfminecraft/simplefactions/map/Compiler.java b/src/main/java/net/tfminecraft/simplefactions/map/Compiler.java index 3d71ae7e..9414a776 100644 --- a/src/main/java/net/tfminecraft/simplefactions/map/Compiler.java +++ b/src/main/java/net/tfminecraft/simplefactions/map/Compiler.java @@ -7,7 +7,6 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import com.google.gson.Gson; @@ -16,7 +15,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import net.tfminecraft.simplefactions.Cache; import net.tfminecraft.simplefactions.SimpleFactions; import net.tfminecraft.simplefactions.guild.Guild; import net.tfminecraft.simplefactions.managers.FactionManager; @@ -24,7 +22,6 @@ import net.tfminecraft.simplefactions.managers.TitleManager; import net.tfminecraft.simplefactions.managers.WarManager; import net.tfminecraft.simplefactions.map.export.OccupationMapExport; -import net.tfminecraft.simplefactions.map.infra.EffectiveTerrain; import net.tfminecraft.simplefactions.map.provinces.Province; import net.tfminecraft.simplefactions.map.provinces.ProvinceDataEntry; @@ -96,20 +93,6 @@ static JsonObject provinceToJson(Province p, String occupierId) { } o.add("trade", trade); - if (!p.isSea()) { - double terrain = p.getTradeCarry(); - double infrastructure = p.getInfrastructure(); - o.addProperty("terrain", p.getTerrain().name().toLowerCase(Locale.ROOT)); - o.addProperty("terrain_value", terrain); - if (infrastructure > 0 && Double.isFinite(Cache.infrastructureFull) && Cache.infrastructureFull > 0) { - o.addProperty("infrastructure", r2(infrastructure)); - o.addProperty("infrastructure_fill", r2(Math.max(0, Math.min(1, - infrastructure / Cache.infrastructureFull)))); - } - o.addProperty("effective_terrain", r2(EffectiveTerrain.calculate(terrain, infrastructure, - Cache.infrastructureFull, Cache.infrastructureTarget, 1))); - } - return o; } diff --git a/src/main/java/net/tfminecraft/simplefactions/map/infra/EffectiveTerrain.java b/src/main/java/net/tfminecraft/simplefactions/map/infra/EffectiveTerrain.java deleted file mode 100644 index 6f1f9b03..00000000 --- a/src/main/java/net/tfminecraft/simplefactions/map/infra/EffectiveTerrain.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.tfminecraft.simplefactions.map.infra; - -public final class EffectiveTerrain { - private EffectiveTerrain() {} - - public static double calculate(double terrain, double infrastructure, double full, double target, double access) { - if (terrain >= target || infrastructure <= 0 || full <= 0) return terrain; - double fill = Math.min(1, infrastructure / full); - return terrain + (target - terrain) * fill * access; - } -} diff --git a/src/main/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSources.java b/src/main/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSources.java deleted file mode 100644 index d646a38e..00000000 --- a/src/main/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSources.java +++ /dev/null @@ -1,50 +0,0 @@ -package net.tfminecraft.simplefactions.map.infra; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import net.tfminecraft.simplefactions.Cache; -import net.tfminecraft.simplefactions.enums.GuildModifier; -import net.tfminecraft.simplefactions.guild.Guild; -import net.tfminecraft.simplefactions.guild.GuildModifierOverride; -import net.tfminecraft.simplefactions.installation.Installation; -import net.tfminecraft.simplefactions.map.provinces.Province; -import net.tfminecraft.simplefactions.objects.Faction; - -public final class InfrastructureSources { - private InfrastructureSources() {} - - public static Map collect( - Map provinces, Collection guilds, Collection factions, - Set trackProvinces) { - Map sources = new HashMap<>(); - for (Guild guild : guilds) { - if (guild == null || !guild.isBase() || !guild.hasCapital()) continue; - add(provinces, sources, guild.getCapital(), GuildModifierOverride.resolve(guild, GuildModifier.INFRASTRUCTURE)); - } - for (Faction faction : factions) { - if (faction == null || faction.getInstallationHandler() == null) continue; - for (Installation installation : faction.getInstallationHandler().getAll()) { - double amount = switch (installation.getKind()) { - case TRAIN_STATION -> Cache.infrastructureStation; - case PORT -> Cache.infrastructurePort; - case AIRPORT -> Cache.infrastructureAirport; - default -> 0; - }; - add(provinces, sources, installation.getProvince(), amount); - } - } - for (Integer id : trackProvinces) { - add(provinces, sources, id, Cache.infrastructureTrack); - } - return sources; - } - - private static void add(Map provinces, Map sources, int id, double amount) { - Province province = provinces.get(id); - if (province == null || province.isSea() || amount <= 0) return; - sources.merge(id, amount, Double::sum); - } -} diff --git a/src/main/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSpread.java b/src/main/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSpread.java deleted file mode 100644 index 5c41a59d..00000000 --- a/src/main/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSpread.java +++ /dev/null @@ -1,49 +0,0 @@ -package net.tfminecraft.simplefactions.map.infra; - -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.PriorityQueue; - -public final class InfrastructureSpread { - private InfrastructureSpread() {} - - public record Node(double terrain, boolean land, String realm, Collection neighbours) {} - public record Arrival(double amount, String origin) {} - private record Step(int province, Arrival arrival) {} - - public static Map spread( - Map provinces, Map sources, double wilderness, double floor) { - Map best = new HashMap<>(); - PriorityQueue pending = new PriorityQueue<>( - Comparator.comparingDouble((Step step) -> step.arrival().amount()).reversed() - .thenComparingInt(Step::province)); - for (Map.Entry source : sources.entrySet()) { - Node province = provinces.get(source.getKey()); - if (province == null || !province.land() || source.getValue() <= 0) continue; - Arrival arrival = new Arrival(source.getValue(), province.realm()); - best.put(source.getKey(), arrival); - pending.add(new Step(source.getKey(), arrival)); - } - while (!pending.isEmpty()) { - Step step = pending.remove(); - Arrival here = step.arrival(); - if (!here.equals(best.get(step.province())) || here.amount() < floor) continue; - for (Integer neighbour : provinces.get(step.province()).neighbours()) { - Node entered = provinces.get(neighbour); - if (entered == null || !entered.land()) continue; - if (entered.realm() != null && !Objects.equals(entered.realm(), here.origin())) continue; - double amount = here.amount() * entered.terrain(); - if (entered.realm() == null) amount *= wilderness; - Arrival previous = best.get(neighbour); - if (amount < floor || (previous != null && amount <= previous.amount())) continue; - Arrival arrival = new Arrival(amount, here.origin()); - best.put(neighbour, arrival); - pending.add(new Step(neighbour, arrival)); - } - } - return best; - } -} diff --git a/src/main/java/net/tfminecraft/simplefactions/map/infra/TrackProvinceCache.java b/src/main/java/net/tfminecraft/simplefactions/map/infra/TrackProvinceCache.java index 652018e6..8c4db506 100644 --- a/src/main/java/net/tfminecraft/simplefactions/map/infra/TrackProvinceCache.java +++ b/src/main/java/net/tfminecraft/simplefactions/map/infra/TrackProvinceCache.java @@ -23,7 +23,7 @@ public Set provinces() { public void vehicleFrameworkUnavailable(Consumer info) { if (!vehicleFrameworkUnavailableLogged) { vehicleFrameworkUnavailableLogged = true; - info.accept("VehicleFramework is not enabled; railway track gives no infrastructure."); + info.accept("VehicleFramework is not enabled; railway track is not read."); } } @@ -35,7 +35,7 @@ public boolean refresh(Supplier> sampler, Runnable recalculate, Con sampled = Set.of(); if (!warned) { warned = true; - warning.accept("Could not read VehicleFramework tracks; track infrastructure is disabled."); + warning.accept("Could not read VehicleFramework tracks; track provinces are treated as empty."); } } if (provinces.equals(sampled)) return false; diff --git a/src/main/java/net/tfminecraft/simplefactions/map/provinces/Province.java b/src/main/java/net/tfminecraft/simplefactions/map/provinces/Province.java index 3a686be3..4d790145 100644 --- a/src/main/java/net/tfminecraft/simplefactions/map/provinces/Province.java +++ b/src/main/java/net/tfminecraft/simplefactions/map/provinces/Province.java @@ -17,8 +17,6 @@ import net.tfminecraft.simplefactions.managers.TitleManager; import net.tfminecraft.simplefactions.objects.Faction; import net.tfminecraft.simplefactions.objects.FactionModifier; -import net.tfminecraft.simplefactions.map.infra.EffectiveTerrain; -import net.tfminecraft.simplefactions.guild.network.InstallationAccess; import net.tfminecraft.simplefactions.utils.ModifierMerger; import net.tfminecraft.simplefactions.enums.FactionModifiers; import net.tfminecraft.simplefactions.enums.GuildModifier; @@ -34,7 +32,6 @@ public class Province { private int centerX; private int centerZ; private double prosperity = 0; - private double infrastructure = 0; private final Set neighbours = new HashSet<>(); public Province() { @@ -91,7 +88,7 @@ public void calculateTrade( double amount; double carry = GuildModifierOverride.resolve(guild, GuildModifier.TRADE_CARRY); double effectiveDistance = distance / Math.pow(carry, 1.1); - double factor = Math.pow(getTradeCarry(guild), effectiveDistance); + double factor = Math.pow(getTradeCarry(), effectiveDistance); if (prev == -1) { // Capital province amount = net.tfminecraft.simplefactions.government.stability.StabilityDebuffs.realmSeed( @@ -133,7 +130,7 @@ public void calculateProduction( int distance ) { double amount; - double terrainFactor = Math.pow(getTradeCarry(guild), 0.5); + double terrainFactor = Math.pow(getTradeCarry(), 0.5); double factor = terrainFactor*getTradeFactor(guild); if (prev == null) { @@ -170,7 +167,7 @@ public void calculateProduction( public double getTradeFactor(Guild guild) { double trade = getGuildTrade(guild); if(trade == 0) return 0.05; - double K = 2.5 / Math.pow(getTradeCarry(guild), 0.5); + double K = 2.5 / Math.pow(getTradeCarry(), 0.5); return (trade / (trade + K)); } @@ -323,7 +320,7 @@ private void spreadHubProduction( ProvinceDataEntry prev, int distance ) { - double factor = Math.pow(getTradeCarry(guild), 0.5) * getTradeFactor(guild); + double factor = Math.pow(getTradeCarry(), 0.5) * getTradeFactor(guild); double amount = prev.getProduction() * factor; if (amount < 0.1) return; @@ -350,15 +347,6 @@ public double getTradeCarry() { return Cache.tradeCarry.getOrDefault(terrain, 0.5); } - public double getTradeCarry(Guild guild) { - double terrain = getTradeCarry(); - if (guild == null || isSea() || infrastructure <= 0 || terrain >= Cache.infrastructureTarget) return terrain; - return EffectiveTerrain.calculate(terrain, infrastructure, Cache.infrastructureFull, - Cache.infrastructureTarget, InstallationAccess.of(guild.getFaction(), getOwner())); - } - - public double getInfrastructure() { return infrastructure; } - public void setInfrastructure(double amount) { infrastructure = amount; } public Set getNeighbours() { return Collections.unmodifiableSet(neighbours); } diff --git a/src/main/resources/Guilds/branches.yml b/src/main/resources/Guilds/branches.yml index dff85d27..c0502ff6 100644 --- a/src/main/resources/Guilds/branches.yml +++ b/src/main/resources/Guilds/branches.yml @@ -17,7 +17,6 @@ bureaucracy: - trade_power 1 0.5 - production 4 0.5 - trade_upkeep 0 0.03 - - infrastructure 0 0.1 - prestige 0 5 - prestige_bonus 0 0.5 guild_halls: @@ -86,26 +85,6 @@ supply_lines: - hub_upkeep 1 0.5 - trade_upkeep 0 0.01 - prestige 0 5 -infrastructure: - name: "#86d1b0Infrastructure" - icon: "rail.0" - group: 3 - description: - - "#86d1b0Infrastructure #d4c9aeimproves poor" - - "#d4c9aeland in the realm for every guild," - - "#d4c9aestarting at the capital. Hubs come" - - "#d4c9aemore slowly; each level costs daily upkeep." - allowed-types: - - realm - modifiers: - - infrastructure 0 2 - - infrastructure_upkeep 0 1 - - hub_limit 2 0.25 - - hub_trade 0 0.05 - - hub_production 0 0.08 - - hub_upkeep 1 0.5 - - trade_upkeep 0 0.01 - - prestige 0 5 counting_houses: name: "#a9744fCounting Houses" icon: "chest_minecart.0" diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 8574295d..474b544c 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -190,18 +190,6 @@ elevation-size-multiplier: 1.0 elevation-exponent: 1.1 eviction-multiplier: 2.0 -# Infrastructure closes the gap from raw land terrain to target at full strength. -infrastructure: - full: 20 - target: 0.75 - wilderness-spread: 0.25 - spread-floor: 0.5 - station: 10 - port: 10 - airport: 5 - track: 10 - track-refresh-seconds: 300 - terrain-modifiers: - FARMLAND 0.95 - PLAINS 0.85 diff --git a/src/test/java/net/tfminecraft/simplefactions/guild/GuildSavedInfrastructureBranchTest.java b/src/test/java/net/tfminecraft/simplefactions/guild/GuildSavedInfrastructureBranchTest.java new file mode 100644 index 00000000..2c6ff662 --- /dev/null +++ b/src/test/java/net/tfminecraft/simplefactions/guild/GuildSavedInfrastructureBranchTest.java @@ -0,0 +1,107 @@ +package net.tfminecraft.simplefactions.guild; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BannerMeta; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +import net.tfminecraft.simplefactions.database.GuildData; +import net.tfminecraft.simplefactions.database.JsonUtil; +import net.tfminecraft.simplefactions.guild.branch.Branch; +import net.tfminecraft.simplefactions.guild.upgrade.Upgrade; +import net.tfminecraft.simplefactions.loaders.BranchLoader; +import net.tfminecraft.simplefactions.loaders.GuildLoader; +import net.tfminecraft.simplefactions.loaders.UpgradeLoader; +import net.tfminecraft.simplefactions.objects.Faction; + +class GuildSavedInfrastructureBranchTest { + private final Map savedBranches = new HashMap<>(BranchLoader.map); + private final Map savedTypes = new HashMap<>(GuildLoader.map); + private final Map savedUpgrades = new LinkedHashMap<>(UpgradeLoader.map); + + @AfterEach + void restore() { + BranchLoader.map.clear(); + BranchLoader.map.putAll(savedBranches); + GuildLoader.map.clear(); + GuildLoader.map.putAll(savedTypes); + UpgradeLoader.map.clear(); + UpgradeLoader.map.putAll(savedUpgrades); + } + + @Test + void aSavedInfrastructureBranchIsSkipped() { + loadBundledBranches(); + GuildData data = JsonUtil.GSON.fromJson(""" + { + "id": "realm-guild", + "name": "The Realm", + "leader": "Ada", + "type": "realm", + "capital": 4, + "banner": ["white"], + "wealth modifiers": [], + "branches": [ + {"id": "infrastructure", "level": 4}, + {"id": "bureaucracy", "level": 2} + ] + } + """, GuildData.class); + World world = mock(World.class); + when(world.getChunkAt(0, 0)).thenReturn(mock(Chunk.class)); + + try (MockedStatic bukkit = mockStatic(Bukkit.class); + MockedConstruction items = mockConstruction(ItemStack.class, (mock, context) -> + when(mock.getItemMeta()).thenReturn(mock(BannerMeta.class)))) { + bukkit.when(() -> Bukkit.getWorld(nullable(String.class))).thenReturn(world); + bukkit.when(Bukkit::getLogger).thenReturn(mock(Logger.class)); + + Guild guild = new Guild(data, mock(Faction.class)); + + assertEquals("realm-guild", guild.getId()); + assertNull(guild.getBranch("infrastructure")); + Branch bureaucracy = guild.getBranch("bureaucracy"); + assertNotNull(bureaucracy); + assertEquals(2, bureaucracy.getLevel()); + assertEquals(1, items.constructed().size()); + } + } + + private static void loadBundledBranches() { + YamlConfiguration types = new YamlConfiguration(); + types.set("guild.name", "Guild"); + types.set("realm.name", "Realm"); + types.set("realm.base", true); + GuildLoader.map.clear(); + 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(); + UpgradeLoader.map.clear(); + for (String key : config.getKeys(false)) { + BranchLoader.map.put(key, new Branch(key, config.getConfigurationSection(key))); + } + } +} diff --git a/src/test/java/net/tfminecraft/simplefactions/guild/hub/InfrastructureMenuCopyTest.java b/src/test/java/net/tfminecraft/simplefactions/guild/hub/InfrastructureMenuCopyTest.java deleted file mode 100644 index 38c3a91a..00000000 --- a/src/test/java/net/tfminecraft/simplefactions/guild/hub/InfrastructureMenuCopyTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.tfminecraft.simplefactions.guild.hub; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.List; - -import org.junit.jupiter.api.Test; - -import net.tfminecraft.simplefactions.guild.hub.InfrastructurePreview.InstallationPreview; - -class InfrastructureMenuCopyTest { - @Test - void headlineUsesTheCachedDailyFigure() { - assertEquals( - "§7Infrastructure is worth about +45 a day to your realm", - InfrastructureMenuCopy.headline(45)); - assertEquals( - "§7Infrastructure is worth about -3 a day to your realm", - InfrastructureMenuCopy.headline(-3)); - assertEquals( - "§7Infrastructure worth is worked out once a day.", - InfrastructureMenuCopy.headlineUnknown()); - } - - @Test - void installationPreviewNamesInfrastructureIncomeAndUpkeep() { - assertEquals( - List.of( - "§a+10 infrastructure here", - "§7about +6 a day for your realm", - "§7upkeep 5"), - InfrastructureMenuCopy.installationPreview(new InstallationPreview(10, 6, 5))); - assertEquals( - List.of( - "§7This adds no infrastructure here", - "§7about +0 a day for your realm", - "§7upkeep 30"), - InfrastructureMenuCopy.installationPreview(new InstallationPreview(0, 0, 30))); - assertTrue(InfrastructureMenuCopy.installationPreview(null).get(0).contains("Working out")); - } -} 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 5e625db5..fd2effb5 100644 --- a/src/test/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubServiceTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/guild/hub/SupplyHubServiceTest.java @@ -525,28 +525,6 @@ void supplyLinesHubLimitAddsOneHubEveryTwoLevels() { } } - @Test - void ledger_infrastructureUpkeepIsShownAndSettledWithoutShedding() { - Guild guild = mock(Guild.class); - Faction faction = mock(Faction.class); - Bank bank = mock(Bank.class); - when(bank.getWealth()).thenReturn(0.0); - when(guild.getModifier(GuildModifier.INFRASTRUCTURE_UPKEEP)).thenReturn(4.0); - Ledger ledger = ledger(faction, guild, bank); - - assertTrue(Cashflow.INFRASTRUCTURE_UPKEEP.getDisplay().contains("Infrastructure")); - assertEquals(-4.0, ledger.getIncome(Cashflow.INFRASTRUCTURE_UPKEEP), 1e-9); - assertEquals(-4.0, ledger.getNetIncome(), 1e-9); - DailyGuildTransfers buffer = new DailyGuildTransfers(); - ledger.populateDailyTransfers(buffer); - assertEquals(-4.0, buffer.getExternalDeltas().get(guild), 1e-9); - - Guild withoutInfrastructure = mock(Guild.class); - when(withoutInfrastructure.getModifier(GuildModifier.INFRASTRUCTURE_UPKEEP)).thenReturn(0.0); - Ledger noInfrastructureLedger = ledger(faction, withoutInfrastructure, bank); - assertEquals(0.0, noInfrastructureLedger.getIncome(Cashflow.INFRASTRUCTURE_UPKEEP), 1e-9); - } - @Test void ledger_removesEveryHubWhenNoneCanBePaid() { Guild guild = mock(Guild.class); 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 6b60bc2d..4592b7b6 100644 --- a/src/test/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreviewTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreviewTest.java @@ -1,7 +1,6 @@ package net.tfminecraft.simplefactions.guild.income; 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 static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -41,7 +40,6 @@ import net.tfminecraft.simplefactions.managers.ProvinceManager; import net.tfminecraft.simplefactions.map.provinces.Province; import net.tfminecraft.simplefactions.objects.Faction; -import net.tfminecraft.simplefactions.objects.handler.GuildHandler; class BranchIncomePreviewTest { private final List savedFactions = new ArrayList<>(); @@ -201,70 +199,4 @@ void hubTradeBranchNoLongerChangesIncome() { assertEquals(0, live.previewUpgradeIncomeExact(guild, supplyLines)); assertEquals(0, supplyLines.getLevel()); } - - @Test - void branchPreviewIncludesInfrastructureUpkeepChange() { - YamlConfiguration yaml = new YamlConfiguration(); - yaml.set("group", 3); - yaml.set("modifiers", List.of("INFRASTRUCTURE_UPKEEP 0 1")); - Branch infrastructure = new Branch(new Branch("infrastructure", yaml), 2); - when(guild.getModifier(GuildModifier.INFRASTRUCTURE_UPKEEP)).thenReturn(2.0); - - assertEquals(-1.0, live.previewUpgradeIncomeExact(guild, infrastructure)); - assertEquals(1.0, live.previewDowngradeIncomeExact(guild, infrastructure)); - assertEquals(2, infrastructure.getLevel()); - assertFalse(BranchIncomePreview.showsRealm(guild, infrastructure)); - } - - @Test - void infrastructureOnTheRealmGuildCountsEveryGuild() { - Cache.tradeCarry.put(Terrain.PLAINS, 0.4); - Guild realmGuild = guild("realm", true); - Guild other = guild("fields-two", false); - GuildHandler handler = mock(GuildHandler.class); - when(handler.getGuilds()).thenReturn(List.of(realmGuild)); - when(faction.getId()).thenReturn("home"); - when(faction.getGuildHandler()).thenReturn(handler); - FactionManager.factions.add(faction); - - YamlConfiguration yaml = new YamlConfiguration(); - yaml.set("group", 3); - yaml.set("modifiers", List.of("INFRASTRUCTURE 0 20")); - Branch infrastructure = new Branch(new Branch("infrastructure", yaml), 0); - assertTrue(BranchIncomePreview.showsRealm(realmGuild, infrastructure)); - assertFalse(BranchIncomePreview.showsRealm(other, infrastructure)); - assertFalse(BranchIncomePreview.showsRealm(realmGuild, branch)); - - BranchIncomePreview.Prepared prepared = BranchIncomePreview.prepare(live); - Map current = BranchIncomePreview.modifiers(realmGuild); - Map raised = BranchIncomePreview.adjust(current, infrastructure, 0, 1); - double alone = BranchIncomePreview.estimateRealm( - prepared, realmGuild, current, raised, - BranchIncomePreview.guildsNow(), BranchIncomePreview.factionsNow()); - when(handler.getGuilds()).thenReturn(List.of(realmGuild, other)); - double both = BranchIncomePreview.estimateRealm( - prepared, realmGuild, current, raised, - BranchIncomePreview.guildsNow(), BranchIncomePreview.factionsNow()); - - assertTrue(alone > 0, "alone " + alone); - assertTrue(both > alone, "both " + both + " alone " + alone); - assertEquals(0, infrastructure.getLevel()); - } - - private Guild guild(String id, boolean base) { - Guild created = mock(Guild.class); - when(created.getId()).thenReturn(id); - when(created.isBase()).thenReturn(base); - when(created.hasCapital()).thenReturn(true); - when(created.getCapital()).thenReturn(capital.getId()); - when(created.getFaction()).thenReturn(faction); - when(created.getModifier(any())).thenAnswer(invocation -> switch ((GuildModifier) invocation.getArgument(0)) { - case TRADE_POWER -> 8.0; - case TRADE_CARRY -> 1.2; - case PRODUCTION -> 6.0; - case TRADE_UPKEEP -> 0.05; - default -> 0.0; - }); - return created; - } } diff --git a/src/test/java/net/tfminecraft/simplefactions/loaders/ConfigLoaderInfrastructureTest.java b/src/test/java/net/tfminecraft/simplefactions/loaders/ConfigLoaderInfrastructureTest.java deleted file mode 100644 index 2f96c2f6..00000000 --- a/src/test/java/net/tfminecraft/simplefactions/loaders/ConfigLoaderInfrastructureTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package net.tfminecraft.simplefactions.loaders; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import net.tfminecraft.simplefactions.Cache; - -class ConfigLoaderInfrastructureTest { - @TempDir - Path tempDir; - private final double[] previous = values(); - - @AfterEach - void restore() { - Cache.infrastructureFull = previous[0]; - Cache.infrastructureTarget = previous[1]; - Cache.infrastructureWildernessSpread = previous[2]; - Cache.infrastructureSpreadFloor = previous[3]; - Cache.infrastructureStation = previous[4]; - Cache.infrastructurePort = previous[5]; - Cache.infrastructureAirport = previous[6]; - Cache.infrastructureTrack = previous[7]; - Cache.infrastructureTrackRefreshSeconds = (int) previous[8]; - } - - @Test - void missingSectionUsesDefaultsAndResetsPreviousSettings() throws IOException { - load(""" - infrastructure: - full: 30 - target: 0.80 - wilderness-spread: 0.10 - spread-floor: 0.25 - station: 12 - port: 8 - airport: 4 - """); - load("enable-map: false\n"); - - assertValues(20, 0.75, 0.25, 0.5, 10, 10, 5, 10, 300); - } - - @Test - void missingKeysUseDefaultsIndependently() throws IOException { - load(""" - infrastructure: - full: 30 - """); - - assertValues(30, 0.75, 0.25, 0.5, 10, 10, 5, 10, 300); - } - - @Test - void configuredKeysAreReadIntoCache() throws IOException { - load(""" - infrastructure: - full: 30 - target: 0.80 - wilderness-spread: 0.10 - spread-floor: 0.25 - station: 12 - port: 8 - airport: 4 - track: 17 - track-refresh-seconds: 45 - """); - - assertValues(30, 0.80, 0.10, 0.25, 12, 8, 4, 17, 45); - } - - @Test - void zeroFullUsesDefault() throws IOException { - load("infrastructure:\n full: 0\n"); - - assertEquals(20, Cache.infrastructureFull, 1e-9); - } - - @Test - void negativeFullUsesDefault() throws IOException { - load("infrastructure:\n full: -5\n"); - - assertEquals(20, Cache.infrastructureFull, 1e-9); - } - - @Test - void zeroSpreadFloorUsesDefault() throws IOException { - load("infrastructure:\n spread-floor: 0\n"); - - assertEquals(0.5, Cache.infrastructureSpreadFloor, 1e-9); - } - - @Test - void negativeSpreadFloorUsesDefault() throws IOException { - load("infrastructure:\n spread-floor: -0.25\n"); - - assertEquals(0.5, Cache.infrastructureSpreadFloor, 1e-9); - } - - @Test - void positiveSpreadFloorIsKept() throws IOException { - load("infrastructure:\n spread-floor: 0.25\n"); - - assertEquals(0.25, Cache.infrastructureSpreadFloor, 1e-9); - } - - private void load(String yaml) throws IOException { - Path file = tempDir.resolve("config.yml"); - Files.writeString(file, yaml); - new ConfigLoader().loadConfig(file.toFile()); - } - - private static double[] values() { - return new double[] {Cache.infrastructureFull, Cache.infrastructureTarget, - Cache.infrastructureWildernessSpread, Cache.infrastructureSpreadFloor, - Cache.infrastructureStation, Cache.infrastructurePort, Cache.infrastructureAirport, - Cache.infrastructureTrack, Cache.infrastructureTrackRefreshSeconds}; - } - - private static void assertValues(double... expected) { - double[] actual = values(); - for (int i = 0; i < expected.length; i++) assertEquals(expected[i], actual[i], 1e-9); - } -} diff --git a/src/test/java/net/tfminecraft/simplefactions/loaders/ProvinceLoaderInfrastructureTest.java b/src/test/java/net/tfminecraft/simplefactions/loaders/ProvinceLoaderInfrastructureTest.java new file mode 100644 index 00000000..7ab3bae4 --- /dev/null +++ b/src/test/java/net/tfminecraft/simplefactions/loaders/ProvinceLoaderInfrastructureTest.java @@ -0,0 +1,37 @@ +package net.tfminecraft.simplefactions.loaders; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import net.tfminecraft.simplefactions.enums.Terrain; +import net.tfminecraft.simplefactions.map.provinces.Province; + +class ProvinceLoaderInfrastructureTest { + @TempDir + Path dir; + + @Test + void aSavedInfrastructureSegmentDoesNotStopTheProvinceLoading() throws Exception { + Path provinces = dir.resolve("provinces.txt"); + Path neighbours = dir.resolve("neighbours.json"); + Files.writeString(provinces, "1 = 10,64,20;bog;40;12\n2 = 30,64,40;plains;7\n"); + Files.writeString(neighbours, "{\"1\":[2],\"2\":[1]}"); + + Map loaded = new ProvinceLoader().loadProvinces( + provinces.toFile(), neighbours.toFile()); + + assertEquals(Terrain.BOG, loaded.get(1).getTerrain()); + assertEquals(40, loaded.get(1).getFertility()); + assertEquals(10, loaded.get(1).getCenterX()); + assertEquals(20, loaded.get(1).getCenterZ()); + assertEquals(Terrain.PLAINS, loaded.get(2).getTerrain()); + assertEquals(7, loaded.get(2).getFertility()); + assertEquals(1, loaded.get(1).getNeighbours().size()); + } +} diff --git a/src/test/java/net/tfminecraft/simplefactions/loaders/SupplyHubBranchesTest.java b/src/test/java/net/tfminecraft/simplefactions/loaders/SupplyHubBranchesTest.java index 46373845..df0855e4 100644 --- a/src/test/java/net/tfminecraft/simplefactions/loaders/SupplyHubBranchesTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/loaders/SupplyHubBranchesTest.java @@ -60,11 +60,11 @@ void bundledBranchesLoadForGuildsAndRealms() { Branch freightRealm = BranchLoader.getByGroup(realmType, 4); assertNotNull(supplyGuild); - assertNotNull(supplyRealm); + assertNull(supplyRealm); assertNotNull(freightGuild); assertNotNull(freightRealm); assertEquals("supply_lines", supplyGuild.getId()); - assertEquals("infrastructure", supplyRealm.getId()); + assertNull(BranchLoader.getByString("infrastructure")); assertEquals("counting_houses", freightRealm.getId()); assertEquals(2.0, supplyGuild.getModifier(GuildModifier.HUB_LIMIT).getBase()); assertEquals(0.5, supplyGuild.getModifier(GuildModifier.HUB_LIMIT).getPerLevel()); @@ -73,12 +73,7 @@ void bundledBranchesLoadForGuildsAndRealms() { assertEquals(-0.01, freightRealm.getModifier(GuildModifier.TRADE_UPKEEP).getPerLevel()); assertEquals(1.0, supplyGuild.getModifier(GuildModifier.HUB_UPKEEP).getBase()); assertEquals(0.5, supplyGuild.getModifier(GuildModifier.HUB_UPKEEP).getPerLevel()); - Branch infrastructure = new Branch(BranchLoader.getByString("infrastructure"), 10); - assertEquals(20.0, infrastructure.getAmount(GuildModifier.INFRASTRUCTURE)); - assertEquals(10.0, infrastructure.getAmount(GuildModifier.INFRASTRUCTURE_UPKEEP)); - Branch bureaucracy = new Branch(BranchLoader.getByString("bureaucracy"), 5); - assertEquals(20.5, infrastructure.getAmount(GuildModifier.INFRASTRUCTURE) - + bureaucracy.getAmount(GuildModifier.INFRASTRUCTURE)); + assertNotNull(BranchLoader.getByString("bureaucracy")); Branch storehouses = new Branch(BranchLoader.getByString("storehouses"), 3); Branch supplyLines = new Branch(BranchLoader.getByString("supply_lines"), 4); assertEquals(0.70, storehouses.getAmount(GuildModifier.TRADE_CARRY)); @@ -94,7 +89,7 @@ void bundledBranchesLoadForGuildsAndRealms() { } @Test - void savedBranchesMoveToTheAllowedBranchAtTheSameLevel() { + void savedSupplyLinesStayWhenTheRealmHasNoBranchInThatGroup() { loadBundledBranches(); Branch supplyLines = new Branch(BranchLoader.getByString("supply_lines"), 3); Map realmBranches = new HashMap<>(Map.of(3, supplyLines)); @@ -102,7 +97,7 @@ void savedBranchesMoveToTheAllowedBranchAtTheSameLevel() { BranchLoader.replaceDisallowedBranches(realmBranches, GuildLoader.getByString("realm")); BranchLoader.replaceDisallowedBranches(normalBranches, GuildLoader.getByString("guild")); - assertEquals("infrastructure", realmBranches.get(3).getId()); + assertEquals("supply_lines", realmBranches.get(3).getId()); assertEquals(3, realmBranches.get(3).getLevel()); assertEquals("supply_lines", normalBranches.get(3).getId()); assertEquals(3, normalBranches.get(3).getLevel()); diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreatorIncomeLineTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreatorIncomeLineTest.java deleted file mode 100644 index ad54ef36..00000000 --- a/src/test/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreatorIncomeLineTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.tfminecraft.simplefactions.managers.inventory; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class GuildCreatorIncomeLineTest { - @Test - void aRealmBranchNamesTheWholeRealm() { - GuildCreator creator = new GuildCreator(); - String realm = creator.incomeChangeLine(12.5, true); - String guild = creator.incomeChangeLine(12.5, false); - String waiting = creator.incomeChangeLine(null, true); - - assertTrue(realm.contains("Estimated Realm Income Change"), realm); - assertTrue(realm.contains("+12.50"), realm); - assertTrue(guild.contains("Estimated Income Change"), guild); - assertTrue(!guild.contains("Realm"), guild); - assertTrue(waiting.contains("Estimated Realm Income Change"), waiting); - assertTrue(waiting.contains("Calculating"), waiting); - } -} diff --git a/src/test/java/net/tfminecraft/simplefactions/map/CompilerProvinceExportTest.java b/src/test/java/net/tfminecraft/simplefactions/map/CompilerProvinceExportTest.java index 82659bd8..27c5e03a 100644 --- a/src/test/java/net/tfminecraft/simplefactions/map/CompilerProvinceExportTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/map/CompilerProvinceExportTest.java @@ -2,109 +2,26 @@ 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 static org.mockito.Mockito.mock; -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.google.gson.JsonObject; -import net.tfminecraft.simplefactions.Cache; -import net.tfminecraft.simplefactions.enums.Terrain; import net.tfminecraft.simplefactions.guild.Guild; import net.tfminecraft.simplefactions.map.provinces.Province; import net.tfminecraft.simplefactions.map.provinces.ProvinceDataEntry; class CompilerProvinceExportTest { - private final Map previousCarry = new HashMap<>(Cache.tradeCarry); - private final double previousFull = Cache.infrastructureFull; - private final double previousTarget = Cache.infrastructureTarget; - - @BeforeEach - void setUp() { - Cache.infrastructureFull = 20; - Cache.infrastructureTarget = 0.75; - Cache.tradeCarry.clear(); - Cache.tradeCarry.put(Terrain.BOG, 0.40); - Cache.tradeCarry.put(Terrain.PLAINS, 0.75); - Cache.tradeCarry.put(Terrain.SEA, 0.60); - Cache.tradeCarry.put(Terrain.WATER, 0.75); - } - - @AfterEach - void restore() { - Cache.infrastructureFull = previousFull; - Cache.infrastructureTarget = previousTarget; - Cache.tradeCarry.clear(); - Cache.tradeCarry.putAll(previousCarry); - } - - @Test - void bogWithInfrastructureExportsFillAndEffectiveTerrain() { - Province province = new Province(1, "bog", 0); - province.setInfrastructure(12); - - JsonObject json = Compiler.provinceToJson(province, null); - - assertEquals("bog", json.get("terrain").getAsString()); - assertEquals(0.40, json.get("terrain_value").getAsDouble()); - assertEquals(12, json.get("infrastructure").getAsDouble()); - assertEquals(0.60, json.get("infrastructure_fill").getAsDouble()); - assertEquals(0.61, json.get("effective_terrain").getAsDouble()); - } - @Test - void terrainValueIsNotRounded() { - Cache.tradeCarry.put(Terrain.BOG, 0.445); - - JsonObject json = Compiler.provinceToJson(new Province(1, "bog", 0), null); - - assertEquals(0.445, json.get("terrain_value").getAsDouble()); - } - - @Test - void invalidInfrastructureFullOmitsInfrastructureFields() { - Cache.infrastructureFull = -5; - Province province = new Province(1, "bog", 0); - province.setInfrastructure(12); - - JsonObject json = Compiler.provinceToJson(province, null); - - assertFalse(json.has("infrastructure")); - assertFalse(json.has("infrastructure_fill")); - } - - @Test - void bogWithoutInfrastructureOmitsInfrastructureFields() { - JsonObject json = Compiler.provinceToJson(new Province(1, "bog", 0), null); - - assertEquals(0.40, json.get("effective_terrain").getAsDouble()); - assertFalse(json.has("infrastructure")); - assertFalse(json.has("infrastructure_fill")); - } - - @Test - void infrastructureDoesNotRaisePlainsAboveItsTerrainValue() { - Province province = new Province(1, "plains", 0); - province.setInfrastructure(20); - - assertEquals(0.75, Compiler.provinceToJson(province, null).get("effective_terrain").getAsDouble()); - } - - @Test - void seaAndWaterOmitAllNewTerrainFields() { - for (String terrain : new String[] {"sea", "water"}) { + void provincesOmitInfrastructureAndEffectiveTerrain() { + for (String terrain : new String[] {"bog", "plains", "sea", "water"}) { JsonObject json = Compiler.provinceToJson(new Province(1, terrain, 0), null); - assertFalse(json.has("terrain")); - assertFalse(json.has("terrain_value")); assertFalse(json.has("infrastructure")); assertFalse(json.has("infrastructure_fill")); assertFalse(json.has("effective_terrain")); + assertFalse(json.has("terrain")); + assertFalse(json.has("terrain_value")); } } @@ -122,6 +39,6 @@ void tradeObjectAndExistingFieldOrderArePreserved() { assertEquals("prosperity", json.keySet().toArray()[1]); assertEquals("occupied_by", json.keySet().toArray()[2]); assertEquals("trade", json.keySet().toArray()[3]); - assertTrue(json.has("effective_terrain")); + assertEquals(4, json.keySet().size()); } } diff --git a/src/test/java/net/tfminecraft/simplefactions/map/infra/EffectiveTerrainTest.java b/src/test/java/net/tfminecraft/simplefactions/map/infra/EffectiveTerrainTest.java deleted file mode 100644 index d791740c..00000000 --- a/src/test/java/net/tfminecraft/simplefactions/map/infra/EffectiveTerrainTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.tfminecraft.simplefactions.map.infra; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -class EffectiveTerrainTest { - @Test - void zeroInfrastructureLeavesRawTerrainAlone() { - for (double terrain : new double[] {0.80, 0.75, 0.40, 0.30}) { - assertEquals(terrain, EffectiveTerrain.calculate(terrain, 0, 20, 0.75, 1)); - } - } - - @Test - void fullInfrastructureFillsTheGapAndStopsAtFull() { - assertEquals(0.75, EffectiveTerrain.calculate(0.40, 20, 20, 0.75, 1), 1e-9); - assertEquals(0.75, EffectiveTerrain.calculate(0.40, 40, 20, 0.75, 1), 1e-9); - } - - @Test - void halfInfrastructureFillsHalfTheGap() { - assertEquals(0.575, EffectiveTerrain.calculate(0.40, 10, 20, 0.75, 1), 1e-9); - } - - @Test - void accessScalesTheGap() { - assertEquals(0.575, EffectiveTerrain.calculate(0.40, 20, 20, 0.75, 0.5), 1e-9); - assertEquals(0.40, EffectiveTerrain.calculate(0.40, 20, 20, 0.75, 0), 1e-9); - } - - @Test - void terrainAtOrAboveTheTargetIsUnchangedAtAnyFill() { - for (double infrastructure : new double[] {0, 10, 20, 40}) { - assertEquals(0.80, EffectiveTerrain.calculate(0.80, infrastructure, 20, 0.75, 1)); - assertEquals(0.75, EffectiveTerrain.calculate(0.75, infrastructure, 20, 0.75, 1)); - } - } -} diff --git a/src/test/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSourcesTest.java b/src/test/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSourcesTest.java deleted file mode 100644 index 5612fc9a..00000000 --- a/src/test/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSourcesTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package net.tfminecraft.simplefactions.map.infra; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import net.tfminecraft.simplefactions.Cache; -import net.tfminecraft.simplefactions.enums.GuildModifier; -import net.tfminecraft.simplefactions.guild.Guild; -import net.tfminecraft.simplefactions.guild.GuildModifierOverride; -import net.tfminecraft.simplefactions.installation.Installation; -import net.tfminecraft.simplefactions.installation.InstallationKind; -import net.tfminecraft.simplefactions.installation.handler.InstallationHandler; -import net.tfminecraft.simplefactions.map.provinces.Province; -import net.tfminecraft.simplefactions.objects.Faction; - -class InfrastructureSourcesTest { - private final double station = Cache.infrastructureStation; - private final double port = Cache.infrastructurePort; - private final double airport = Cache.infrastructureAirport; - private final double track = Cache.infrastructureTrack; - - @AfterEach - void restore() { - GuildModifierOverride.clear(); - Cache.infrastructureStation = station; - Cache.infrastructurePort = port; - Cache.infrastructureAirport = airport; - Cache.infrastructureTrack = track; - } - - @Test - void realmCapitalUsesTheResolvedModifierOnce() { - Guild guild = guild(true, 1, 12.5); - Map sources = InfrastructureSources.collect( - Map.of(1, province(1, "forest")), List.of(guild), List.of(), Set.of()); - - assertEquals(12.5, sources.get(1)); - verify(guild, times(1)).getModifier(GuildModifier.INFRASTRUCTURE); - assertTrue(GuildModifier.INFRASTRUCTURE.isPositive()); - assertEquals(GuildModifier.PRODUCTION.scalesWithInactivity(), GuildModifier.INFRASTRUCTURE.scalesWithInactivity()); - } - - @Test - void capitalUsesPreviewOverrideInsteadOfTheLiveModifier() { - Guild guild = guild(true, 1, 12.5); - GuildModifierOverride.use(guild, Map.of(GuildModifier.INFRASTRUCTURE, 17.0)); - Map sources = InfrastructureSources.collect( - Map.of(1, province(1, "forest")), List.of(guild), List.of(), Set.of()); - - assertEquals(17, sources.get(1)); - verify(guild, never()).getModifier(GuildModifier.INFRASTRUCTURE); - } - - @Test - void nonBaseGuildsAndGuildsWithoutCapitalsAddNothing() { - Guild regular = guild(false, 1, 12.5); - Guild noCapital = guild(true, 1, 12.5); - when(noCapital.hasCapital()).thenReturn(false); - - assertTrue(InfrastructureSources.collect(Map.of(1, province(1, "forest")), - List.of(regular, noCapital), List.of(), Set.of()).isEmpty()); - verify(regular, never()).getModifier(GuildModifier.INFRASTRUCTURE); - verify(noCapital, never()).getModifier(GuildModifier.INFRASTRUCTURE); - } - - @Test - void installationsUseConfigAmountsWithoutScalingByLevelAndFortsAddNothing() { - Cache.infrastructureStation = 7; - Cache.infrastructurePort = 11; - Cache.infrastructureAirport = 3; - Installation station = installation(InstallationKind.TRAIN_STATION, 1); - Installation port = installation(InstallationKind.PORT, 2); - Installation airport = installation(InstallationKind.AIRPORT, 3); - Installation fort = installation(InstallationKind.FORT, 4); - Map sources = InfrastructureSources.collect(Map.of( - 1, province(1, "forest"), 2, province(2, "forest"), - 3, province(3, "forest"), 4, province(4, "forest")), List.of(), - List.of(faction(station, port, airport, fort)), Set.of()); - - assertEquals(Map.of(1, 7.0, 2, 11.0, 3, 3.0), sources); - verify(station, never()).getLevel(); - verify(port, never()).getLevel(); - verify(airport, never()).getLevel(); - } - - @Test - void sourcesInOneProvinceAddBeforeTheySpread() { - Cache.infrastructureStation = 10; - Guild guild = guild(true, 1, 10); - Map sources = InfrastructureSources.collect(Map.of( - 1, province(1, "forest"), 2, province(2, "plains")), List.of(guild), - List.of(faction(installation(InstallationKind.TRAIN_STATION, 1))), Set.of()); - Map result = InfrastructureSpread.spread(Map.of( - 1, new InfrastructureSpread.Node(0.6, true, "realm", List.of(2)), - 2, new InfrastructureSpread.Node(0.75, true, "realm", List.of(1))), sources, 0.25, 0.5); - - assertEquals(20, sources.get(1)); - assertEquals(15, result.get(2).amount()); - } - - @Test - void sourcesOutsideTheManagerAndOnSeaOrWaterAreIgnored() { - Map sources = InfrastructureSources.collect(Map.of( - 1, province(1, "sea"), 2, province(2, "water")), - List.of(guild(true, 1, 10), guild(true, 2, 10), guild(true, 3, 10)), - List.of(faction(installation(InstallationKind.PORT, 1), - installation(InstallationKind.TRAIN_STATION, 2), - installation(InstallationKind.AIRPORT, 3))), Set.of()); - - assertTrue(sources.isEmpty()); - } - - @Test - void trackProvincesAddOnceAndCombineWithOtherSources() { - Cache.infrastructureTrack = 10; - Map sources = InfrastructureSources.collect(Map.of( - 1, province(1, "forest"), 2, province(2, "sea"), 3, province(3, "plains")), - List.of(), List.of(faction(installation(InstallationKind.TRAIN_STATION, 1))), Set.of(1, 2, 3)); - - assertEquals(Map.of(1, 20.0, 3, 10.0), sources); - } - - @Test - void trackProvinceSourceSpreadsOnlyThroughUnownedLand() { - Map provinces = Map.of(1, province(1, "plains"), 2, province(2, "plains")); - Map sources = InfrastructureSources.collect(provinces, List.of(), List.of(), Set.of(1)); - Map result = InfrastructureSpread.spread(Map.of( - 1, new InfrastructureSpread.Node(0.75, true, null, List.of(2)), - 2, new InfrastructureSpread.Node(0.75, true, "realm", List.of(1))), sources, 0.25, 0.5); - - assertEquals(10, result.get(1).amount()); - assertTrue(!result.containsKey(2)); - } - - private static Province province(int id, String terrain) { - return new Province(id, terrain, 0); - } - - private static Guild guild(boolean base, int capital, double amount) { - Guild guild = mock(Guild.class); - when(guild.isBase()).thenReturn(base); - when(guild.hasCapital()).thenReturn(true); - when(guild.getCapital()).thenReturn(capital); - when(guild.getModifier(GuildModifier.INFRASTRUCTURE)).thenReturn(amount); - return guild; - } - - private static Installation installation(InstallationKind kind, int province) { - Installation installation = mock(Installation.class); - when(installation.getKind()).thenReturn(kind); - when(installation.getProvince()).thenReturn(province); - when(installation.getLevel()).thenReturn(5); - return installation; - } - - private static Faction faction(Installation... installations) { - Faction faction = mock(Faction.class); - InstallationHandler handler = mock(InstallationHandler.class); - when(faction.getInstallationHandler()).thenReturn(handler); - when(handler.getAll()).thenReturn(List.of(installations)); - return faction; - } -} diff --git a/src/test/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSpreadTest.java b/src/test/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSpreadTest.java deleted file mode 100644 index dc924b7c..00000000 --- a/src/test/java/net/tfminecraft/simplefactions/map/infra/InfrastructureSpreadTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package net.tfminecraft.simplefactions.map.infra; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import net.tfminecraft.simplefactions.map.infra.InfrastructureSpread.Arrival; -import net.tfminecraft.simplefactions.map.infra.InfrastructureSpread.Node; - -class InfrastructureSpreadTest { - @Test - void eachHopMultipliesByTheEnteredRawTerrain() { - Map result = spread(Map.of( - 1, land(0.4, "realm", 2), - 2, land(0.75, "realm", 1, 3), - 3, land(0.75, "realm", 2)), Map.of(1, 10.0)); - - assertEquals(7.5, result.get(2).amount(), 1e-9); - assertEquals(5.625, result.get(3).amount(), 1e-9); - assertEquals("realm", result.get(3).origin()); - } - - @Test - void aWorsePathDoesNotReplaceTheBetterArrival() { - Map result = spread(Map.of( - 1, land(0.75, "realm", 2, 3), - 2, land(0.75, "realm", 1, 4), - 3, land(0.4, "realm", 1, 4), - 4, land(0.75, "realm", 2, 3)), Map.of(1, 10.0)); - - assertEquals(5.625, result.get(4).amount(), 1e-9); - } - - @Test - void waterCannotBeEnteredOrUsedAsASource() { - Map result = spread(Map.of( - 1, land(0.75, "realm", 2), - 2, new Node(0.75, false, "realm", List.of(1, 3)), - 3, land(0.75, "realm", 2)), Map.of(1, 10.0, 2, 100.0)); - - assertFalse(result.containsKey(2)); - assertFalse(result.containsKey(3)); - } - - @Test - void anotherRealmBlocksSpread() { - Map result = spread(Map.of( - 1, land(0.75, "home", 2), - 2, land(0.75, "foreign", 1, 3), - 3, land(0.75, "home", 2)), Map.of(1, 10.0)); - - assertFalse(result.containsKey(2)); - assertFalse(result.containsKey(3)); - } - - @Test - void everyUnownedProvinceEnteredGetsTheWildernessPenalty() { - Map graph = Map.of( - 1, land(0.75, "home", 2), - 2, land(0.4, null, 1, 3), - 3, land(0.75, null, 2)); - Map result = InfrastructureSpread.spread(graph, Map.of(1, 10.0), 0.25, 0.1); - - assertEquals(10 * 0.40 * 0.25, result.get(2).amount(), 1e-9); - assertEquals(10 * 0.40 * 0.25 * 0.75 * 0.25, result.get(3).amount(), 1e-9); - assertFalse(spread(graph, Map.of(1, 10.0)).containsKey(3)); - } - - @Test - void unownedSourcesStayInUnownedLand() { - Map result = spread(Map.of( - 1, land(0.75, null, 2, 3), - 2, land(0.75, "realm", 1), - 3, land(0.75, null, 1)), Map.of(1, 10.0)); - - assertFalse(result.containsKey(2)); - assertEquals(1.875, result.get(3).amount(), 1e-9); - assertNull(result.get(3).origin()); - } - - @Test - void ownedSourcesCanReenterTheirRealmFromWilderness() { - Map result = spread(Map.of( - 1, land(0.75, "home", 2), - 2, land(0.4, null, 1, 3, 4), - 3, land(0.75, "home", 2), - 4, land(0.75, "foreign", 2)), Map.of(1, 10.0)); - - assertEquals(0.75, result.get(3).amount(), 1e-9); - assertEquals("home", result.get(3).origin()); - assertFalse(result.containsKey(4)); - } - - @Test - void arrivalsBelowTheFloorStopButAnArrivalAtTheFloorContinues() { - Map result = spread(Map.of( - 1, land(0.75, "realm", 2, 3), - 2, land(0.4, "realm", 1, 4), - 3, land(0.5, "realm", 1, 5), - 4, land(0.75, "realm", 2), - 5, land(1, "realm", 3)), Map.of(1, 1.0)); - - assertFalse(result.containsKey(2)); - assertFalse(result.containsKey(4)); - assertEquals(0.5, result.get(3).amount(), 1e-9); - assertEquals(0.5, result.get(5).amount(), 1e-9); - } - - @Test - void theWinningArrivalKeepsItsOriginAcrossWilderness() { - Map result = spread(Map.of( - 1, land(0.75, "first", 3), - 2, land(0.75, "second", 3), - 3, land(0.75, null, 1, 2, 4, 5), - 4, land(0.75, "first", 3), - 5, land(0.75, "second", 3)), Map.of(1, 10.0, 2, 20.0)); - - assertEquals("second", result.get(3).origin()); - assertFalse(result.containsKey(4)); - assertEquals(2.8125, result.get(5).amount(), 1e-9); - } - - private static Node land(double terrain, String realm, Integer... neighbours) { - return new Node(terrain, true, realm, List.of(neighbours)); - } - - private static Map spread(Map graph, Map sources) { - return InfrastructureSpread.spread(graph, sources, 0.25, 0.5); - } -} diff --git a/src/test/java/net/tfminecraft/simplefactions/map/infra/ProvinceInfrastructureTest.java b/src/test/java/net/tfminecraft/simplefactions/map/infra/ProvinceInfrastructureTest.java deleted file mode 100644 index a112a621..00000000 --- a/src/test/java/net/tfminecraft/simplefactions/map/infra/ProvinceInfrastructureTest.java +++ /dev/null @@ -1,249 +0,0 @@ -package net.tfminecraft.simplefactions.map.infra; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; - -import net.tfminecraft.simplefactions.Cache; -import net.tfminecraft.simplefactions.enums.GuildModifier; -import net.tfminecraft.simplefactions.enums.Terrain; -import net.tfminecraft.simplefactions.guild.Guild; -import net.tfminecraft.simplefactions.guild.GuildModifierOverride; -import net.tfminecraft.simplefactions.guild.hub.HubNetwork; -import net.tfminecraft.simplefactions.guild.income.EconomicPreview; -import net.tfminecraft.simplefactions.guild.income.TradeBreakdown; -import net.tfminecraft.simplefactions.installation.Installation; -import net.tfminecraft.simplefactions.installation.InstallationKind; -import net.tfminecraft.simplefactions.installation.handler.InstallationHandler; -import net.tfminecraft.simplefactions.managers.FactionManager; -import net.tfminecraft.simplefactions.managers.ProvinceManager; -import net.tfminecraft.simplefactions.managers.TitleManager; -import net.tfminecraft.simplefactions.map.provinces.Province; -import net.tfminecraft.simplefactions.map.provinces.ProvinceDataEntry; -import net.tfminecraft.simplefactions.objects.Faction; - -class ProvinceInfrastructureTest { - private final Map previousCarry = new HashMap<>(Cache.tradeCarry); - private final boolean previousEnabled = Cache.provincesEnabled; - private final double previousFull = Cache.infrastructureFull; - private final double previousTarget = Cache.infrastructureTarget; - private final double previousWilderness = Cache.infrastructureWildernessSpread; - private final double previousFloor = Cache.infrastructureSpreadFloor; - private final double previousStation = Cache.infrastructureStation; - private final double previousMaxUpkeep = Cache.maxTradeUpkeep; - private MockedStatic factions; - private MockedStatic titles; - private MockedStatic hubs; - private Guild guild; - private Faction faction; - - @BeforeEach - void setUp() { - Cache.provincesEnabled = true; - Cache.infrastructureFull = 20; - Cache.infrastructureTarget = 0.75; - Cache.infrastructureWildernessSpread = 0.25; - Cache.infrastructureSpreadFloor = 0.5; - Cache.infrastructureStation = 10; - Cache.maxTradeUpkeep = 0.75; - for (Terrain terrain : Terrain.values()) Cache.tradeCarry.put(terrain, 0.5); - Cache.tradeCarry.put(Terrain.FARMLAND, 0.8); - Cache.tradeCarry.put(Terrain.PLAINS, 0.75); - Cache.tradeCarry.put(Terrain.BOG, 0.4); - Cache.tradeCarry.put(Terrain.MOUNTAIN, 0.3); - factions = mockStatic(FactionManager.class); - titles = mockStatic(TitleManager.class); - hubs = mockStatic(HubNetwork.class); - faction = mock(Faction.class); - when(faction.getId()).thenReturn("realm"); - guild = mock(Guild.class); - when(guild.getId()).thenReturn("guild"); - when(guild.getFaction()).thenReturn(faction); - when(guild.getTradeBreakdown()).thenReturn(new TradeBreakdown()); - when(guild.getModifier(GuildModifier.TRADE_CARRY)).thenReturn(1.0); - } - - @AfterEach - void restore() { - GuildModifierOverride.clear(); - hubs.close(); - titles.close(); - factions.close(); - Cache.tradeCarry.clear(); - Cache.tradeCarry.putAll(previousCarry); - Cache.provincesEnabled = previousEnabled; - Cache.infrastructureFull = previousFull; - Cache.infrastructureTarget = previousTarget; - Cache.infrastructureWildernessSpread = previousWilderness; - Cache.infrastructureSpreadFloor = previousFloor; - Cache.infrastructureStation = previousStation; - Cache.maxTradeUpkeep = previousMaxUpkeep; - } - - @Test - void anEmptySourceMapLeavesGuildCarryEqualToRawOnEveryTerrain() { - Map map = new HashMap<>(); - for (Terrain terrain : Terrain.values()) { - int id = terrain.ordinal() + 1; - map.put(id, new Province(id, terrain.name(), 0)); - } - ProvinceManager manager = manager(map); - manager.recalculate(); - - for (Province province : manager.getProvinces()) { - assertEquals(0, province.getInfrastructure()); - assertEquals(province.getTradeCarry(), province.getTradeCarry(guild)); - } - } - - @Test - void bothRecalculationPathsRebuildSourcesFromCurrentModifiers() { - Province first = new Province(1, "bog", 0); - Province second = new Province(2, "plains", 0); - first.addNeighbour(2); - second.addNeighbour(1); - titles.when(() -> TitleManager.getByProvince(1)).thenReturn(faction); - titles.when(() -> TitleManager.getByProvince(2)).thenReturn(faction); - capitalSource(10); - ProvinceManager manager = manager(Map.of(1, first, 2, second)); - - manager.recalculate(); - assertEquals(10, first.getInfrastructure()); - assertEquals(7.5, second.getInfrastructure(), 1e-9); - assertEquals(0.575, first.getTradeCarry(guild), 1e-9); - when(guild.getModifier(GuildModifier.INFRASTRUCTURE)).thenReturn(20.0); - manager.recalculateForSingleGuild(guild, false); - assertEquals(20, first.getInfrastructure()); - assertEquals(15, second.getInfrastructure(), 1e-9); - when(guild.getModifier(GuildModifier.INFRASTRUCTURE)).thenReturn(0.0); - manager.recalculate(); - assertEquals(0, first.getInfrastructure()); - assertEquals(first.getTradeCarry(), first.getTradeCarry(guild)); - } - - @Test - void snapshotsRebuildTheirOwnInfrastructureAndDoNotChangeTheLiveMap() { - Province capital = new Province(1, "bog", 0); - titles.when(() -> TitleManager.getByProvince(1)).thenReturn(faction); - capitalSource(10); - ProvinceManager live = manager(Map.of(1, capital)); - live.recalculate(); - EconomicPreview.Prepared prepared = EconomicPreview.prepare(live); - ProvinceManager snapshot = EconomicPreview.copyOf(prepared); - - assertNotSame(capital, snapshot.get(1)); - assertEquals(10, snapshot.get(1).getInfrastructure()); - GuildModifierOverride.use(guild, Map.of(GuildModifier.INFRASTRUCTURE, 20.0)); - snapshot.recalculate(); - GuildModifierOverride.clear(); - - assertEquals(20, snapshot.get(1).getInfrastructure()); - assertEquals(10, capital.getInfrastructure()); - assertEquals(10, EconomicPreview.copyOf(prepared).get(1).getInfrastructure()); - } - - @Test - void snapshotsReadInstallationsAtRecalculationTime() { - InstallationHandler handler = mock(InstallationHandler.class); - when(faction.getInstallationHandler()).thenReturn(handler); - factions.when(FactionManager::getCopy).thenReturn(List.of(faction)); - ProvinceManager live = manager(Map.of(1, new Province(1, "bog", 0))); - EconomicPreview.Prepared prepared = EconomicPreview.prepare(live); - Installation station = mock(Installation.class); - when(station.getKind()).thenReturn(InstallationKind.TRAIN_STATION); - when(station.getProvince()).thenReturn(1); - when(handler.getAll()).thenReturn(List.of(station)); - ProvinceManager snapshot = EconomicPreview.copyOf(prepared); - snapshot.recalculate(); - - assertEquals(10, snapshot.get(1).getInfrastructure()); - assertEquals(0, live.get(1).getInfrastructure()); - when(handler.getAll()).thenReturn(List.of()); - snapshot.recalculate(); - assertEquals(0, snapshot.get(1).getInfrastructure()); - } - - @Test - void singleGuildRecalculationRebuildsInstallationsEvenWithoutACapital() { - InstallationHandler handler = mock(InstallationHandler.class); - Installation station = mock(Installation.class); - when(station.getKind()).thenReturn(InstallationKind.TRAIN_STATION); - when(station.getProvince()).thenReturn(1); - when(handler.getAll()).thenReturn(List.of(station)); - when(faction.getInstallationHandler()).thenReturn(handler); - factions.when(FactionManager::getCopy).thenReturn(List.of(faction)); - ProvinceManager manager = manager(Map.of(1, new Province(1, "bog", 0))); - - manager.recalculateForSingleGuild(guild, false); - - assertEquals(10, manager.get(1).getInfrastructure()); - } - - @Test - void effectiveTerrainIsUsedByTradeProductionAndUpkeep() { - Province province = new Province(1, "bog", 0); - province.setInfrastructure(20); - ProvinceManager manager = manager(Map.of(1, province)); - province.calculateTrade(manager, guild, 10, 1); - - assertEquals(7.5, province.getRawGuildTrade(guild), 1e-9); - double factor = 7.5 / (7.5 + 2.5 / Math.sqrt(0.75)); - assertEquals(factor, province.getTradeFactor(guild), 1e-9); - province.calculateProduction(manager, guild, new ProvinceDataEntry(guild, 0, 10), 1); - assertEquals(10 * Math.sqrt(0.75) * factor, province.getGuildProduction(guild), 1e-9); - when(guild.getModifier(GuildModifier.TRADE_UPKEEP)).thenReturn(1.0); - province.setProsperity(100); - double expectedNet = Math.round(100 * (1 - factor) * 100) / 100.0; - assertEquals(expectedNet, manager.getIncome(guild, false), 1e-9); - assertEquals(0.4, province.getTradeCarry()); - assertEquals(0.4, Cache.getTradeCarry(Terrain.BOG)); - } - - @Test - void hubProductionSpreadUsesEffectiveTerrain() { - Province source = new Province(1, "plains", 0); - Province destination = new Province(2, "bog", 0); - source.addNeighbour(2); - destination.setInfrastructure(20); - destination.setData(guild.getId(), new ProvinceDataEntry(guild, 10, 0)); - ProvinceManager manager = manager(Map.of(1, source, 2, destination)); - source.seedProduction(manager, guild, 100); - - double factor = 10 / (10 + 2.5 / Math.sqrt(0.75)); - assertEquals(100 * Math.sqrt(0.75) * factor, destination.getGuildProduction(guild), 1e-9); - } - - @Test - void seaAndWaterCarryRemainRawEvenIfInfrastructureIsSet() { - for (Terrain terrain : List.of(Terrain.SEA, Terrain.WATER)) { - Province province = new Province(1, terrain.name(), 0); - province.setInfrastructure(20); - assertEquals(province.getTradeCarry(), province.getTradeCarry(guild)); - } - } - - private void capitalSource(double amount) { - when(guild.isBase()).thenReturn(true); - when(guild.hasCapital()).thenReturn(true); - when(guild.getCapital()).thenReturn(1); - when(guild.getModifier(GuildModifier.INFRASTRUCTURE)).thenReturn(amount); - factions.when(FactionManager::getAllGuilds).thenReturn(List.of(guild)); - } - - private static ProvinceManager manager(Map provinces) { - ProvinceManager manager = new ProvinceManager(); - manager.start(provinces); - return manager; - } -} diff --git a/src/test/java/net/tfminecraft/simplefactions/map/infra/TrackProvinceCacheTest.java b/src/test/java/net/tfminecraft/simplefactions/map/infra/TrackProvinceCacheTest.java index 71ed649d..bfbebd77 100644 --- a/src/test/java/net/tfminecraft/simplefactions/map/infra/TrackProvinceCacheTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/map/infra/TrackProvinceCacheTest.java @@ -47,7 +47,7 @@ void vehicleFrameworkAbsentLogsOnlyOnce() { assertFalse(cache.refresh(() -> { cache.vehicleFrameworkUnavailable(message -> { - assertEquals("VehicleFramework is not enabled; railway track gives no infrastructure.", message); + assertEquals("VehicleFramework is not enabled; railway track is not read.", message); infoMessages.incrementAndGet(); }); return Set.of(); diff --git a/src/test/java/net/tfminecraft/simplefactions/map/provinces/ProvinceTradeCarryTest.java b/src/test/java/net/tfminecraft/simplefactions/map/provinces/ProvinceTradeCarryTest.java new file mode 100644 index 00000000..8edf0156 --- /dev/null +++ b/src/test/java/net/tfminecraft/simplefactions/map/provinces/ProvinceTradeCarryTest.java @@ -0,0 +1,105 @@ +package net.tfminecraft.simplefactions.map.provinces; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import net.tfminecraft.simplefactions.Cache; +import net.tfminecraft.simplefactions.enums.GuildModifier; +import net.tfminecraft.simplefactions.enums.Terrain; +import net.tfminecraft.simplefactions.guild.Guild; +import net.tfminecraft.simplefactions.guild.income.TradeBreakdown; +import net.tfminecraft.simplefactions.managers.ProvinceManager; +import net.tfminecraft.simplefactions.managers.TitleManager; +import net.tfminecraft.simplefactions.objects.Faction; + +class ProvinceTradeCarryTest { + private final Map previousCarry = new HashMap<>(Cache.tradeCarry); + private final boolean previousEnabled = Cache.provincesEnabled; + private final double previousMaxUpkeep = Cache.maxTradeUpkeep; + private MockedStatic titles; + private Guild guild; + + @BeforeEach + void setUp() { + Cache.provincesEnabled = true; + Cache.maxTradeUpkeep = 0.75; + Cache.tradeCarry.put(Terrain.PLAINS, 0.75); + Cache.tradeCarry.put(Terrain.BOG, 0.4); + Cache.tradeCarry.put(Terrain.SEA, 0.6); + Cache.tradeCarry.put(Terrain.WATER, 0.75); + titles = mockStatic(TitleManager.class); + Faction faction = mock(Faction.class); + when(faction.getId()).thenReturn("realm"); + guild = mock(Guild.class); + when(guild.getId()).thenReturn("guild"); + when(guild.getFaction()).thenReturn(faction); + when(guild.getTradeBreakdown()).thenReturn(new TradeBreakdown()); + when(guild.getModifier(GuildModifier.TRADE_CARRY)).thenReturn(1.0); + } + + @AfterEach + void restore() { + titles.close(); + Cache.tradeCarry.clear(); + Cache.tradeCarry.putAll(previousCarry); + Cache.provincesEnabled = previousEnabled; + Cache.maxTradeUpkeep = previousMaxUpkeep; + } + + @Test + void tradeProductionAndUpkeepUseRawTerrain() { + Province province = new Province(1, "bog", 0); + ProvinceManager manager = manager(Map.of(1, province)); + province.calculateTrade(manager, guild, 10, 1); + + assertEquals(4.0, province.getRawGuildTrade(guild), 1e-9); + double factor = 4.0 / (4.0 + 2.5 / Math.sqrt(0.4)); + assertEquals(factor, province.getTradeFactor(guild), 1e-9); + province.calculateProduction(manager, guild, new ProvinceDataEntry(guild, 0, 10), 1); + assertEquals(10 * Math.sqrt(0.4) * factor, province.getGuildProduction(guild), 1e-9); + when(guild.getModifier(GuildModifier.TRADE_UPKEEP)).thenReturn(1.0); + province.setProsperity(100); + double expectedNet = Math.round(100 * (1 - factor) * 100) / 100.0; + assertEquals(expectedNet, manager.getIncome(guild, false), 1e-9); + assertEquals(0.4, province.getTradeCarry()); + assertEquals(0.4, Cache.getTradeCarry(Terrain.BOG)); + } + + @Test + void hubProductionSpreadUsesRawTerrain() { + Province source = new Province(1, "plains", 0); + Province destination = new Province(2, "bog", 0); + source.addNeighbour(2); + destination.setData(guild.getId(), new ProvinceDataEntry(guild, 10, 0)); + ProvinceManager manager = manager(Map.of(1, source, 2, destination)); + source.seedProduction(manager, guild, 100); + + double factor = 10 / (10 + 2.5 / Math.sqrt(0.4)); + assertEquals(100 * Math.sqrt(0.4) * factor, destination.getGuildProduction(guild), 1e-9); + } + + @Test + void seaAndWaterUseTheirConfiguredCarry() { + for (Terrain terrain : List.of(Terrain.SEA, Terrain.WATER)) { + Province province = new Province(1, terrain.name(), 0); + assertEquals(Cache.getTradeCarry(terrain), province.getTradeCarry()); + } + } + + private static ProvinceManager manager(Map provinces) { + ProvinceManager manager = new ProvinceManager(); + manager.start(provinces); + return manager; + } +}