Happenings: weather and seasons, minor threats, hardships, fortunes, dragons
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a4e4bd4385
commit
75327f30e0
6 changed files with 903 additions and 38 deletions
|
|
@ -828,5 +828,164 @@ class ConstructionTest(unittest.TestCase):
|
|||
self.assertIsNone(p["equip"]["tool_construction"])
|
||||
|
||||
|
||||
|
||||
class HappeningsTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.world = world_with(("ann", "warrior"), ("sam", "scout"))
|
||||
self.world["next_event_at"] = None
|
||||
for track in ("minor", "hardship", "fortune"):
|
||||
self.world[f"next_{track}_at"] = None
|
||||
self.city = self.world["city"]
|
||||
self.rng = random.Random(4)
|
||||
|
||||
def test_weather_rolls_and_seasons_turn(self):
|
||||
seen = set()
|
||||
for hour in range(1, 24 * 14):
|
||||
game.tick(self.world, T0 + hour * game.HOUR, self.rng)
|
||||
seen.add(self.city["weather"]["kind"])
|
||||
self.assertGreaterEqual(len(seen), 3)
|
||||
self.assertEqual(game.season(self.world, T0)["name"], "Spring")
|
||||
self.assertEqual(game.season(self.world, T0 + 8 * game.DAY)["name"], "Summer")
|
||||
self.assertEqual(game.season(self.world, T0 + 22 * game.DAY)["name"], "Winter")
|
||||
|
||||
def test_weather_changes_skill_speed(self):
|
||||
p = self.world["players"]["ann"]
|
||||
farm = game.ACTION_BY_ID["farm_wheat"]
|
||||
plain = game.action_time(self.world, p, farm)
|
||||
self.city["weather"]["kind"] = "heat"
|
||||
self.assertAlmostEqual(game.action_time(self.world, p, farm), plain * 0.85)
|
||||
self.assertAlmostEqual(game.city_rate(self.world, "food"), 1.15)
|
||||
|
||||
def test_sickness_makes_the_city_eat_more(self):
|
||||
game._hardship(self.world, T0, self.rng, "sickness")
|
||||
self.assertAlmostEqual(game.city_rate(self.world, "food"), 1.5)
|
||||
self.assertEqual(self.world["events"][-1]["report"]["icon"], "🤒")
|
||||
|
||||
def test_locusts_and_rot_take_from_stores_and_buildings_protect(self):
|
||||
self.city["stockpile"] = {"bread": 100, "wheat": 100, "logs": 100, "stone": 100}
|
||||
game._hardship(self.world, T0, self.rng, "locusts")
|
||||
self.assertEqual(self.city["stockpile"]["bread"], 60)
|
||||
self.assertEqual(self.city["stockpile"]["stone"], 100)
|
||||
self.city["buildings"]["lumber_shed"]["level"] = 2
|
||||
game._hardship(self.world, T0, self.rng, "rot")
|
||||
self.assertEqual(self.city["stockpile"]["logs"], 85)
|
||||
|
||||
def test_storm_damages_a_building(self):
|
||||
self.city["buildings"]["walls"]["level"] = 1
|
||||
game._hardship(self.world, T0, self.rng, "storm")
|
||||
self.assertTrue(self.city["buildings"]["walls"]["damaged"])
|
||||
|
||||
def test_fire_is_put_out_by_water_barrels(self):
|
||||
self.city["stockpile"] = {"water_barrel": 6, "logs": 100}
|
||||
game._hardship(self.world, T0, self.rng, "fire")
|
||||
self.assertEqual(self.city["stockpile"], {"water_barrel": 1, "logs": 100})
|
||||
self.assertIn("the fire brigade", game.city_uses("water_barrel"))
|
||||
|
||||
def test_winter_burns_firewood_or_the_city_goes_cold(self):
|
||||
game._hardship(self.world, T0, self.rng, "winter")
|
||||
self.city["stockpile"] = {"logs": 3}
|
||||
game.tick(self.world, T0 + 4 * game.HOUR, self.rng)
|
||||
self.assertNotIn("logs", self.city["stockpile"])
|
||||
self.assertTrue(self.city["cold"])
|
||||
|
||||
def test_wolves_halve_farming_until_fighters_clear_them(self):
|
||||
game._minor_threat(self.world, T0, self.rng, "wolves")
|
||||
p = self.world["players"]["ann"]
|
||||
self.assertEqual(game.skill_mods(self.world, game.ACTION_BY_ID["farm_wheat"])["yield"], 0.5)
|
||||
p["xp"]["combat"] = game.XP_TABLE[30]
|
||||
p["hp"] = game.max_hp(p)
|
||||
self.assertIsNone(game.start_action(self.world, p, "clear_wolves", T0))
|
||||
summary = game.process_player(self.world, p, T0 + 3600, random.Random(1))
|
||||
self.assertIsNone(game.active_problem(self.world, "wolves"))
|
||||
self.assertEqual(summary["stopped"], "That's been dealt with")
|
||||
|
||||
def test_collapse_blocks_ore_until_planks_shore_it_up(self):
|
||||
game._minor_threat(self.world, T0, self.rng, "collapse")
|
||||
p = self.world["players"]["ann"]
|
||||
self.assertIn("collapsed", game.start_action(self.world, p, "mine_copper", T0))
|
||||
self.assertIsNone(game.start_action(self.world, p, "mine_stone", T0))
|
||||
p["bank"] = {"plank": 50}
|
||||
self.assertIsNone(game.aid_problem(self.world, p, "collapse", "plank", "bank", T0))
|
||||
self.assertEqual(p["bank"], {"plank": 20})
|
||||
self.assertIsNone(game.active_problem(self.world, "collapse"))
|
||||
|
||||
def test_flood_recedes_on_its_own(self):
|
||||
game._minor_threat(self.world, T0, self.rng, "flood")
|
||||
game.tick(self.world, T0 + 37 * game.HOUR, self.rng)
|
||||
self.assertIsNone(game.active_problem(self.world, "flood"))
|
||||
|
||||
def test_goblins_steal_every_hour(self):
|
||||
self.city["stockpile"] = {"stone": 1000}
|
||||
game._minor_threat(self.world, T0, self.rng, "goblins")
|
||||
game.tick(self.world, T0 + 5 * game.HOUR, self.rng)
|
||||
self.assertLess(self.city["stockpile"]["stone"], 1000)
|
||||
|
||||
def test_fortunes(self):
|
||||
game._fortune(self.world, T0, self.rng, "harvest")
|
||||
self.assertEqual(game.skill_mods(self.world, game.ACTION_BY_ID["farm_wheat"])["yield"], 2)
|
||||
game._fortune(self.world, T0, self.rng, "master")
|
||||
self.assertTrue(any(e["kind"] == "master" for e in self.city["effects"]))
|
||||
before = self.city["treasury"]
|
||||
game._fortune(self.world, T0, self.rng, "noble")
|
||||
self.assertGreater(self.city["treasury"], before)
|
||||
game._fortune(self.world, T0, self.rng, "pilgrims")
|
||||
self.assertEqual(self.world["players"]["ann"]["bank"]["bones"], 10)
|
||||
game.tick(self.world, T0 + 13 * game.HOUR, self.rng)
|
||||
self.assertFalse(any(e["kind"] == "harvest" for e in self.city["effects"]))
|
||||
|
||||
def test_festival_boosts_favour(self):
|
||||
game._fortune(self.world, T0, self.rng, "festival")
|
||||
p = self.world["players"]["ann"]
|
||||
p["bank"] = {"stone": 100}
|
||||
game.donate(self.world, p, "stone", 100, T0)
|
||||
self.assertEqual(p["favour"], 150)
|
||||
|
||||
def test_mercenaries_join_the_next_defence(self):
|
||||
self.world["next_event_at"] = T0
|
||||
game.tick(self.world, T0, self.rng)
|
||||
game._fortune(self.world, T0, self.rng, "mercenaries")
|
||||
p = self.world["players"]["ann"]
|
||||
self.city["treasury"] = 5000
|
||||
before = game.defence(self.world)["total"]
|
||||
self.assertIsNone(game.hire_mercenaries(self.world, p, T0))
|
||||
self.assertEqual(game.defence(self.world)["total"], before + 25)
|
||||
|
||||
def test_dragons_burn_when_they_win(self):
|
||||
self.city["buildings"]["houses"]["level"] = 1
|
||||
self.city["stockpile"] = {"logs": 500, "bread": 200}
|
||||
self.world["events"].append({"id": 99, "kind": "dragon", "status": "upcoming", "announced": T0,
|
||||
"fire_at": T0 + 10, "recon": 0, "sabotage": 0.0, "jitter": 0,
|
||||
"strength": 1000, "group": "a green dragon", "trait": "x"})
|
||||
self.assertEqual(game.upcoming_raid(self.world)["kind"], "dragon")
|
||||
game.tick(self.world, T0 + 20, self.rng)
|
||||
report = self.world["events"][-1]["report"]
|
||||
self.assertIn("ablaze", report["title"])
|
||||
self.assertTrue(self.city["buildings"]["houses"]["damaged"])
|
||||
self.assertLess(self.city["stockpile"]["logs"], 500)
|
||||
|
||||
def test_tracks_fire_on_their_own_cooldowns(self):
|
||||
self.world["next_fortune_at"] = T0
|
||||
self.world["next_hardship_at"] = T0
|
||||
game.tick(self.world, T0 + 1, self.rng)
|
||||
tracks = [e.get("track") for e in self.world["events"]]
|
||||
self.assertIn("fortune", tracks)
|
||||
self.assertIn("hardship", tracks)
|
||||
self.assertGreater(self.world["next_fortune_at"], T0 + 30 * game.HOUR)
|
||||
|
||||
def test_v7_worlds_get_weather_and_tracks(self):
|
||||
world = world_with()
|
||||
world["version"] = 7
|
||||
for key in ("weather", "effects", "problems", "hired"):
|
||||
del world["city"][key]
|
||||
for key in ("granary", "lumber_shed", "well"):
|
||||
del world["city"]["buildings"][key]
|
||||
for track in ("minor", "hardship", "fortune"):
|
||||
del world[f"next_{track}_at"]
|
||||
game.migrate(world, T0)
|
||||
self.assertEqual(world["city"]["weather"]["kind"], "clear")
|
||||
self.assertIn("granary", world["city"]["buildings"])
|
||||
self.assertIsNotNone(world["next_fortune_at"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue