Encounter cards: gold range as a reward chip, drop the odds lines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-20 14:21:34 +01:00
parent ba7556de4d
commit ea1bae9b53
3 changed files with 11 additions and 9 deletions

View file

@ -564,7 +564,9 @@ def _encounter(id: str, name: str, group: str, level: int, roster: list, gold: i
total = sum(v["weight"] for v in variants)
xp = round(sum(v["xp"] * v["weight"] for v in variants) / total)
enemy = {"name": name, "level": level, "boss": boss, "variants": variants}
return _action(id, "combat", extra.pop("label", None) or name, level, RESPAWN, xp, gold=gold,
# Gold per kill is a range around the encounter's worth.
purse = [max(1, round(gold * 0.7)), max(2, round(gold * 1.3))] if gold else None
return _action(id, "combat", extra.pop("label", None) or name, level, RESPAWN, xp, gold=purse,
drops=[list(drop) for drop in drops], enemy=enemy, group=group, **extra)
@ -1865,8 +1867,9 @@ def _complete(world: dict, player: dict, action: dict, now: float, rng: random.R
gain_xp(world, player, "combat", xp // 3, now, summary)
return
summary["wins"] += 1
player["gold"] += action.get("gold", 0)
summary["gold"] += action.get("gold", 0)
gold = rng.randint(*action["gold"]) if action.get("gold") else 0
player["gold"] += gold
summary["gold"] += gold
for item, qty, odds in action.get("drops", []):
if rng.random() < odds:
outputs[item] = outputs.get(item, 0) + qty

View file

@ -708,11 +708,9 @@ function actionCard(a) {
io += Object.entries(a.outputs).map(([id, q]) => itemChip(id, q)).join("");
let extra = "";
if (a.enemy) {
const o = me.fight_odds[a.id], e = a.enemy, pct = (x) => `${Math.round(x * 100)}%`;
extra = `${variantRows(e, o)}
<div class="small">Hits up to <b>${o.their_max}</b>${e.boss ? ` · <span class="tag warn">Boss</span>` : ""}</div>
<div class="small">You hit <b>${pct(o.hit)}</b> (max ${o.max_hit}) · it hits <b>${pct(o.their_hit)}</b></div>
<div class="small">~<b>${dur(o.kill_time)}</b> a kill · <b>${Math.round(o.damage)}</b> damage taken${a.gold ? ` · ${a.gold} gold` : ""}</div>`;
const o = me.fight_odds[a.id], e = a.enemy;
extra = `${e.boss ? `<div><span class="tag warn">Boss</span></div>` : ""}${variantRows(e, o)}`;
if (a.gold) io += `<span class="item-chip" title="Gold: ${a.gold[0]}–${a.gold[1]} a kill">${COIN} ${a.gold[0]}–${a.gold[1]}</span>`;
if (a.drops) io += a.drops.map(([id, q, odds]) => `<span class="item-chip" title="${esc(item(id).name)}${q > 1 ? ` ×${q}` : ""}: ${Math.round(odds * 100)}% chance">${item(id).icon} ${Math.round(odds * 100)}%</span>`).join("");
if (a.special === "sabotage") extra += `<div class="small muted">-3% raid strength per win (max 30%)</div>`;
}

View file

@ -163,7 +163,8 @@ class ActionTest(unittest.TestCase):
summary = game.process_player(world, p, T0 + 3600, random.Random(5))
self.assertGreater(summary["done"], 50) # a fight lasts as long as it takes
self.assertEqual(summary["wins"] + summary["losses"], summary["done"])
self.assertEqual(p["gold"], summary["wins"] * 2)
low, high = game.ACTION_BY_ID["fight_wolves"]["gold"]
self.assertTrue(summary["wins"] * low <= p["gold"] <= summary["wins"] * high)
class GiftTest(unittest.TestCase):