Skip to content

Commit b46bde9

Browse files
Show what infrastructure earns in the menus.
The realm guild's infrastructure reaches every guild, and starting a build should say what that is worth first. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 315e52d commit b46bde9

14 files changed

Lines changed: 525 additions & 11 deletions

File tree

‎src/main/java/net/tfminecraft/simplefactions/guild/hub/HubEstimates.java‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,26 @@ public static double infrastructureWorth(String realmId) {
147147
return 0;
148148
}
149149

150+
/** False before the first daily pass, so a menu can avoid showing a missing figure as +0. */
151+
public static boolean hasInfrastructureWorth(Faction realm) {
152+
return realm != null && hasInfrastructureWorth(realm.getId());
153+
}
154+
155+
public static boolean hasInfrastructureWorth(String realmId) {
156+
if (realmId == null) {
157+
return false;
158+
}
159+
if (headlines.containsKey(realmId)) {
160+
return true;
161+
}
162+
for (String key : headlines.keySet()) {
163+
if (realmId.equalsIgnoreCase(key)) {
164+
return true;
165+
}
166+
}
167+
return false;
168+
}
169+
150170
public static Terms applyTerms(Destination destination, int ratePercent, long feeCents) {
151171
if (destination == null) {
152172
return new Terms(0, 0, 0, 0);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package net.tfminecraft.simplefactions.guild.hub;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Locale;
6+
7+
/** Lore for the realm headline and the installation confirmation. */
8+
public final class InfrastructureMenuCopy {
9+
private InfrastructureMenuCopy() {}
10+
11+
public static String headline(double worth) {
12+
return "§7Infrastructure is worth about " + signed(worth) + " a day to your realm";
13+
}
14+
15+
public static String headlineUnknown() {
16+
return "§7Infrastructure worth is worked out once a day.";
17+
}
18+
19+
public static String calculating() {
20+
return "§7Working out what this would earn...";
21+
}
22+
23+
public static List<String> installationPreview(HubEstimates.InstallationPreview preview) {
24+
if (preview == null) {
25+
return List.of(calculating());
26+
}
27+
List<String> lines = new ArrayList<>();
28+
if (preview.infrastructureHere() > 0) {
29+
lines.add("§a" + signed(preview.infrastructureHere()) + " infrastructure here");
30+
} else {
31+
lines.add("§7This adds no infrastructure here");
32+
}
33+
lines.add("§7about " + signed(preview.realmPerDay()) + " a day for your realm");
34+
if (preview.upkeep() > 0) {
35+
lines.add("§7upkeep " + compact(preview.upkeep()));
36+
}
37+
return lines;
38+
}
39+
40+
static String signed(double value) {
41+
String number = compact(Math.abs(value));
42+
if (value < 0) {
43+
return "-" + number;
44+
}
45+
return "+" + number;
46+
}
47+
48+
static String compact(double value) {
49+
double rounded = Math.round(value * 100.0) / 100.0;
50+
if (Math.abs(rounded - Math.rint(rounded)) < 0.001) {
51+
return Long.toString((long) Math.rint(rounded));
52+
}
53+
return String.format(Locale.ROOT, "%.2f", rounded);
54+
}
55+
}

‎src/main/java/net/tfminecraft/simplefactions/guild/income/BranchIncomePreview.java‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
import net.tfminecraft.simplefactions.guild.GuildModifierOverride;
1010
import net.tfminecraft.simplefactions.guild.branch.Branch;
1111
import net.tfminecraft.simplefactions.guild.branch.BranchModifier;
12+
import net.tfminecraft.simplefactions.managers.FactionManager;
1213
import net.tfminecraft.simplefactions.managers.ProvinceManager;
14+
import net.tfminecraft.simplefactions.managers.RelationManager;
1315
import net.tfminecraft.simplefactions.objects.Faction;
1416

1517
/**
@@ -65,6 +67,29 @@ public static double estimate(
6567
return Math.round(net * 100.0) / 100.0;
6668
}
6769

70+
/**
71+
* The realm guild's infrastructure reaches every guild in the realm, so an
72+
* up or down of that branch is their incomes added together.
73+
*/
74+
public static boolean showsRealm(Guild guild, Branch branch) {
75+
return guild != null
76+
&& guild.isBase()
77+
&& branch != null
78+
&& branch.getModifier(GuildModifier.INFRASTRUCTURE) != null;
79+
}
80+
81+
public static double estimateRealm(
82+
Prepared prepared,
83+
Guild realmGuild,
84+
Map<GuildModifier, Double> current,
85+
Map<GuildModifier, Double> hypothetical) {
86+
double before = realmIncome(prepared, realmGuild, current);
87+
double after = realmIncome(prepared, realmGuild, hypothetical);
88+
double upkeepChange = Math.max(0.0, hypothetical.getOrDefault(GuildModifier.INFRASTRUCTURE_UPKEEP, 0.0))
89+
- Math.max(0.0, current.getOrDefault(GuildModifier.INFRASTRUCTURE_UPKEEP, 0.0));
90+
return Math.round((after - before - upkeepChange) * 100.0) / 100.0;
91+
}
92+
6893
public static Map<GuildModifier, Double> modifiers(Guild guild) {
6994
EnumMap<GuildModifier, Double> amounts = new EnumMap<>(GuildModifier.class);
7095
for (GuildModifier modifier : GuildModifier.values()) {
@@ -105,6 +130,39 @@ private static ProvinceManager copy(ProvinceManager source) {
105130
return snapshot;
106131
}
107132

133+
private static double realmIncome(
134+
Prepared prepared, Guild realmGuild, Map<GuildModifier, Double> amounts) {
135+
ProvinceManager snapshot = copy(prepared.snapshot);
136+
GuildModifierOverride.use(realmGuild, amounts);
137+
try {
138+
snapshot.recalculateQuiet();
139+
double sum = 0;
140+
Faction realm = realmGuild.getFaction();
141+
for (Guild guild : FactionManager.getAllGuilds()) {
142+
if (guild == null || !sameRealm(realm, guild.getFaction())) {
143+
continue;
144+
}
145+
sum += snapshot.getIncome(guild, false) * (1.0 - taxFraction(guild));
146+
}
147+
return sum;
148+
} finally {
149+
GuildModifierOverride.clear();
150+
}
151+
}
152+
153+
private static boolean sameRealm(Faction realm, Faction guildFaction) {
154+
if (realm == null || guildFaction == null) {
155+
return false;
156+
}
157+
if (realm == guildFaction) {
158+
return true;
159+
}
160+
if (realm.getId() != null && realm.getId().equalsIgnoreCase(guildFaction.getId())) {
161+
return true;
162+
}
163+
return RelationManager.sameRealm(realm, guildFaction);
164+
}
165+
108166
private static double income(ProvinceManager snapshot, Guild guild, Map<GuildModifier, Double> amounts) {
109167
GuildModifierOverride.use(guild, amounts);
110168
try {

‎src/main/java/net/tfminecraft/simplefactions/managers/CommandManager.java‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import net.tfminecraft.simplefactions.installation.Installation;
4141
import net.tfminecraft.simplefactions.installation.InstallationKind;
4242
import net.tfminecraft.simplefactions.installation.handler.ConstructResult;
43+
import net.tfminecraft.simplefactions.managers.inventory.InstallationBuildConfirm;
4344
import net.tfminecraft.simplefactions.settlement.handler.CapitalResult;
4445
import net.tfminecraft.simplefactions.tiers.Title;
4546
import net.tfminecraft.simplefactions.tiers.admin.TitleAdminCommand;
@@ -623,15 +624,15 @@ private boolean dispatchCommand(CommandSender sender, Command cmd, String label,
623624
return true;
624625
}
625626
String name = String.join(" ", Arrays.copyOfRange(args, 2, args.length));
626-
ConstructResult result = f.getInstallationHandler().construct(
627+
if (InstallationBuildConfirm.open(
628+
p,
629+
f,
627630
kind,
628631
name,
629632
province,
630633
p.getLocation().getBlockX(),
631-
p.getLocation().getBlockZ());
632-
p.sendMessage(result.getMessage());
633-
if(result.isSuccess()) {
634-
p.playSound(p, Sound.BLOCK_ANVIL_USE, 1f, 1f);
634+
p.getLocation().getBlockZ())) {
635+
p.playSound(p, Sound.BLOCK_NOTE_BLOCK_BIT, 1f, 1f);
635636
}
636637
return true;
637638
} else if(cmd.getName().equalsIgnoreCase(cmd1) && args[0].equalsIgnoreCase("deconstruct")) {

‎src/main/java/net/tfminecraft/simplefactions/managers/InventoryManager.java‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import net.tfminecraft.simplefactions.managers.holder.SFInventoryHolder;
3838
import net.tfminecraft.simplefactions.managers.holder.WarInventoryHolder;
3939
import net.tfminecraft.simplefactions.managers.inventory.CampaignInstallationPickView;
40+
import net.tfminecraft.simplefactions.managers.inventory.InstallationBuildConfirm;
4041
import net.tfminecraft.simplefactions.managers.inventory.CampaignRaidLaunchView;
4142
import net.tfminecraft.simplefactions.managers.inventory.CampaignView;
4243
import net.tfminecraft.simplefactions.managers.inventory.CompanyView;
@@ -1001,6 +1002,8 @@ public void clearPendingOnQuit(PlayerQuitEvent e) {
10011002
if (pendingCompanyFounds.remove(e.getPlayer()) != null) {
10021003
confirming.remove(e.getPlayer());
10031004
}
1005+
InstallationBuildConfirm.forget(e.getPlayer());
1006+
confirming.remove(e.getPlayer());
10041007
slotChanges.remove(e.getPlayer());
10051008
}
10061009

@@ -1477,6 +1480,17 @@ public void clickButton(InventoryClickEvent e) {
14771480
}
14781481
return;
14791482
}
1483+
key = new NamespacedKey(SimpleFactions.plugin, "installation_build");
1484+
data = m.getPersistentDataContainer().get(key, PersistentDataType.STRING);
1485+
if (data != null) {
1486+
if (item.getType().equals(Material.RED_CONCRETE)) {
1487+
InstallationBuildConfirm.cancel(p);
1488+
} else {
1489+
InstallationBuildConfirm.accept(p);
1490+
}
1491+
confirming.remove(p);
1492+
return;
1493+
}
14801494
key = new NamespacedKey(SimpleFactions.plugin, "installation");
14811495
data = m.getPersistentDataContainer().get(key, PersistentDataType.STRING);
14821496
if(data != null) {

‎src/main/java/net/tfminecraft/simplefactions/managers/ProvinceManager.java‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,28 @@ public void recalculate() {
140140
for(Guild guild : FactionManager.getAllGuilds()) getIncome(guild);
141141
}
142142

143+
/**
144+
* Same flood as {@link #recalculate()} without writing guild trade breakdowns.
145+
* A realm-wide branch preview runs this off the server thread.
146+
*/
147+
public void recalculateQuiet() {
148+
if (!Cache.provincesEnabled) {
149+
return;
150+
}
151+
HubNetwork.refreshIfLive(this);
152+
recalculateInfrastructure();
153+
dropMissingGuilds();
154+
for (Guild g : FactionManager.getAllGuilds()) {
155+
if (!g.hasCapital()) continue;
156+
recalculateGuild(g);
157+
}
158+
for (Guild g : FactionManager.getAllGuilds()) {
159+
if (!g.hasCapital()) continue;
160+
recalculateProduction(g);
161+
}
162+
recalculateProsperity();
163+
}
164+
143165
public void recalculateForSingleGuild(Guild g, boolean save) {
144166
if (!Cache.provincesEnabled) {
145167
return;

‎src/main/java/net/tfminecraft/simplefactions/managers/inventory/BranchIncomePreviewService.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ public static void schedule(
5959
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
6060
double delta;
6161
try {
62-
delta = BranchIncomePreview.estimate(prepared, guild, current, hypothetical, taxFraction);
62+
delta = BranchIncomePreview.showsRealm(guild, branch)
63+
? BranchIncomePreview.estimateRealm(prepared, guild, current, hypothetical)
64+
: BranchIncomePreview.estimate(prepared, guild, current, hypothetical, taxFraction);
6365
} catch (RuntimeException ex) {
6466
plugin.getLogger().log(
6567
Level.WARNING,

‎src/main/java/net/tfminecraft/simplefactions/managers/inventory/FactionView.java‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
import net.tfminecraft.simplefactions.managers.RelationManager;
2424
import net.tfminecraft.simplefactions.objects.Faction;
2525
import net.tfminecraft.simplefactions.rest.BannerFetcher;
26+
import net.tfminecraft.simplefactions.Cache;
2627
import net.tfminecraft.simplefactions.SimpleFactions;
2728
import net.tfminecraft.simplefactions.guild.Guild;
29+
import net.tfminecraft.simplefactions.guild.hub.HubEstimates;
30+
import net.tfminecraft.simplefactions.guild.hub.InfrastructureMenuCopy;
2831
import net.tfminecraft.simplefactions.utils.FactionRanker;
2932
import net.tfminecraft.simplefactions.enums.MenuItemType;
3033
import net.tfminecraft.simplefactions.enums.RankType;
@@ -129,6 +132,9 @@ public void factionView(Player player, Faction f, Inventory i) {
129132
i.setItem(11, creator.createMenuItem(player, f, MenuItemType.GOVERNMENT));
130133
i.setItem(12, creator.createMenuItem(player, f, MenuItemType.WEALTH));
131134
i.setItem(13, creator.createMenuItem(player, f, MenuItemType.PRESTIGE));
135+
if (Cache.provincesEnabled) {
136+
i.setItem(21, infrastructureHeadline(f));
137+
}
132138
i.setItem(14, creator.createMenuItem(player, f, MenuItemType.MEMBERS));
133139
i.setItem(23, creator.createMenuItem(player, f, MenuItemType.GUILDS));
134140
String hubOffers = net.tfminecraft.simplefactions.guild.hub.HubAgreementService.joinSummary(player.getName());
@@ -151,6 +157,19 @@ public void factionView(Player player, Faction f, Inventory i) {
151157
if(open) player.openInventory(i);
152158
}
153159

160+
@SuppressWarnings("deprecation")
161+
private ItemStack infrastructureHeadline(Faction faction) {
162+
ItemStack item = new ItemStack(Material.RAIL);
163+
ItemMeta meta = item.getItemMeta();
164+
meta.setDisplayName("§6Infrastructure");
165+
String line = HubEstimates.hasInfrastructureWorth(faction)
166+
? InfrastructureMenuCopy.headline(HubEstimates.infrastructureWorth(faction))
167+
: InfrastructureMenuCopy.headlineUnknown();
168+
meta.setLore(List.of(line));
169+
item.setItemMeta(meta);
170+
return item;
171+
}
172+
154173
// Keep the existing legacy text representation, formatting, and exact-string comparisons.
155174
@SuppressWarnings("deprecation")
156175
public void factionGuildsView(Player player, Faction f, Inventory i) {

‎src/main/java/net/tfminecraft/simplefactions/managers/inventory/GuildCreator.java‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import net.tfminecraft.simplefactions.SimpleFactions;
2828
import net.tfminecraft.simplefactions.guild.branch.Branch;
2929
import net.tfminecraft.simplefactions.guild.branch.BranchModifier;
30+
import net.tfminecraft.simplefactions.guild.income.BranchIncomePreview;
3031
import net.tfminecraft.simplefactions.guild.income.Cashflow;
3132
import net.tfminecraft.simplefactions.guild.income.Ledger;
3233
import net.tfminecraft.simplefactions.guild.income.LedgerHistory;
@@ -348,7 +349,7 @@ private List<String> upgradeLore(Guild guild, Branch branch, Double delta) {
348349
lore.add("");
349350
lore.add(StringFormatter.formatHex("#d4c9aeCurrent Net Trade Income: #7fbd73"+net.tfminecraft.simplefactions.utils.Formatter.formatDouble(
350351
guild.getTradeBreakdown().getNetTradeIncome() - guild.getHubTaxBreakdown().getTotalTax())));
351-
lore.add(incomeChangeLine(delta));
352+
lore.add(incomeChangeLine(delta, BranchIncomePreview.showsRealm(guild, branch)));
352353
}
353354
lore.add("");
354355
lore.add(StringFormatter.formatHex("#50e846§lClick to Upgrade"));
@@ -395,7 +396,7 @@ private List<String> downgradeLore(Guild guild, Branch branch, Double delta) {
395396
} else {
396397
lore.add(StringFormatter.formatHex("#d4c9aeCurrent Net Trade Income: #7fbd73"+net.tfminecraft.simplefactions.utils.Formatter.formatDouble(
397398
guild.getTradeBreakdown().getNetTradeIncome() - guild.getHubTaxBreakdown().getTotalTax())));
398-
lore.add(incomeChangeLine(delta));
399+
lore.add(incomeChangeLine(delta, BranchIncomePreview.showsRealm(guild, branch)));
399400
}
400401
lore.add("");
401402
lore.add(StringFormatter.formatHex(
@@ -406,15 +407,16 @@ private List<String> downgradeLore(Guild guild, Branch branch, Double delta) {
406407
return lore;
407408
}
408409

409-
private String incomeChangeLine(Double delta) {
410+
String incomeChangeLine(Double delta, boolean realm) {
411+
String label = realm ? "Estimated Realm Income Change" : "Estimated Income Change";
410412
if (delta == null) {
411-
return StringFormatter.formatHex("#f2e5c2Estimated Income Change#d6cf69: #7a706aCalculating...");
413+
return StringFormatter.formatHex("#f2e5c2" + label + "#d6cf69: #7a706aCalculating...");
412414
}
413415
if (delta.isNaN()) {
414416
return StringFormatter.formatHex("#cf493aIncome estimate unavailable");
415417
}
416418
return StringFormatter.formatHex(
417-
"#f2e5c2Estimated Income Change#d6cf69: "
419+
"#f2e5c2" + label + "#d6cf69: "
418420
+ (delta >= 0 ? "#4fd945+" : "#cf493a")
419421
+ String.format("%.2f", delta)
420422
+ "d/day"

0 commit comments

Comments
 (0)