Master builder: favour buys double labour and XP for players, not finished work

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-19 11:01:29 +01:00
parent e6b8c327e8
commit fb4b8bf7c8
4 changed files with 53 additions and 21 deletions

30
game.py
View file

@ -1084,9 +1084,15 @@ def _complete(world: dict, player: dict, action: dict, now: float, rng: random.R
if action.get("special") == "problem" and action["skill"] != "combat":
_work_problem(world, player, action, now)
if action.get("special") == "labour":
site = world["city"]["construction"]
site["labour"] += labour_per_action(player)
summary["labour"] += labour_per_action(player)
city = world["city"]
site = city["construction"]
gain = labour_per_action(player)
# A master builder doubles the labour (and the XP) of whoever works while the boost lasts.
boost = min(city.get("site_boost", 0), gain)
city["site_boost"] = city.get("site_boost", 0) - boost
xp *= 1 + boost / gain
site["labour"] += gain + boost
summary["labour"] += gain + boost
if site["labour"] >= site["needed"]:
_finish_construction(world, now)
_start_next(world, now)
@ -1348,6 +1354,7 @@ def _start_next(world: dict, now: float) -> None:
else:
del building["fund"][item]
repair = building["damaged"]
city["boosts_bought"] = 0 # master builders cost the base price again on a new site
city["construction"] = {"building": key, "repair": repair, "start": now, "labour": 0.0,
"needed": labour_needed(building["level"], repair)}
verb = "Repairs to" if repair else "Work on"
@ -2295,7 +2302,8 @@ def happenings_view(world: dict, now: float) -> dict:
# --- favour --------------------------------------------------------------
BUILDER_FAVOUR_PER_LABOUR = 0.67
MASTER_BUILDER_LABOUR = 50 # boosted labour per hire: doubled labour and XP for whoever works it
MASTER_BUILDER_FAVOUR = 150 # +50% for every further hire on the same site
INFORMANT_FAVOUR = 250
RALLY_FAVOUR = 150
RALLY_DEFENCE = 10
@ -2305,14 +2313,14 @@ def favours(world: dict, now: float) -> dict:
"""What favour can buy for the city right now: cost, and why not if it can't."""
city = world["city"]
work = city["construction"]
remaining = max(0.0, work["needed"] - work["labour"]) if work else 0
event = upcoming_event(world)
raid = upcoming_raid(world)
return {
"builders": {
"cost": max(25, math.ceil(remaining / 2 * BUILDER_FAVOUR_PER_LABOUR)),
"why": None if work and remaining > 1 else "Nothing is being built",
"labour": round(remaining / 2),
"cost": round(MASTER_BUILDER_FAVOUR * (1 + 0.5 * city.get("boosts_bought", 0))),
"why": None if work else "Nothing is being built",
"labour": MASTER_BUILDER_LABOUR,
"boost": round(city.get("site_boost", 0)),
},
"informant": {
"cost": INFORMANT_FAVOUR,
@ -2340,9 +2348,11 @@ def spend_favour(world: dict, player: dict, kind: str, now: float) -> str | None
city = world["city"]
if kind == "builders":
work = city["construction"]
work["labour"] += (work["needed"] - work["labour"]) / 2
city["site_boost"] = city.get("site_boost", 0) + MASTER_BUILDER_LABOUR
city["boosts_bought"] = city.get("boosts_bought", 0) + 1
name = BUILDINGS[work["building"]]["name"].lower()
log(world, now, "favour", f"{player['name']} hired extra builders for the {name}", player["id"])
log(world, now, "favour", f"{player['name']} hired a master builder for the {name}: "
f"the next {MASTER_BUILDER_LABOUR} labour counts double", player["id"])
elif kind == "informant":
upcoming_event(world)["recon"] += RECON_PER_INTEL
log(world, now, "favour", f"{player['name']} paid an informant for news from the roads", player["id"])