Personal ledger on the character sheet
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ece0f101aa
commit
4b0724e6e5
4 changed files with 159 additions and 11 deletions
114
game.py
114
game.py
|
|
@ -23,6 +23,7 @@ CLASS_SPEED = 0.8
|
|||
LUCKY_CHANCE = 0.1
|
||||
OVERFLOW_FAVOUR = 0.5 # favour for items a full bank spills to the city, vs. giving on purpose
|
||||
MAX_LOG = 200
|
||||
PLAYER_LOG = 60 # lines in each player's own ledger
|
||||
REGEN_PER_HOUR = 0.25 # share of max HP recovered per game hour
|
||||
REST_RATE = 0.01 # share of max HP back per game second resting between fights...
|
||||
BITE = 2.0 # ...and each bite of food takes this long, so food rests you faster
|
||||
|
|
@ -1624,7 +1625,7 @@ RAID_TRAITS = ["lightly armoured, heavy on archers", "mounted and fast, few bows
|
|||
|
||||
# --- world ---------------------------------------------------------------
|
||||
|
||||
WORLD_VERSION = 35
|
||||
WORLD_VERSION = 36
|
||||
|
||||
|
||||
def new_world(now: float, speed: float = 1.0) -> dict:
|
||||
|
|
@ -1890,6 +1891,11 @@ def migrate(world: dict, now: float | None = None) -> dict:
|
|||
# Townsfolk needs: everyone starts Content and drifts to what the city gives them.
|
||||
world["city"].update(happiness=0.5, clothes_due=0.0, unheated=False)
|
||||
world["version"] = 35
|
||||
if world["version"] < 36:
|
||||
# Your own ledger, from here on.
|
||||
for player in world["players"].values():
|
||||
player.setdefault("ledger", [])
|
||||
world["version"] = 36
|
||||
return world
|
||||
|
||||
|
||||
|
|
@ -2079,6 +2085,64 @@ def log(world: dict, now: float, kind: str, text: str, who: str | None = None) -
|
|||
del world["ledger"][:-MAX_LOG]
|
||||
|
||||
|
||||
def note(world: dict, player: dict, now: float, verb: str, item: str | None = None, qty: int = 0,
|
||||
gold: int = 0, who: str | None = None, label: str | None = None) -> None:
|
||||
"""A line in the player's own ledger: "Sold 40 bones". The same again within the hour adds up."""
|
||||
ledger = player["ledger"]
|
||||
key = (verb, item, who, label)
|
||||
entry = next((e for e in reversed(ledger[-10:]) if "verb" in e and (e["verb"], e.get("item"), e.get("who"), e.get("label")) == key
|
||||
and now - e["at"] < HOUR / speed(world)), None)
|
||||
if entry:
|
||||
ledger.remove(entry)
|
||||
else:
|
||||
entry = {"verb": verb, **{k: v for k, v in (("item", item), ("who", who), ("label", label)) if v}}
|
||||
entry["at"] = now
|
||||
if qty:
|
||||
entry["qty"] = entry.get("qty", 0) + qty
|
||||
if gold:
|
||||
entry["gold"] = entry.get("gold", 0) + gold
|
||||
ledger.append(entry)
|
||||
del ledger[:-PLAYER_LOG]
|
||||
|
||||
|
||||
def _tally(action: dict, summary: dict) -> tuple:
|
||||
"""What a summary holds so far that track() counts: items got (kept, spilled, sold or delivered), XP, gold, wins, losses."""
|
||||
got = {}
|
||||
for key in ("items", "overflow", "sold"):
|
||||
for item, qty in summary[key].items():
|
||||
got[item] = got.get(item, 0) + qty
|
||||
if summary.get("delivered") and action["outputs"]:
|
||||
made = next(iter(action["outputs"]))
|
||||
got[made] = got.get(made, 0) + summary["delivered"]
|
||||
return got, sum(summary["xp"].values()), summary["gold"], summary["wins"], summary["losses"]
|
||||
|
||||
|
||||
def track(world: dict, player: dict, action: dict, began: float, end: float, before: tuple, summary: dict) -> None:
|
||||
"""One action done, added to its stretch in the ledger: "Mine copper · 3h 20m · 250 copper ore".
|
||||
A stretch goes on while the next one starts where the last ended (two crops take turns)."""
|
||||
got, xp, gold, won, lost = _tally(action, summary)
|
||||
old_got, old_xp, old_gold, old_won, old_lost = before
|
||||
ledger = player["ledger"]
|
||||
entry = next((e for e in reversed(ledger[-10:]) if e.get("act") == action["id"]
|
||||
and began - e["at"] <= end - began + 300 / speed(world)), None)
|
||||
if entry:
|
||||
ledger.remove(entry)
|
||||
else:
|
||||
entry = {"act": action["id"], "start": began, "n": 0, "xp": 0, "gold": 0, "items": {}}
|
||||
entry["at"] = end
|
||||
entry["n"] += 1
|
||||
entry["xp"] = round(entry["xp"] + xp - old_xp, 1)
|
||||
entry["gold"] += gold - old_gold
|
||||
for item, qty in got.items():
|
||||
if qty > old_got.get(item, 0):
|
||||
entry["items"][item] = entry["items"].get(item, 0) + qty - old_got.get(item, 0)
|
||||
if "enemy" in action:
|
||||
entry["won"] = entry.get("won", 0) + won - old_won
|
||||
entry["lost"] = entry.get("lost", 0) + lost - old_lost
|
||||
ledger.append(entry)
|
||||
del ledger[:-PLAYER_LOG]
|
||||
|
||||
|
||||
def building_level(world: dict, key: str) -> int:
|
||||
"""Effective level: a damaged building does nothing until repaired."""
|
||||
building = world["city"]["buildings"][key]
|
||||
|
|
@ -2151,6 +2215,7 @@ def new_player(uid: str, name: str, cls: str, now: float) -> dict:
|
|||
"garrison": False,
|
||||
"donated": 0,
|
||||
"gave": {}, # item (or "gold"): {qty, worth, favour} given to the city, see credit()
|
||||
"ledger": [], # what you did, see note() and track()
|
||||
"pinned": [], # skills pinned to the top of the sidebar
|
||||
"tracked": None, # recipe shown under the top bar
|
||||
}
|
||||
|
|
@ -3015,7 +3080,15 @@ def can_start(world: dict, player: dict, action: dict) -> str | None:
|
|||
return None
|
||||
|
||||
|
||||
def _complete(world: dict, player: dict, action: dict, now: float, rng: random.Random, summary: dict,
|
||||
def _complete(world: dict, player: dict, action: dict, began: float, now: float, rng: random.Random, summary: dict,
|
||||
fight: dict | None = None, loot: tuple | None = None) -> None:
|
||||
"""Finish one action that ran from `began`, and put it in the player's ledger."""
|
||||
before = _tally(action, summary)
|
||||
_finish_action(world, player, action, now, rng, summary, fight, loot)
|
||||
track(world, player, action, began, now, before, summary)
|
||||
|
||||
|
||||
def _finish_action(world: dict, player: dict, action: dict, now: float, rng: random.Random, summary: dict,
|
||||
fight: dict | None = None, loot: tuple | None = None) -> None:
|
||||
"""Finish one action. A shared fight comes in as `fight`, with this member's share of its `loot`."""
|
||||
brew = potion_effect(player, action["skill"])
|
||||
|
|
@ -3217,7 +3290,7 @@ def process_player(world: dict, player: dict, now: float, rng: random.Random | N
|
|||
"""Run the player's action forward to `now` (capped at 24h past last_active)."""
|
||||
rng = rng or random.Random()
|
||||
summary = empty_summary()
|
||||
collect_orders(world, player)
|
||||
collect_orders(world, player, now)
|
||||
current = player["action"]
|
||||
if current and current.get("party"):
|
||||
if current["party"] in world["parties"]:
|
||||
|
|
@ -3265,7 +3338,7 @@ def process_player(world: dict, player: dict, now: float, rng: random.Random | N
|
|||
stop(player, "Bank full")
|
||||
summary["stopped"] = "Bank full"
|
||||
break
|
||||
_complete(world, player, action, current["start"] + duration, rng, summary)
|
||||
_complete(world, player, action, current["start"], current["start"] + duration, rng, summary)
|
||||
summary["done"] += 1
|
||||
current["start"] += duration
|
||||
if current.get("plot2"):
|
||||
|
|
@ -3645,7 +3718,7 @@ def process_party(world: dict, pid: str, now: float, rng: random.Random) -> None
|
|||
if results.get(player["id"], "left") == "left":
|
||||
continue
|
||||
summary = player.setdefault("away", empty_summary())
|
||||
_complete(world, player, action, end, rng, summary, fight=fight, loot=shares.get(player["id"]))
|
||||
_complete(world, player, action, end - fight["duration"], end, rng, summary, fight=fight, loot=shares.get(player["id"]))
|
||||
summary["done"] += 1
|
||||
together = summary.setdefault("with", {})
|
||||
for other in members:
|
||||
|
|
@ -3714,9 +3787,11 @@ def _clear_bonus(world: dict, player: dict, spec: dict, now: float, rng: random.
|
|||
summary = player.setdefault("away", empty_summary())
|
||||
player["gold"] += spec["bonus_gold"]
|
||||
summary["gold"] += spec["bonus_gold"]
|
||||
note(world, player, now, "Looted", "gold", spec["bonus_gold"], label=spec["name"])
|
||||
found = [trophy(rng.choice(list(STYLES)), spec["tier"]), *(reagent(rng.choice(list(REAGENT_NOUNS)), spec["tier"])
|
||||
for _ in range(2))] if spec["tier"] else []
|
||||
for item in found:
|
||||
note(world, player, now, "Looted", item, 1, label=spec["name"])
|
||||
kept = bank_add(world, player, item, 1, now, summary)
|
||||
if kept:
|
||||
summary["items"][item] = summary["items"].get(item, 0) + kept
|
||||
|
|
@ -4077,13 +4152,16 @@ def _deliver(world: dict, order: dict, giver: dict, qty: int, now: float,
|
|||
share = min(pay, round(base * INPUT_SHARE * sum(ITEMS[i]["value"] * n for i, n in items.items()) / worth))
|
||||
if share:
|
||||
helper["gold"] += share
|
||||
note(world, helper, now, "Earned", "gold", share, who=order["by"])
|
||||
earned = order.setdefault("earned", {})
|
||||
earned[uid] = earned.get(uid, 0) + share
|
||||
pay -= share
|
||||
giver["gold"] += pay
|
||||
if pay:
|
||||
note(world, giver, now, "Earned", "gold", pay, who=order["by"])
|
||||
owner = world["players"].get(order["by"])
|
||||
if owner:
|
||||
_hand_over(world, order, owner)
|
||||
_hand_over(world, order, owner, now)
|
||||
if order["delivered"] >= order["qty"]:
|
||||
order["status"] = "done"
|
||||
order["done"] = now
|
||||
|
|
@ -4092,18 +4170,19 @@ def _deliver(world: dict, order: dict, giver: dict, qty: int, now: float,
|
|||
f"{amount(order['item'], order['qty'])}")
|
||||
|
||||
|
||||
def _hand_over(world: dict, order: dict, owner: dict) -> None:
|
||||
def _hand_over(world: dict, order: dict, owner: dict, now: float) -> None:
|
||||
"""Put delivered items in the requester's bank; what doesn't fit waits on the order."""
|
||||
fits = min(order["held"], bank_room(world, owner, order["item"]))
|
||||
if fits:
|
||||
owner["bank"][order["item"]] = owner["bank"].get(order["item"], 0) + fits
|
||||
order["held"] -= fits
|
||||
note(world, owner, now, "Received", order["item"], fits, label="Work order")
|
||||
|
||||
|
||||
def collect_orders(world: dict, player: dict) -> None:
|
||||
def collect_orders(world: dict, player: dict, now: float) -> None:
|
||||
for order in world["orders"]:
|
||||
if order["by"] == player["id"] and order["held"]:
|
||||
_hand_over(world, order, player)
|
||||
_hand_over(world, order, player, now)
|
||||
|
||||
|
||||
def expire_orders(world: dict, now: float) -> None:
|
||||
|
|
@ -4189,6 +4268,7 @@ def donate(world: dict, player: dict, item: str, qty: int, now: float) -> str |
|
|||
city["stockpile"][item] = city["stockpile"].get(item, 0) + qty
|
||||
label = amount(item, qty)
|
||||
credit(player, item, qty, city_rate(world, "favour"))
|
||||
note(world, player, now, "Donated", item, qty)
|
||||
log(world, now, "donation", f"{player['name']} donated {label}", player["id"])
|
||||
return None
|
||||
|
||||
|
|
@ -4203,6 +4283,8 @@ def give(world: dict, giver: dict, receiver: dict, item: str, qty: int, now: flo
|
|||
return f"{receiver['name']} only has room for {room:,}"
|
||||
bank_take(giver, item, qty)
|
||||
receiver["bank"][item] = receiver["bank"].get(item, 0) + qty
|
||||
note(world, giver, now, "Gave", item, qty, who=receiver["id"])
|
||||
note(world, receiver, now, "Received", item, qty, who=giver["id"])
|
||||
log(world, now, "gift", f"{giver['name']} gave {amount(item, qty)} to {receiver['name']}", giver["id"])
|
||||
return None
|
||||
|
||||
|
|
@ -4211,7 +4293,9 @@ def sell(world: dict, player: dict, item: str, qty: int, now: float) -> str | No
|
|||
if qty <= 0 or player["bank"].get(item, 0) < qty:
|
||||
return "You don't have that many"
|
||||
bank_take(player, item, qty)
|
||||
player["gold"] += int(qty * sell_price(world, item, now))
|
||||
gold = int(qty * sell_price(world, item, now))
|
||||
player["gold"] += gold
|
||||
note(world, player, now, "Sold", item, qty, gold)
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -4256,6 +4340,7 @@ def fund(world: dict, player: dict, key: str, item: str, qty: int | str, source:
|
|||
else:
|
||||
bank_take(player, item, qty)
|
||||
credit(player, item, qty, city_rate(world, "favour"))
|
||||
note(world, player, now, "Donated", item, qty, label=BUILDINGS[key]["name"])
|
||||
elif item == "gold":
|
||||
city["treasury"] -= qty
|
||||
else:
|
||||
|
|
@ -4287,6 +4372,7 @@ def install(world: dict, player: dict, fitting: str, source: str, now: float) ->
|
|||
return "You don't have one"
|
||||
bank_take(player, fitting, 1)
|
||||
credit(player, fitting, 1, city_rate(world, "favour"))
|
||||
note(world, player, now, "Donated", fitting, 1, label=BUILDINGS[spec["building"]]["name"])
|
||||
elif source == "city":
|
||||
if not city["stockpile"].get(fitting):
|
||||
return "There isn't one in the stockpile"
|
||||
|
|
@ -4662,6 +4748,7 @@ def pay_wages(world: dict, player: dict, qty, source: str, now: float) -> str |
|
|||
if source == "bank":
|
||||
player["gold"] -= qty
|
||||
credit(player, "gold", qty, city_rate(world, "favour"))
|
||||
note(world, player, now, "Donated", "gold", qty, label="Guard wages")
|
||||
log(world, now, "donation", f"{player['name']} paid {qty:,} gold of guard wages", player["id"])
|
||||
else:
|
||||
city["treasury"] -= qty
|
||||
|
|
@ -5130,8 +5217,10 @@ def _resolve_dragon(world: dict, event: dict, now: float, rng: random.Random) ->
|
|||
player["favour"] += 40
|
||||
gain_xp(world, player, "combat", round(attack * 2.5), now)
|
||||
bank_add(world, player, "dragon_bones", 2, now, empty_summary())
|
||||
note(world, player, now, "Looted", "dragon_bones", 2)
|
||||
for weight in WEIGHTS:
|
||||
bank_add(world, player, reagent(weight, 3), 1, now, empty_summary())
|
||||
note(world, player, now, "Looted", reagent(weight, 3), 1)
|
||||
if garrison:
|
||||
lines.append("Everyone in the garrison earned 40 favour, combat experience, dragon bones and Ironhill reagents.")
|
||||
log(world, now, "raid", report["title"])
|
||||
|
|
@ -5631,6 +5720,7 @@ def _fortune(world: dict, now: float, rng: random.Random, kind: str | None = Non
|
|||
lines.append(f"They prayed at the temple: +{gift} devotion.")
|
||||
for player in world["players"].values():
|
||||
bank_add(world, player, "bones", 10, now, empty_summary())
|
||||
note(world, player, now, "Received", "bones", 10, label="Pilgrims")
|
||||
lines.append("They left 10 bones with every adventurer.")
|
||||
_record(world, now, "fortune", kind, "🙏", "Pilgrims passed through the city", lines)
|
||||
elif kind == "mercenaries":
|
||||
|
|
@ -5925,6 +6015,7 @@ def buy_ware(world: dict, player: dict, ware_id: str, skill: str | None, now: fl
|
|||
return f"Needs {spec['price']:,} gold"
|
||||
player["gold"] -= spec["price"]
|
||||
player["owned"].append(ware_id)
|
||||
note(world, player, now, "Bought", gold=spec["price"], label=spec["name"])
|
||||
else:
|
||||
if ware_id not in caravan["wares"]:
|
||||
return "Not for sale"
|
||||
|
|
@ -5939,6 +6030,7 @@ def buy_ware(world: dict, player: dict, ware_id: str, skill: str | None, now: fl
|
|||
if player["gold"] < price:
|
||||
return f"Needs {price:,} gold"
|
||||
player["gold"] -= price
|
||||
note(world, player, now, "Bought", gold=price, label=spec["name"])
|
||||
held = next((b for b in player["boosts"] if b["ware"] == ware_id and b["skill"] == skill), None)
|
||||
if spec["charges"] is None:
|
||||
if held:
|
||||
|
|
@ -6106,6 +6198,7 @@ def give_monument(world: dict, player: dict, key: str, qty, now: float) -> str |
|
|||
return "Not enough gold"
|
||||
qty = min(qty, room)
|
||||
player["gold"] -= qty
|
||||
note(world, player, now, "Donated", "gold", qty, label=spec["name"])
|
||||
fund["gold"] += qty
|
||||
fund["donors"][player["id"]] = fund["donors"].get(player["id"], 0) + qty
|
||||
if fund["gold"] >= spec["cost"]:
|
||||
|
|
@ -6591,6 +6684,7 @@ ORDER_RECIPES, ORDER_RAW = _order_sources()
|
|||
# line here (new / balance / fix), and a new release a new entry on top.
|
||||
PATCH_NOTES = [
|
||||
{"date": "2026-10-28", "title": "Queue", "notes": [
|
||||
("new", "Added a personal ledger to character sheet"),
|
||||
("new", "Work orders no longer erase your crafting queue"),
|
||||
("new", "Items link to where they're made: click to jump to the recipe."),
|
||||
("new", "Work order slots link to the recipe when you have none."),
|
||||
|
|
|
|||
|
|
@ -2733,10 +2733,34 @@ function characterSheet(uid) {
|
|||
${equipmentPanel(p, mine)}
|
||||
${mine ? lookPanel(p) : ""}
|
||||
</div>
|
||||
<div>
|
||||
<div class="panel"><h2>Skills</h2><div class="sheet-skills">${skillRows}</div></div>
|
||||
${mine ? ledgerPanel(p) : ""}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// What you did, newest first: a stretch at one action is one line ("Mine copper · 3h 20m"), gifts and sales their own.
|
||||
function ledgerPanel(p) {
|
||||
const got = (id, q) => id === "gold" ? `<span class="item-chip">${COIN} ${fmt(q)}</span>` : `${itemChip(id, q)} ${esc(item(id).name)}`;
|
||||
const rows = [...p.ledger].reverse().map((l) => {
|
||||
let line;
|
||||
if (l.act) {
|
||||
const a = C.actions.find((x) => x.id === l.act);
|
||||
const loot = Object.entries(l.items).map(([id, q]) => `<span>${got(id, q)}</span>`).join("");
|
||||
const fights = l.won !== undefined ? ` · ${fmt(l.won)} won${l.lost ? `, ${fmt(l.lost)} lost` : ""}` : ` · ×${fmt(l.n)}`;
|
||||
line = `${skillOf(a.skill).icon} <b>${a.enemy ? "Fought " : ""}${esc(a.name)}</b> <span class="muted">· ${dur(l.at - l.start)}${fights}</span>
|
||||
<div class="got">${loot}${l.gold ? `<span class="item-chip">${COIN} ${fmt(l.gold)}</span>` : ""}${l.xp ? `<span class="item-chip">${fmt(Math.round(l.xp))} XP</span>` : ""}</div>`;
|
||||
} else {
|
||||
const who = l.who ? ` ${l.verb === "Gave" ? "to" : "from"} ${esc(playerName(l.who))}` : "";
|
||||
line = `<b>${esc(l.verb)}</b> ${l.item ? got(l.item, l.qty) : esc(l.label)}${l.item && l.label ? ` <span class="muted">· ${esc(l.label)}</span>` : ""}${who}`
|
||||
+ (l.gold ? ` <span class="muted">·</span> <span class="item-chip">${COIN} ${l.verb === "Bought" ? "" : "+"}${fmt(l.gold)}</span>` : "");
|
||||
}
|
||||
return `<li>${line}<span class="when">${ago(l.at)}</span></li>`;
|
||||
}).join("");
|
||||
return `<div class="panel"><h2>Ledger</h2><ul class="ledger">${rows || `<li class="muted">Nothing yet.</li>`}</ul></div>`;
|
||||
}
|
||||
|
||||
function lookPanel(p) {
|
||||
const look = p.look, wear = (kind) => Object.keys(C.cosmetics).filter((k) => C.cosmetics[k].kind === kind && (C.free_looks.includes(k) || p.owned.includes(k)));
|
||||
const swatches = (part) => wear("colour").map((k) => `<button class="swatch ${look[part] === k ? "on" : ""}" style="background:${C.cosmetics[k].value}"
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ table.parts td:last-child { text-align: right; font-variant-numeric: tabular-num
|
|||
.ledger { list-style: none; margin: 0; padding: 0; max-height: 70vh; overflow-y: auto; }
|
||||
.ledger li { padding: 6px 0; border-bottom: 1px solid var(--line); font-size: 14px; }
|
||||
.ledger .when { color: var(--muted); font-size: 12px; display: block; }
|
||||
.ledger .got { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 4px; }
|
||||
.person .top { display: flex; gap: 12px; align-items: center; }
|
||||
.person .ico { font-size: 30px; }
|
||||
.levels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 18px; font-size: 13px; margin-top: 12px; }
|
||||
|
|
|
|||
|
|
@ -3378,3 +3378,32 @@ class TownsfolkTest(unittest.TestCase):
|
|||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
class LedgerTest(unittest.TestCase):
|
||||
def test_a_stretch_of_work_is_one_line(self):
|
||||
world = world_with(("fred", "commoner"))
|
||||
p = world["players"]["fred"]
|
||||
game.start_action(world, p, "mine_stone", T0)
|
||||
game.process_player(world, p, T0 + 100, random.Random(1))
|
||||
game.process_player(world, p, T0 + 200, random.Random(1))
|
||||
[line] = p["ledger"]
|
||||
self.assertEqual((line["act"], line["n"], line["items"], line["xp"]), ("mine_stone", 10, {"stone": 10}, 50))
|
||||
self.assertEqual(line["at"] - line["start"], 200)
|
||||
# Stopped and started again later: a new stretch.
|
||||
game.start_action(world, p, "mine_stone", T0 + 1000)
|
||||
game.process_player(world, p, T0 + 1020, random.Random(1))
|
||||
self.assertEqual([l["n"] for l in p["ledger"]], [10, 1])
|
||||
|
||||
def test_gifts_and_sales_add_up_within_the_hour(self):
|
||||
world = world_with(("fred", "commoner"), ("ann", "commoner"))
|
||||
fred, ann = world["players"]["fred"], world["players"]["ann"]
|
||||
fred["bank"] = {"stone": 30}
|
||||
game.give(world, fred, ann, "stone", 5, T0)
|
||||
game.give(world, fred, ann, "stone", 5, T0 + 60)
|
||||
game.sell(world, fred, "stone", 10, T0 + 120)
|
||||
self.assertEqual([(l["verb"], l["qty"]) for l in fred["ledger"]], [("Gave", 10), ("Sold", 10)])
|
||||
self.assertEqual(fred["ledger"][1]["gold"], 10 * game.ITEMS["stone"]["value"])
|
||||
self.assertEqual((ann["ledger"][0]["verb"], ann["ledger"][0]["who"]), ("Received", "fred"))
|
||||
game.sell(world, fred, "stone", 1, T0 + 2 * game.HOUR)
|
||||
self.assertEqual([l["verb"] for l in fred["ledger"]], ["Gave", "Sold", "Sold"])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue