Warmth shared by every pile hot enough

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-21 13:22:20 +01:00
parent b4ecacf96c
commit f7daa4e44d
3 changed files with 26 additions and 7 deletions

View file

@ -130,8 +130,9 @@ Alpha. One season, bandit raids plus two friendly events, six buildings.
so every log adds to how long it lasts and a full fire never shuts a
beginner out. Better logs burn hotter colours (red, gold, white, blue,
green, violet), burn first in their pile, and push your own cheaper fuel out
when your pile is full; the fire is as hot as its hottest pile. Whoever owns
that pile gets 10% of the XP of every craft the fire pays for. Because the fires are shared, the server runs
when your pile is full; the fire is as hot as its hottest pile. Warmth: 10%
of the XP of every craft the fire pays for, shared evenly by everyone whose
pile is hot enough for that craft on its own. Because the fires are shared, the server runs
everyone's actions as time passes, not only when they next look.
- **Events:** raids are announced a day or two ahead. Reconnaissance from
scouts, informants and the spymaster's guild narrows down when, how strong,

13
game.py
View file

@ -167,7 +167,7 @@ FIRES = {
FLAMES = [("Red", "#ff6a2a"), ("Gold", "#ffcb3d"), ("White", "#eeeaf8"),
("Blue", "#4fa3ff"), ("Green", "#3ed37a"), ("Violet", "#b07cff")]
TEND_XP = [2, 3, 4.5, 5.5, 6.5, 7.5] # per game minute of fire fed, by the log's tier
WARMTH = 0.1 # of a craft's XP, to whoever fed the fuel that paid for it
WARMTH = 0.1 # of a craft's XP, shared by everyone whose pile is hot enough for it
FIRE_LOW = 30 * 60 # game seconds left when a fire counts as low
@ -2139,12 +2139,15 @@ def fire_inputs(world: dict, action: dict) -> dict:
def _warm(world: dict, crafter: dict, action: dict, xp: float, now: float) -> None:
"""A fire paid for a craft: count it, and give its feeder a share of the XP."""
"""A fire paid for a craft: count it, and share the warmth between everyone whose
pile alone was hot enough for it (a beginner's logs count for bricks and copper)."""
fire = world["city"]["fires"][action["fire"]]
fire["saved"] = fire.get("saved", 0) + 1
keeper = world["players"].get(burning(fire["fuel"])["by"])
if keeper and keeper is not crafter:
gain_xp(world, keeper, "firekeeping", xp * WARMTH, now, keeper.setdefault("away", empty_summary()))
keepers = [world["players"].get(by) for by in {chunk["by"] for chunk in fire["fuel"]}
if burning(fire["fuel"], by)["tier"] >= action["fuel"]]
keepers = [keeper for keeper in keepers if keeper and keeper is not crafter]
for keeper in keepers:
gain_xp(world, keeper, "firekeeping", xp * WARMTH / len(keepers), now, keeper.setdefault("away", empty_summary()))
def fires_view(world: dict, now: float) -> dict:

View file

@ -1953,6 +1953,21 @@ class FirekeepingTest(unittest.TestCase):
self.assertEqual(self.fires["kitchen"]["saved"], 4)
self.assertEqual(self.sam["xp"]["firekeeping"], 0)
def test_warmth_is_shared_by_every_pile_hot_enough_for_the_craft(self):
self.world["players"]["ash"] = ash = game.new_player("ash", "Ash", "commoner", T0)
ash["xp"]["cooking"] = game.XP_TABLE[15]
self.fires["kitchen"].update(at=T0, fuel=[{"tier": 3, "left": 6000, "by": "kai"},
{"tier": 0, "left": 6000, "by": "sam"}])
ash["bank"] = {"raw_shrimp": 1, "raw_trout": 1}
game.start_action(self.world, ash, "cook_shrimp", T0, once=True)
game.process_player(self.world, ash, T0 + 25, random.Random(1))
self.assertAlmostEqual(self.kai["xp"]["firekeeping"], 6 * game.WARMTH / 2)
self.assertAlmostEqual(self.sam["xp"]["firekeeping"], 6 * game.WARMTH / 2)
game.start_action(self.world, ash, "cook_trout", T0 + 25, once=True)
game.process_player(self.world, ash, T0 + 60, random.Random(1))
self.assertAlmostEqual(self.kai["xp"]["firekeeping"], 6 * game.WARMTH / 2 + 15 * game.WARMTH) # trout: kai's alone
self.assertAlmostEqual(self.sam["xp"]["firekeeping"], 6 * game.WARMTH / 2)
def test_catch_up_runs_everyone_and_keeps_what_they_missed(self):
game.start_action(self.world, self.sam, "mine_stone", T0)
self.assertTrue(game.catch_up(self.world, T0 + 100, random.Random(1)))