Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/main/java/net/tfminecraft/simplefactions/Cache.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ public static boolean requireProvinces(org.bukkit.command.CommandSender sender)

public static double supplyHubMaxTax = 50.0;

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 Map<Scope, LawEffect> baseEffects = new HashMap<>();

public static Map<Terrain, Double> tradeCarry = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum FactionModifiers {
STABILITY_INFLUENCE(true, false),
TRADE_POWER(true, true),
PRODUCTION(true, true),
INFRASTRUCTURE_ACCESS(true, true),
DIPLOMATIC_CAPACITY_MULTIPLIER(true, false),
ADMIN_POWER_MULTIPLIER(true, false),
ADMIN_POWER_GAIN_MULTIPLIER(true, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum GuildModifier {
TRADE_POWER("#92d665Trade Power", true),
TRADE_UPKEEP("#d6645aTrade Upkeep", false),
PRODUCTION("#f2c94cProduction", true),
INFRASTRUCTURE("#86d1b0Infrastructure", true),
TRADE_CARRY("#86d1b0Trade Carry", true),
HUB_LIMIT("#c99b70Supply Hub Limit", true),
HUB_UPKEEP("#d6645aSupply Hub Upkeep", false),
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/net/tfminecraft/simplefactions/laws/LawEffect.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import net.tfminecraft.simplefactions.objects.Bracket;
import net.tfminecraft.simplefactions.objects.FactionModifier;
import net.tfminecraft.simplefactions.enums.Brackets;
import net.tfminecraft.simplefactions.enums.FactionModifiers;
import net.tfminecraft.simplefactions.enums.Region;
import net.tfminecraft.simplefactions.enums.Rules;
import net.tfminecraft.simplefactions.enums.Scope;
Expand Down Expand Up @@ -171,6 +172,19 @@ public Map<Region, List<FactionModifier>> getRegionModifiers() {
return regionModifiers;
}

/** Null means absent; an explicit zero still overrides a default. */
public Double getModifierAmount(FactionModifiers type, Region region) {
Double amount = null;
List<FactionModifier> modifiers = new ArrayList<>(globalModifiers);
modifiers.addAll(regionModifiers.getOrDefault(region, List.of()));
for (FactionModifier modifier : modifiers) {
if (modifier.getType() == type) {
amount = (amount == null ? 0 : amount) + modifier.getAmount();
}
}
return amount;
}

public Map<Rules, Boolean> getRules() {
return rules;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ 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);
Cache.infrastructureFull = config.getDouble("infrastructure.full", 20);
Cache.infrastructureTarget = config.getDouble("infrastructure.target", 0.75);
Cache.infrastructureWildernessSpread = config.getDouble("infrastructure.wilderness-spread", 0.25);
Cache.infrastructureSpreadFloor = config.getDouble("infrastructure.spread-floor", 0.5);
Cache.infrastructureStation = config.getDouble("infrastructure.station", 10);
Cache.infrastructurePort = config.getDouble("infrastructure.port", 10);
Cache.infrastructureAirport = config.getDouble("infrastructure.airport", 5);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Cache.settlementLargePopulationThreshold = config.getInt("settlement-large-population-threshold", 8);
Cache.portSeaProximityBlocks = config.getInt("port-sea-proximity-blocks", 20);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.bukkit.Bukkit;
Expand All @@ -18,6 +19,9 @@
import net.tfminecraft.simplefactions.guild.hub.HubTransport.Link;
import net.tfminecraft.simplefactions.map.provinces.Province;
import net.tfminecraft.simplefactions.map.provinces.ProvinceDataEntry;
import net.tfminecraft.simplefactions.map.infra.InfrastructureAccess;
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;
Expand Down Expand Up @@ -107,6 +111,7 @@ public void recalculate() {
return;
}
HubNetwork.refreshIfLive(this);
recalculateInfrastructure();
dropMissingGuilds();
for(Guild g : FactionManager.getAllGuilds()) {
if (!g.hasCapital()) continue;
Expand All @@ -124,6 +129,7 @@ public void recalculateForSingleGuild(Guild g, boolean save) {
if (!Cache.provincesEnabled) {
return;
}
recalculateInfrastructure();
if (!g.hasCapital()) return;
HubNetwork.refreshIfLive(this);
dropMissingGuilds();
Expand All @@ -137,6 +143,24 @@ public void recalculateForSingleGuild(Guild g, boolean save) {
recalculateProsperity();
}

private void recalculateInfrastructure() {
Map<Integer, InfrastructureSpread.Node> graph = new HashMap<>();
for (Province province : provinces.values()) {
Faction realm = InfrastructureAccess.topRealm(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<Integer, Double> sources = InfrastructureSources.collect(
provinces, FactionManager.getAllGuilds(), FactionManager.getCopy());
Map<Integer, InfrastructureSpread.Arrival> 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();
Expand Down Expand Up @@ -326,6 +350,7 @@ public void copyAllDataFrom(ProvinceManager source) {
}

dst.setProsperity(src.getProsperity());
dst.setInfrastructure(src.getInfrastructure());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package net.tfminecraft.simplefactions.map.infra;

import java.util.HashSet;
import java.util.Set;

import net.tfminecraft.simplefactions.enums.FactionModifiers;
import net.tfminecraft.simplefactions.enums.Region;
import net.tfminecraft.simplefactions.enums.Scope;
import net.tfminecraft.simplefactions.guild.income.IncomePreviewContext;
import net.tfminecraft.simplefactions.laws.Law;
import net.tfminecraft.simplefactions.laws.LawEffect;
import net.tfminecraft.simplefactions.laws.LawGroup;
import net.tfminecraft.simplefactions.objects.Faction;

public final class InfrastructureAccess {
private InfrastructureAccess() {}

public static double forGuild(Faction guildFaction, Faction owner) {
if (owner == null) return 1;
if (guildFaction != null && sameFaction(topRealm(guildFaction), topRealm(owner))) return 1;
double grant = amount(economyLaw(owner), Scope.FOREIGN_GUILDS, Region.OUR_TERRITORY);
double reach = amount(economyLaw(guildFaction), Scope.DOMESTIC_GUILDS, Region.FOREIGN_TERRITORY);
return Math.max(0, Math.min(1, grant + reach));
}

public static Faction topRealm(Faction faction) {
Set<Faction> seen = new HashSet<>();
Faction current = faction;
while (current != null) {
// Ignore a cyclic overlord chain rather than granting access through it.
if (!seen.add(current)) return faction;
Faction overlord = current.getOverlord();
if (overlord == null) return current;
current = overlord;
}
return null;
}

private static boolean sameFaction(Faction first, Faction second) {
return first == second || (first != null && second != null && first.getId() != null
&& first.getId().equalsIgnoreCase(second.getId()));
}

private static Law economyLaw(Faction faction) {
if (faction == null || faction.getLawHandler() == null) return null;
LawGroup group = faction.getLawHandler().getGroup("economy");
if (group == null) return null;
IncomePreviewContext context = IncomePreviewContext.current();
Law overlay = context != null && context.previewsLaw(faction)
? IncomePreviewContext.overlayLaw(group) : null;
return overlay != null ? overlay : group.getCurrent();
}

public static double amount(Law law, Scope scope, Region region) {
if (law == null) return 0;
LawEffect effect = law.getScopedEffects().get(scope);
Double explicit = effect == null ? null : effect.getModifierAmount(FactionModifiers.INFRASTRUCTURE_ACCESS, region);
if (explicit != null) return explicit;
boolean grant = scope == Scope.FOREIGN_GUILDS && region == Region.OUR_TERRITORY;
boolean reach = scope == Scope.DOMESTIC_GUILDS && region == Region.FOREIGN_TERRITORY;
if (!grant && !reach) return 0;
return switch (law.getId()) {
case "free_trade" -> grant ? 0.80 : 0.20;
case "mercantilism" -> grant ? 0.50 : 0.20;
case "decentralized" -> grant ? 0.50 : 0.00;
case "protectionism" -> grant ? 0.20 : -0.10;
case "isolationism" -> grant ? 0.00 : -0.30;
default -> 0;
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package net.tfminecraft.simplefactions.map.infra;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

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<Integer, Double> collect(
Map<Integer, Province> provinces, Collection<Guild> guilds, Collection<Faction> factions) {
Map<Integer, Double> 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);
}
}
return sources;
}

private static void add(Map<Integer, Province> provinces, Map<Integer, Double> sources, int id, double amount) {
Province province = provinces.get(id);
if (province == null || province.isSea() || amount <= 0) return;
sources.merge(id, amount, Double::sum);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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<Integer> neighbours) {}
public record Arrival(double amount, String origin) {}
private record Step(int province, Arrival arrival) {}

public static Map<Integer, Arrival> spread(
Map<Integer, Node> provinces, Map<Integer, Double> sources, double wilderness, double floor) {
Map<Integer, Arrival> best = new HashMap<>();
PriorityQueue<Step> pending = new PriorityQueue<>(
Comparator.comparingDouble((Step step) -> step.arrival().amount()).reversed()
.thenComparingInt(Step::province));
for (Map.Entry<Integer, Double> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
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.map.infra.InfrastructureAccess;
import net.tfminecraft.simplefactions.utils.ModifierMerger;
import net.tfminecraft.simplefactions.enums.FactionModifiers;
import net.tfminecraft.simplefactions.enums.GuildModifier;
Expand All @@ -32,6 +34,7 @@ public class Province {
private int centerX;
private int centerZ;
private double prosperity = 0;
private double infrastructure = 0;
private final Set<Integer> neighbours = new HashSet<>();

public Province() {
Expand Down Expand Up @@ -88,7 +91,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(), effectiveDistance);
double factor = Math.pow(getTradeCarry(guild), effectiveDistance);
if (prev == -1) {
// Capital province
amount = net.tfminecraft.simplefactions.government.stability.StabilityDebuffs.realmSeed(
Expand Down Expand Up @@ -130,7 +133,7 @@ public void calculateProduction(
int distance
) {
double amount;
double terrainFactor = Math.pow(getTradeCarry(), 0.5);
double terrainFactor = Math.pow(getTradeCarry(guild), 0.5);
double factor = terrainFactor*getTradeFactor(guild);

if (prev == null) {
Expand Down Expand Up @@ -167,7 +170,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(), 0.5);
double K = 2.5 / Math.pow(getTradeCarry(guild), 0.5);
return (trade / (trade + K));
}

Expand Down Expand Up @@ -320,7 +323,7 @@ private void spreadHubProduction(
ProvinceDataEntry prev,
int distance
) {
double factor = Math.pow(getTradeCarry(), 0.5) * getTradeFactor(guild);
double factor = Math.pow(getTradeCarry(guild), 0.5) * getTradeFactor(guild);
double amount = prev.getProduction() * factor;
if (amount < 0.1) return;

Expand All @@ -346,6 +349,16 @@ private void spreadHubProduction(
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, InfrastructureAccess.forGuild(guild.getFaction(), getOwner()));
}

public double getInfrastructure() { return infrastructure; }
public void setInfrastructure(double amount) { infrastructure = amount; }
public Set<Integer> getNeighbours() {
return Collections.unmodifiableSet(neighbours);
}
Expand Down
Loading
Loading