feat(currency): copper/silver/gold replace the coin
The three denominations are move tokens, not stored buckets — there is one integer, so change-making never arises. The deserter is down to 47c: under a silver, so he cannot cover a bed and the player sees it on turn one. A parity test guards the code-side ratios against the content-side ids. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QYa9u7Kdxv5gX4AnwWexy8
This commit is contained in:
@@ -61,9 +61,10 @@ static func construct(origin: Dictionary, world: ContentDB, creation: Dictionary
|
||||
var qd: Dictionary = world.quest(quest_id)
|
||||
log.active_quests.append(Quest.new(qd.get("id", ""), qd.get("name", ""), "active", qd.get("objective", "")))
|
||||
|
||||
# inventory grants -> game state (NOT the log). Charter §2.
|
||||
# inventory grants -> game state (NOT the log). Charter §2. grant() routes
|
||||
# currency to the purse; everything else to the inventory dict.
|
||||
for grant in origin.get("inventory_grants", []):
|
||||
state.add_item(grant.get("item_id", ""), int(grant.get("qty", 0)))
|
||||
state.grant(grant.get("item_id", ""), int(grant.get("qty", 0)))
|
||||
|
||||
return {"ok": true, "errors": [], "log": log, "state": state}
|
||||
|
||||
|
||||
@@ -18,7 +18,24 @@ func test_loads_world_ids():
|
||||
assert_true(db.has_location("greywater_docks"))
|
||||
assert_true(db.has_quest("find_the_ledger"))
|
||||
assert_true(db.has_item("worn_shortsword"))
|
||||
assert_true(db.has_item("coin"))
|
||||
assert_true(db.has_item("copper"))
|
||||
assert_true(db.has_item("silver"))
|
||||
assert_true(db.has_item("gold"))
|
||||
assert_false(db.has_item("coin"), "the undifferentiated coin is gone")
|
||||
|
||||
|
||||
func test_currency_items_agree_with_the_code():
|
||||
# The denomination ids exist twice — in code (Currency.VALUES, because the
|
||||
# ratios are rules) and in content (slot: "currency"). Guard the two against
|
||||
# drifting apart, since nothing at runtime reconciles them.
|
||||
var content_ids: Array = []
|
||||
for id in db.items:
|
||||
if db.items[id].get("slot", "") == "currency":
|
||||
content_ids.append(id)
|
||||
content_ids.sort()
|
||||
var code_ids: Array = Currency.VALUES.keys()
|
||||
code_ids.sort()
|
||||
assert_eq(content_ids, code_ids)
|
||||
|
||||
|
||||
func test_companions_are_brannoc_and_cadwyn():
|
||||
|
||||
@@ -40,8 +40,9 @@ func test_numeric_luck_absent_from_log():
|
||||
|
||||
func test_inventory_and_dispo_land_in_state_not_log():
|
||||
var res := _build({"name": "Aldric", "class_id": "sellsword"})
|
||||
assert_eq(res["state"].inventory.get("coin", 0), 3)
|
||||
assert_eq(res["state"].inventory.get("worn_shortsword", 0), 1)
|
||||
assert_eq(res["state"].purse_copper, 47, "the deserter is down to his last coin")
|
||||
assert_false("copper" in res["state"].inventory, "money is never an inventory row")
|
||||
assert_false("inventory" in res["log"].to_dict())
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"disposition_overrides": { "brannoc_thane": 40, "cadwyn_vell": 15 },
|
||||
"inventory_grants": [
|
||||
{ "item_id": "worn_shortsword", "qty": 1 },
|
||||
{ "item_id": "coin", "qty": 3 }
|
||||
{ "item_id": "copper", "qty": 47 }
|
||||
],
|
||||
"start_quest_id": "find_the_ledger",
|
||||
"build_constraints": {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{ "id": "coin", "name": "coin", "slot": "currency" }
|
||||
1
content/world/items/copper.json
Normal file
1
content/world/items/copper.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "id": "copper", "name": "copper coin", "slot": "currency" }
|
||||
1
content/world/items/gold.json
Normal file
1
content/world/items/gold.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "id": "gold", "name": "gold coin", "slot": "currency" }
|
||||
1
content/world/items/silver.json
Normal file
1
content/world/items/silver.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "id": "silver", "name": "silver coin", "slot": "currency" }
|
||||
Reference in New Issue
Block a user