diff --git a/src/main/java/net/tfminecraft/dowsing/objects/Node.java b/src/main/java/net/tfminecraft/dowsing/objects/Node.java index 94ba5c8..6eef8e3 100644 --- a/src/main/java/net/tfminecraft/dowsing/objects/Node.java +++ b/src/main/java/net/tfminecraft/dowsing/objects/Node.java @@ -48,6 +48,8 @@ public class Node { Integer modifiedTime; Integer yield; Double yieldPercent; + // Cap on items per cycle from max_yield effects; null means uncapped. + Integer maxYield; Double wealthModifier; Double prestigeGain; NodeType currentType; @@ -178,7 +180,11 @@ public void setCurrentType(NodeType currentType) { public Integer getYield() { double base = yield == null ? 0 : yield; double percent = yieldPercent == null ? 0.0 : yieldPercent; - return Math.max(0, (int) Math.round(base * (1.0 + percent / 100.0))); + int total = Math.max(0, (int) Math.round(base * (1.0 + percent / 100.0))); + return maxYield == null ? total : Math.min(total, Math.max(0, maxYield)); + } + public Integer getMaxYield() { + return maxYield; } public Integer getBaseYield() { return yield == null ? 0 : yield; @@ -588,6 +594,7 @@ public void update() { if(isClaimable()) return; this.yield = 0; this.yieldPercent = 0.0; + this.maxYield = null; this.timeModifier = 0.0; this.addedDrops = new ArrayList(); this.extraction = 0; @@ -688,6 +695,9 @@ void runEffect(String e) { yield = yield+Integer.parseInt(e.split("\\(")[1].replace(")", "")); } else if(type.equalsIgnoreCase("yield_percent")) { yieldPercent = yieldPercent+Double.parseDouble(e.split("\\(")[1].replace(")", "")); + } else if(type.equalsIgnoreCase("max_yield")) { + int cap = Integer.parseInt(e.split("\\(")[1].replace(")", "")); + maxYield = maxYield == null ? cap : Math.min(maxYield, cap); } else if(type.equalsIgnoreCase("add_drop")) { String[] drop = DropPaths.parseAddDropEffect(e); if(drop != null) { diff --git a/src/main/java/net/tfminecraft/dowsing/utils/ItemCreator.java b/src/main/java/net/tfminecraft/dowsing/utils/ItemCreator.java index 7c83d6a..540f0bc 100644 --- a/src/main/java/net/tfminecraft/dowsing/utils/ItemCreator.java +++ b/src/main/java/net/tfminecraft/dowsing/utils/ItemCreator.java @@ -387,6 +387,8 @@ public String getFormattedEffect(String s){ } else { s = s+"§c"+formatYieldPercent(amount)+"%"; } + } else if(type.equalsIgnoreCase("max_yield")) { + s = "§eMax Yield: §f"+Integer.parseInt(effect); } else if(type.equalsIgnoreCase("prestige")) { Double amount = Double.parseDouble(effect); s = "§9Prestige: "; diff --git a/src/main/resources/production_methods.yml b/src/main/resources/production_methods.yml index 9cdb9ae..aab326e 100644 --- a/src/main/resources/production_methods.yml +++ b/src/main/resources/production_methods.yml @@ -105,6 +105,7 @@ artifact_focus: name: "§dArtifact Mine" material: ia.tfmc_magic:cerrith_gem_165 effects: + - max_yield(1) - prestige(8) - time_modifier(100) - upkeep(20) diff --git a/src/test/java/net/tfminecraft/dowsing/objects/ConfiguredModelsTest.java b/src/test/java/net/tfminecraft/dowsing/objects/ConfiguredModelsTest.java index 98b1d38..fba41f8 100644 --- a/src/test/java/net/tfminecraft/dowsing/objects/ConfiguredModelsTest.java +++ b/src/test/java/net/tfminecraft/dowsing/objects/ConfiguredModelsTest.java @@ -4,7 +4,9 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.io.InputStreamReader; import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; @@ -76,6 +78,15 @@ void tearDown() throws Exception { for (var entry : globals.entrySet()) entry.getKey().set(null, entry.getValue()); } + @Test + void shippedArtifactFocusCapsItsYieldAtOne() throws Exception { + try (var reader = new InputStreamReader( + getClass().getResourceAsStream("/production_methods.yml"), StandardCharsets.UTF_8)) { + YamlConfiguration config = YamlConfiguration.loadConfiguration(reader); + assertTrue(config.getStringList("artifact_focus.effects").contains("max_yield(1)")); + } + } + @Test void productionMethodLoadsOptionalFieldsAndCopiesItsConfiguration() throws Exception { ProductionMethod method = method("advanced", 7); diff --git a/src/test/java/net/tfminecraft/dowsing/objects/NodeBehaviorTest.java b/src/test/java/net/tfminecraft/dowsing/objects/NodeBehaviorTest.java index 9818a51..a2e2e66 100644 --- a/src/test/java/net/tfminecraft/dowsing/objects/NodeBehaviorTest.java +++ b/src/test/java/net/tfminecraft/dowsing/objects/NodeBehaviorTest.java @@ -94,6 +94,33 @@ void yieldSupportsPercentBonusesMissingValuesAndNegativeTotals() { assertEquals(0, node.getYield()); assertEquals(0, node.getBaseYield()); assertEquals(0.0, node.getYieldPercent()); } + @Test + void maxYieldCapsTheFinalYieldAndTheLowestCapWins() { + Node node = f.node(); + assertNull(node.getMaxYield()); + node.setYield(1); node.runEffect("yield_percent(120)"); assertEquals(2, node.getYield()); + node.runEffect("max_yield(3)"); assertEquals(3, node.getMaxYield()); assertEquals(2, node.getYield()); + node.runEffect("max_yield(1)"); node.runEffect("max_yield(4)"); + assertEquals(1, node.getMaxYield()); assertEquals(1, node.getYield()); + node.runEffect("max_yield(-2)"); assertEquals(0, node.getYield()); + } + + @Test + void artifactFocusKeepsOneItemWhateverTheToolAndOtherFocusesKeepTheirBonus() { + Node node = f.owned(); + ProductionMethod tool = mock(ProductionMethod.class); NodeSlot toolSlot = mock(NodeSlot.class); + ProductionMethod focus = mock(ProductionMethod.class); NodeSlot focusSlot = mock(NodeSlot.class); + when(toolSlot.getActivePm()).thenReturn(tool); when(focusSlot.getActivePm()).thenReturn(focus); + when(tool.getEffects()).thenReturn(List.of("yield_percent(120)", "time_modifier(-10)")); + when(f.type.getSlots()).thenReturn(List.of(toolSlot, focusSlot)); + when(f.level.getEffects()).thenReturn(List.of("yield(1)")); + when(focus.getEffects()).thenReturn(List.of("max_yield(1)", "add_drop(v.STONE,1)")); + node.update(); assertEquals(1, node.getYield()); + // Switching to an uncapped focus clears the cap on the next update. + when(focus.getEffects()).thenReturn(List.of("yield(5)", "add_drop(v.STONE,1)")); + node.update(); assertNull(node.getMaxYield()); assertEquals(13, node.getYield()); + } + @Test void guildResolutionRefreshesTheLiveOwnerAndDropsDeletedGuilds() { Node node = f.node(); diff --git a/src/test/java/net/tfminecraft/dowsing/utils/ItemCreatorTest.java b/src/test/java/net/tfminecraft/dowsing/utils/ItemCreatorTest.java index de3c99d..8a8263c 100644 --- a/src/test/java/net/tfminecraft/dowsing/utils/ItemCreatorTest.java +++ b/src/test/java/net/tfminecraft/dowsing/utils/ItemCreatorTest.java @@ -206,6 +206,7 @@ void formattingCoversEveryEffectAndBothPositiveAndNegativeModifiers() { Map.entry("time_modifier(-5)", "§eTime Modifier: §a-5.0%"), Map.entry("yield(2)", "§eBase Yield: §a+2"), Map.entry("yield(-2)", "§eBase Yield: §c-2"), Map.entry("yield_percent(2.5)", "§eYield: §a+2.5%"), Map.entry("yield_percent(-2)", "§eYield: §c-2%"), + Map.entry("max_yield(1)", "§eMax Yield: §f1"), Map.entry("prestige(2)", "§9Prestige: §f+2"), Map.entry("prestige(-2)", "§9Prestige: §c-2"), Map.entry("extraction(2)", "§eExtraction: §f+2"), Map.entry("extraction(-2)", "§eExtraction: §c-2"), Map.entry("upkeep(1.234)", "§eUpkeep: §f1.23d"), Map.entry("other(2)", "other(2)"));