Gold: guard wages, raid retune, strongbox, caravan wares, looks, monuments

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-24 13:34:59 +01:00
parent 9d61de2012
commit cd44709d80
6 changed files with 816 additions and 31 deletions

View file

@ -435,6 +435,42 @@ class EventTest(unittest.TestCase):
self.assertEqual(world["next_fortune_at"], T0 + 20 * game.HOUR)
self.assertEqual(world["next_event_at"], T0)
def test_only_paid_guards_stand_and_hold_kit(self):
world = world_with(("sam", "commoner"))
city = world["city"]
city["population"] = 100
city["buildings"]["barracks"]["level"] = 2
city["stockpile"] = {"copper_sword": 100}
self.assertEqual(game.guard_posts(world), 50)
self.assertEqual(game.defence(world)["parts"]["weapons"], 0)
p = world["players"]["sam"]
p["gold"] = 1000
self.assertIsNone(game.pay_wages(world, p, 600, "bank", T0))
self.assertEqual(game.guards_on_duty(world), 10)
self.assertEqual(p["favour"], 600)
parts = game.defence(world)["parts"]
self.assertEqual(parts["weapons"], round(0.35 * 10 * 4, 1))
self.assertEqual(parts["militia"], round(0.3 * 2 * 100 * 10 / 50, 1))
city["treasury"] = 10_000
self.assertIsNone(game.pay_wages(world, p, "all", "city", T0))
self.assertEqual(city["wages"], 50 * game.GUARD_WAGE) # never more than every post
self.assertEqual(game.pay_wages(world, p, 1, "bank", T0), "Every post is paid for")
event = {"strength": 1, "sabotage": 0, "group": "the Red Hand"}
game._resolve_raid(world, event, T0, random.Random(1))
self.assertEqual(event["report"]["guards"], 50)
self.assertEqual(city["wages"], 0)
def test_threat_size_is_against_standing_defence(self):
self.assertEqual(game.threat_size({"standing": 100}, 110), 0)
self.assertEqual(game.threat_size({"standing": 100}, 260), 3)
self.assertEqual(game.threat_size({"strength": 20}, 20), 0) # rumoured before v29
def test_v28_worlds_pay_the_coming_threats_guards(self):
world = world_with()
world["version"] = 28
game.migrate(world, T0)
self.assertEqual(game.guards_on_duty(world), game.guard_posts(world))
def test_bigger_bands_take_more_scouting(self):
small = {"kind": "raid", "strength": 20}
huge = {"kind": "raid", "strength": 150}
@ -1098,6 +1134,7 @@ class TierTest(unittest.TestCase):
def test_militia_takes_the_best_weapons_first(self):
world = world_with()
world["city"]["stockpile"] = {"copper_sword": 50, "rune_sword": 2, "iron_shield": 3}
world["city"]["wages"] = 10_000
parts = game.defence(world)["parts"]
armed = int(world["city"]["population"] // 2) # 10
self.assertEqual(parts["weapons"], round(0.35 * (2 * 35 + (armed - 2) * 4), 1))
@ -1592,6 +1629,8 @@ class WearTest(unittest.TestCase):
def test_only_issued_gear_breaks_worst_first(self):
self.city["stockpile"] = {"iron_sword": 10, "copper_sword": 30}
self.city["buildings"]["barracks"]["level"] = 1
self.city["wages"] = 10_000
rng = random.Random()
rng.random = lambda: 0.0
broken = game.wear_kit(self.world, 0.4, rng)
@ -1601,6 +1640,7 @@ class WearTest(unittest.TestCase):
def test_a_won_raid_breaks_some_kit_and_reports_it(self):
self.city["stockpile"] = {"copper_sword": 20, "arrows": 100}
self.city["wages"] = 10_000
event = {"strength": 1, "sabotage": 0, "group": "the Red Hand"}
game._resolve_raid(self.world, event, T0, random.Random(1))
self.assertLess(self.city["stockpile"]["copper_sword"], 20)
@ -1610,6 +1650,107 @@ class WearTest(unittest.TestCase):
self.assertIn("broken in the fighting", text)
class GoldTest(unittest.TestCase):
def setUp(self):
self.world = world_with(("ann", "commoner"), ("bob", "commoner"))
self.ann, self.bob = self.world["players"]["ann"], self.world["players"]["bob"]
self.ann["gold"] = self.bob["gold"] = 200_000
def caravan(self, wares):
self.world["caravan"] = {"id": 7, "at": T0 - 1, "until": T0 + 3600, "wares": wares, "look": "e_wolf"}
def test_rented_slots_cost_more_each_time(self):
before = game.bank_slots(self.world, self.ann)
self.assertEqual([game.rent_cost({"rented": n}) for n in range(4)], [1000, 1350, 1800, 2450])
self.assertIsNone(game.rent_slot(self.ann))
self.assertIsNone(game.rent_slot(self.ann))
self.assertEqual(game.bank_slots(self.world, self.ann), before + 2)
self.assertEqual(self.ann["gold"], 200_000 - 2350)
self.ann["gold"] = 0
self.assertEqual(game.rent_slot(self.ann), "Needs 1,800 gold")
def test_caravan_comes_on_the_prep_day_and_sells_once_per_visit(self):
self.world["next_event_at"] = T0
game.tick(self.world, T0, random.Random(4))
caravan = self.world["caravan"]
self.assertTrue(T0 + 2 * game.HOUR <= caravan["at"] <= T0 + 16 * game.HOUR)
self.assertIsNone(game.caravan_view(self.world, self.ann, T0))
game.tick(self.world, caravan["at"] + 1, random.Random(4))
view = game.caravan_view(self.world, self.ann, caravan["at"] + 1)
self.assertEqual(len(view["wares"]), game.CARAVAN_WARES)
self.assertIsNone(game.caravan_view(self.world, self.ann, caravan["until"] + 1))
def test_wares_boost_your_own_work_for_their_charges(self):
self.caravan(["prospector", "tutor"])
self.assertIsNone(game.buy_ware(self.world, self.ann, "prospector", None, T0))
self.assertEqual(game.buy_ware(self.world, self.ann, "prospector", None, T0), "One of each per visit")
self.assertEqual(self.ann["gold"], 200_000 - 1550) # mining 1: 1,500 × (1 + 1/30)
self.assertEqual(game.ware(self.ann, "yield", "mining"), 1.5)
self.assertEqual(game.ware(self.bob, "yield", "mining"), 1)
self.ann["boosts"][0]["left"] = 1
game.ware(self.ann, "yield", "mining", spend=True)
self.assertEqual(self.ann["boosts"], [])
self.assertEqual(game.buy_ware(self.world, self.ann, "tutor", None, T0), "Pick a skill")
self.assertIsNone(game.buy_ware(self.world, self.ann, "tutor", "smithing", T0))
self.assertEqual(game.ware(self.ann, "xp", "smithing"), 1.5)
def test_ware_prices_rise_with_your_level(self):
self.ann["xp"]["mining"] = game.XP_TABLE[60]
self.assertEqual(game.ware_price(self.ann, "prospector", "mining"), 4500)
def test_threat_wares_end_with_the_threat(self):
self.caravan(["spyglass"])
self.assertEqual(game.buy_ware(self.world, self.ann, "spyglass", None, T0), "There's no threat coming")
self.world["next_event_at"] = T0
game.tick(self.world, T0, random.Random(4))
self.caravan(["spyglass"])
self.assertIsNone(game.buy_ware(self.world, self.ann, "spyglass", None, T0))
self.assertEqual(game.ware(self.ann, "recon", "reconnaissance"), 1.5)
raid = game.upcoming_raid(self.world)
raid["strength"] = 0
game.tick(self.world, raid["fire_at"], random.Random(4))
self.assertEqual(game.ware(self.ann, "recon", "reconnaissance"), 1)
def test_looks_need_owning_or_earning(self):
look = {"field": "navy", "trim": "brass", "emblem": None, "title": None}
self.assertIsNone(game.set_look(self.ann, look))
self.assertEqual(game.set_look(self.ann, {**look, "emblem": "e_wolf"}), "You don't own that")
self.caravan([])
self.assertIsNone(game.buy_ware(self.world, self.ann, "e_wolf", None, T0))
self.assertIsNone(game.set_look(self.ann, {**look, "emblem": "e_wolf"}))
self.assertEqual(game.look_view(self.ann)["emblem"], "wolf-head")
self.assertEqual(game.set_look(self.ann, {**look, "title": "Master smith"}), "You haven't earned that title")
self.ann["xp"]["smithing"] = game.XP_TABLE[50]
self.assertIsNone(game.set_look(self.ann, {**look, "title": "Master smith"}))
self.assertIsNone(game.upgrade_home(self.ann))
self.assertEqual(game.look_view(self.ann)["home"], "Cottage")
def test_monuments_take_gold_and_carve_the_donors_in(self):
city = self.world["city"]
self.assertIsNone(game.give_monument(self.world, self.ann, "fountain", 20_000, T0))
self.assertIsNone(game.give_monument(self.world, self.bob, "fountain", "all", T0))
self.assertEqual(self.bob["gold"], 200_000 - 5_000)
self.assertEqual(city["built"][0]["donors"], {"ann": 20_000, "bob": 5_000})
self.assertEqual(game.give_monument(self.world, self.ann, "fountain", 1, T0), "It's already built")
self.assertEqual(self.ann["favour"], 0)
game.give_monument(self.world, self.ann, "statue", 30_000, T0)
game.give_monument(self.world, self.bob, "statue", 20_000, T0)
self.assertEqual(city["carving"]["by"], "ann")
self.assertEqual(game.carve_statue(self.world, self.bob, "bob", T0), "It isn't yours to pick")
self.assertIsNone(game.carve_statue(self.world, self.ann, "bob", T0))
self.assertEqual(city["built"][-1]["of"], "bob")
def test_v28_worlds_get_gold_sinks(self):
world = world_with(("ann", "commoner"))
world["version"] = 28
for key in ("rented", "boosts", "bought", "deeds", "owned", "look"):
del world["players"]["ann"][key]
del world["caravan"]
game.migrate(world, T0)
self.assertEqual(world["players"]["ann"]["look"], game.DEFAULT_LOOK)
self.assertIsNone(world["caravan"])
class ClassTest(unittest.TestCase):
def lucky_run(self, cls, action_id, bank):
world = world_with(("pat", cls))