Files
code_of_conquest_dnd/client/tests/unit/test_game_state.gd
Phillip Tarrant bd49e118d5 feat(newgame): the seeded creation pipeline
construct() takes a seed, not a stat block. It rebuilds the RNG and rolls the
attributes itself, so it never trusts a number handed to it (§2) and the same seed
yields an identical character, hidden Luck included (§10). The whole character is
reproducible from four primitives: seed, race_id, calling_id, spend. The rng
parameter is dropped — the seed is now the only source of randomness.

The roll is 3d6 with a hard floor of 8. Straight 3d6 puts ~9% on a primary the +3
pool cannot rescue, and a character bad at the one thing he is FOR is not grit —
it is the fine §7 forbids, paid for twenty hours.

LCK is unspendable, not merely hidden: spending on it is a validation error.
GameState.stats becomes GameState.sheet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QYa9u7Kdxv5gX4AnwWexy8
2026-07-12 20:43:51 -05:00

99 lines
2.5 KiB
GDScript

extends "res://addons/gut/test.gd"
const GameState = preload("res://scripts/state/game_state.gd")
func test_state_holds_a_sheet_not_a_stat_bag():
var gs = GameState.new()
assert_null(gs.sheet, "the sheet is built by NewGame.construct")
assert_false("stats" in gs, "the flat stats Dictionary is gone")
func test_add_item_accumulates():
var s = GameState.new()
s.add_item("worn_shortsword", 3)
s.add_item("worn_shortsword", 2)
assert_eq(s.inventory["worn_shortsword"], 5)
func test_npc_disposition_clamps():
var s = GameState.new()
s.set_npc_disposition("oda_fenn", 250)
assert_eq(s.npc_dispositions["oda_fenn"], 100)
func test_drift_luck_respects_base():
var s = GameState.new()
s.luck_base = 10
s.luck = 10
s.drift_luck(100)
assert_eq(s.luck, 13)
func test_luck_descriptor_delegates():
var s = GameState.new()
s.luck = 4
assert_eq(s.luck_descriptor(), "Fortune spits on you")
func test_remove_item_decrements_and_floors_at_zero():
var gs = GameState.new()
gs.add_item("worn_shortsword", 3)
gs.remove_item("worn_shortsword", 2)
assert_eq(gs.inventory.get("worn_shortsword", 0), 1)
gs.remove_item("worn_shortsword", 5)
assert_eq(gs.inventory.get("worn_shortsword", 0), 0)
func test_revealed_topics_tracked():
var gs = GameState.new()
assert_false(gs.is_revealed("varrell_twins"))
gs.mark_revealed("varrell_twins")
assert_true(gs.is_revealed("varrell_twins"))
func test_gifts_given_tracked():
var gs = GameState.new()
assert_false(gs.is_gift_given("amulet"))
gs.mark_gift_given("amulet")
assert_true(gs.is_gift_given("amulet"))
func test_purse_starts_empty():
assert_eq(GameState.new().purse_copper, 0)
func test_grant_currency_lands_in_the_purse_not_the_inventory():
var gs = GameState.new()
gs.grant("silver", 2)
assert_eq(gs.purse_copper, 200)
assert_eq(gs.inventory.size(), 0, "money is never an inventory row")
func test_grant_non_currency_lands_in_the_inventory():
var gs = GameState.new()
gs.grant("worn_shortsword", 1)
assert_eq(gs.inventory.get("worn_shortsword", 0), 1)
assert_eq(gs.purse_copper, 0)
func test_take_currency_spends_from_the_purse():
var gs = GameState.new()
gs.grant("gold", 1)
gs.take("copper", 5)
assert_eq(gs.purse_copper, 9995)
func test_take_currency_clamps_at_zero():
var gs = GameState.new()
gs.grant("copper", 47)
gs.take("gold", 1)
assert_eq(gs.purse_copper, 0, "the purse never goes negative")
func test_take_non_currency_removes_from_the_inventory():
var gs = GameState.new()
gs.grant("worn_shortsword", 2)
gs.take("worn_shortsword", 1)
assert_eq(gs.inventory.get("worn_shortsword", 0), 1)