Skip to content

Commit e35a5da

Browse files
Drefvelinryanbarlow97claude
authored
Slow prosperity growth past 80 and add workshop trade upkeep (#45)
Province prosperity is unchanged up to prosperity-soft-cap (80). Past it the excess grows with a square root, so each point is worth less (half at 140, a third at 240) without a hard ceiling. This stops guilds from stacking production into the same provinces to run them far past 80. Workshops now add 1% trade upkeep per level, like guild halls and storehouses already do, so reinvesting in production levels off instead of compounding forever. Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 57cfc02 commit e35a5da

7 files changed

Lines changed: 107 additions & 0 deletions

File tree

‎src/main/java/net/tfminecraft/simplefactions/Cache.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public class Cache {
2828
public static double prestigePerTradePower = 0.1;
2929
public static double prestigeFromTradeSoftCap = 2000;
3030
public static double prestigeFromTradeFalloff = 0.1;
31+
public static double prosperitySoftCap = 80;
32+
public static double prosperitySoftCapScale = 20;
3133
public static double maxPlaytimePrestigeExponent = 5;
3234
public static String baseYear;
3335
public static int baseIrlYear = java.time.Year.now().getValue();

‎src/main/java/net/tfminecraft/simplefactions/loaders/ConfigLoader.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public void loadConfig(File configFile) {
3434
Cache.prestigePerTradePower = config.getDouble("prestige-per-trade-power", 0.1);
3535
Cache.prestigeFromTradeSoftCap = config.getDouble("prestige-from-trade-soft-cap", 2000);
3636
Cache.prestigeFromTradeFalloff = config.getDouble("prestige-from-trade-falloff", 0.1);
37+
Cache.prosperitySoftCap = config.getDouble("prosperity-soft-cap", 80);
38+
Cache.prosperitySoftCapScale = config.getDouble("prosperity-soft-cap-scale", 20);
3739
Cache.maxPlaytimePrestigeExponent = config.getDouble("max-prestige-playtime-exponent", 5);
3840
Cache.bankBlock = config.getString("bank-block", "v.lodestone");
3941
Cache.maxExtraNodeCapacity = config.getInt("max-extra-node-capacity", 0);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package net.tfminecraft.simplefactions.map.provinces;
2+
3+
import net.tfminecraft.simplefactions.Cache;
4+
5+
/**
6+
* Slows a province's prosperity past {@link Cache#prosperitySoftCap}.
7+
*
8+
* <p>Up to the cap nothing changes. Past it, the excess grows with a square
9+
* root, so the next point is worth full value at the seam and less after:
10+
* half at cap + 3 x scale, a third at cap + 8 x scale. There is no ceiling;
11+
* it just gets harder and harder to add prosperity.
12+
*/
13+
public final class ProsperitySoftCap {
14+
15+
private ProsperitySoftCap() {
16+
}
17+
18+
public static double apply(double prosperity) {
19+
return diminish(prosperity, Cache.prosperitySoftCap, Cache.prosperitySoftCapScale);
20+
}
21+
22+
static double diminish(double prosperity, double softCap, double scale) {
23+
if (softCap <= 0 || scale <= 0 || prosperity <= softCap) {
24+
return prosperity;
25+
}
26+
double extra = prosperity - softCap;
27+
return softCap + 2.0 * scale * (Math.sqrt(1.0 + extra / scale) - 1.0);
28+
}
29+
}

‎src/main/java/net/tfminecraft/simplefactions/map/provinces/Province.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ public void calculateProsperity() {
209209
double exponent = 1 + Math.min(0.1, participants * 0.005);
210210

211211
total = Math.pow(total, exponent);
212+
total = ProsperitySoftCap.apply(total);
212213
total = Math.round(total * 100.0) / 100.0;
213214
this.prosperity = total;
214215
}

‎src/main/resources/Guilds/branches.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ workshops:
4848
modifiers:
4949
- production 3 1
5050
- prestige 0 5
51+
- trade_upkeep 0 0.01
5152
storehouses:
5253
name: "#bf8a45Storehouses"
5354
icon: "barrel.0"

‎src/main/resources/config.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ battle:
100100
siege: siege_default
101101
raid: campaign_raid_template
102102

103+
# A province's prosperity is unchanged up to prosperity-soft-cap. Past it the
104+
# excess grows with a square root, so each point is worth less: half at
105+
# cap + 3 x scale, a third at cap + 8 x scale. No ceiling. 0 disables it.
106+
prosperity-soft-cap: 80
107+
prosperity-soft-cap-scale: 20
108+
103109
branch-upgrade-cost: 100.0
104110
branch-upgrade-exponent: 1.07
105111

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package net.tfminecraft.simplefactions.map.provinces;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import org.junit.jupiter.api.AfterEach;
7+
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
9+
10+
import net.tfminecraft.simplefactions.Cache;
11+
12+
class ProsperitySoftCapTest {
13+
14+
private double previousCap;
15+
private double previousScale;
16+
17+
@BeforeEach
18+
void setUp() {
19+
previousCap = Cache.prosperitySoftCap;
20+
previousScale = Cache.prosperitySoftCapScale;
21+
Cache.prosperitySoftCap = 80;
22+
Cache.prosperitySoftCapScale = 20;
23+
}
24+
25+
@AfterEach
26+
void tearDown() {
27+
Cache.prosperitySoftCap = previousCap;
28+
Cache.prosperitySoftCapScale = previousScale;
29+
}
30+
31+
@Test
32+
void apply_leavesProsperityUpToTheCapAlone() {
33+
assertEquals(0.0, ProsperitySoftCap.apply(0), 1e-9);
34+
assertEquals(42.5, ProsperitySoftCap.apply(42.5), 1e-9);
35+
assertEquals(80.0, ProsperitySoftCap.apply(80), 1e-9);
36+
}
37+
38+
@Test
39+
void apply_slowsPastTheCapWithoutACeiling() {
40+
assertEquals(120.0, ProsperitySoftCap.apply(140), 1e-9);
41+
assertEquals(160.0, ProsperitySoftCap.apply(240), 1e-9);
42+
double far = ProsperitySoftCap.apply(10_000);
43+
assertTrue(far > 600, "expected continued growth far past the cap, was " + far);
44+
}
45+
46+
@Test
47+
void diminish_isSmoothAtTheCap() {
48+
double atCap = ProsperitySoftCap.diminish(80.0, 80.0, 20.0);
49+
double justAbove = ProsperitySoftCap.diminish(80.001, 80.0, 20.0);
50+
assertEquals(0.001, justAbove - atCap, 1e-6);
51+
}
52+
53+
@Test
54+
void diminish_marginalValueHalvesAtCapPlusThreeScales() {
55+
double step = 1e-4;
56+
double at = ProsperitySoftCap.diminish(140.0, 80.0, 20.0);
57+
double next = ProsperitySoftCap.diminish(140.0 + step, 80.0, 20.0);
58+
assertEquals(0.5, (next - at) / step, 1e-3);
59+
}
60+
61+
@Test
62+
void diminish_zeroCapOrScaleDisablesIt() {
63+
assertEquals(500.0, ProsperitySoftCap.diminish(500.0, 0.0, 20.0), 1e-9);
64+
assertEquals(500.0, ProsperitySoftCap.diminish(500.0, 80.0, 0.0), 1e-9);
65+
}
66+
}

0 commit comments

Comments
 (0)