diff --git a/src/main/java/net/tfminecraft/dowsing/managers/NodeManager.java b/src/main/java/net/tfminecraft/dowsing/managers/NodeManager.java index 73782a7..1caebec 100644 --- a/src/main/java/net/tfminecraft/dowsing/managers/NodeManager.java +++ b/src/main/java/net/tfminecraft/dowsing/managers/NodeManager.java @@ -106,10 +106,14 @@ public static boolean canPurchaseCapacity(Guild g) { public static double getTotalUpkeep(Guild g) { if (g == null || Node.held(g)) return 0; double total = 0; + // Every node of g pays the same cost increase, so count g's nodes at most once, and only + // when a node first needs the count. + int[] amount = {-1}; + java.util.function.IntSupplier count = () -> amount[0] >= 0 ? amount[0] : (amount[0] = getNodeAmount(g)); for (Node n : nodes) { if (!n.getIsActive() || !n.hasGuild()) continue; if (!n.getGuild().getId().equalsIgnoreCase(g.getId())) continue; - total += n.getDailyUpkeep(); + total += n.getDailyUpkeep(count); } return total; } diff --git a/src/main/java/net/tfminecraft/dowsing/objects/Node.java b/src/main/java/net/tfminecraft/dowsing/objects/Node.java index 3459489..f9c2a96 100644 --- a/src/main/java/net/tfminecraft/dowsing/objects/Node.java +++ b/src/main/java/net/tfminecraft/dowsing/objects/Node.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.function.IntSupplier; import org.apache.commons.lang.WordUtils; import org.bukkit.Bukkit; @@ -81,16 +82,34 @@ public void setUpkeep(Double upkeep) { * since members joining or capacity being bought does not refresh every node. */ public double getDailyUpkeep() { - double base = upkeep == null ? 0.0 : upkeep; - return BigDecimal.valueOf(base) - .multiply(BigDecimal.valueOf(currentCostIncrease())) + BigDecimal base = baseUpkeep(); + return dailyUpkeep(base, currentCostIncrease()); + } + /** + * {@link #getDailyUpkeep()} with the guild's node count supplied by the caller, so a guild + * total counts its nodes once instead of once per node. Same steps in the same order: the + * base upkeep, then the guild, then the count, which is only asked for when the guild resolves. + */ + public double getDailyUpkeep(IntSupplier nodeAmount) { + BigDecimal base = baseUpkeep(); + if(resolveGuild() == null) return dailyUpkeep(base, storedCostIncrease()); + return dailyUpkeep(base, costIncreaseFor(nodeAmount.getAsInt(), getCapacity())); + } + private BigDecimal baseUpkeep() { + return BigDecimal.valueOf(upkeep == null ? 0.0 : upkeep); + } + private static double dailyUpkeep(BigDecimal base, double costIncrease) { + return base.multiply(BigDecimal.valueOf(costIncrease)) .setScale(2, RoundingMode.HALF_UP) .doubleValue(); } double currentCostIncrease() { - if(resolveGuild() == null) return costIncrease == null ? 1.0 : costIncrease; + if(resolveGuild() == null) return storedCostIncrease(); return costIncreaseFor(NodeManager.getNodeAmount(this.guild), getCapacity()); } + private double storedCostIncrease() { + return costIncrease == null ? 1.0 : costIncrease; + } static double costIncreaseFor(int nodeAmount, int capacity) { int multiplier = Math.max(1, 1+nodeAmount-capacity); return 1.0+((multiplier-1.0)*0.5); diff --git a/src/test/java/net/tfminecraft/dowsing/managers/NodeManagerCyclesTest.java b/src/test/java/net/tfminecraft/dowsing/managers/NodeManagerCyclesTest.java index 18d589b..3a64c60 100644 --- a/src/test/java/net/tfminecraft/dowsing/managers/NodeManagerCyclesTest.java +++ b/src/test/java/net/tfminecraft/dowsing/managers/NodeManagerCyclesTest.java @@ -75,7 +75,7 @@ void countsCapacityAndUpkeepRespectOwnershipSpecialNodesAndPurchasedLimits() { assertEquals(0, NodeManager.getExtraCapacity(null)); assertFalse(NodeManager.canPurchaseCapacity(null)); assertEquals(0, NodeManager.getTotalUpkeep(null)); assertEquals(0, NodeManager.getNodeAmount(null)); assertEquals(1, NodeManager.getNodeCapacity(null)); assertEquals(1, NodeManager.getNodeCapacity(f.guild)); - NodeManager.nodes.add(f.node); when(f.node.getIsActive()).thenReturn(true); when(f.node.getDailyUpkeep()).thenReturn(2.25); + NodeManager.nodes.add(f.node); when(f.node.getIsActive()).thenReturn(true); when(f.node.getDailyUpkeep(any(java.util.function.IntSupplier.class))).thenReturn(2.25); Node inactive = f.nodeAt(f.location), unowned = f.nodeAt(f.location), other = f.nodeAt(f.location); when(unowned.hasGuild()).thenReturn(false); when(unowned.getIsActive()).thenReturn(true); Guild otherGuild = mock(Guild.class); when(otherGuild.getId()).thenReturn("other"); when(other.getGuild()).thenReturn(otherGuild); when(other.getIsActive()).thenReturn(true); diff --git a/src/test/java/net/tfminecraft/dowsing/objects/NodeBehaviorTest.java b/src/test/java/net/tfminecraft/dowsing/objects/NodeBehaviorTest.java index a2e2e66..4a76b36 100644 --- a/src/test/java/net/tfminecraft/dowsing/objects/NodeBehaviorTest.java +++ b/src/test/java/net/tfminecraft/dowsing/objects/NodeBehaviorTest.java @@ -237,8 +237,11 @@ void upkeepRoundsHalfUpAndRecalculatesFromLiveGuildCapacity() { node.setUpkeep(0.29); node.setCostIncrease(1.5); assertEquals(0.44, node.getDailyUpkeep()); node.setGuild(f.guild); NodeManager.nodes.add(node); NodeManager.nodes.add(f.owned()); assertEquals(1.5, node.currentCostIncrease()); assertEquals(0.44, node.getDailyUpkeep()); + assertEquals(node.getDailyUpkeep(), node.getDailyUpkeep(() -> NodeManager.getNodeAmount(f.guild))); + assertEquals(0.58, node.getDailyUpkeep(() -> 3), "a counted amount gives the same cost formula"); Cache.extraCapacity = true; Cache.membersPerCapacity = 2; Cache.maxMemberCapacity = 5; assertEquals(2, node.getCapacity()); assertEquals(0.29, node.getDailyUpkeep()); + assertEquals(node.getDailyUpkeep(), node.getDailyUpkeep(() -> NodeManager.getNodeAmount(f.guild))); Cache.extraCapacityCost = 12.345; NodeManager.extraCapacityByGuild.put("guild", 2); assertEquals(37.04, node.getNodeCapacityCost());