feat(content): calling + race blurbs, loaded by ContentDB
Hand-written like items/ — no build-tool namespace needed. Blurbs are content; mechanics are code, and a test asserts the blurb files carry no hit dice. The prose is placeholder; swapping it later touches no code, which is what stops the content BOM from blocking the engine. 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:
@@ -8,6 +8,8 @@ var locations: Dictionary = {}
|
||||
var npcs: Dictionary = {}
|
||||
var quests: Dictionary = {}
|
||||
var items: Dictionary = {}
|
||||
var callings: Dictionary = {}
|
||||
var races: Dictionary = {}
|
||||
var canon_entities: Dictionary = {}
|
||||
var topics: Dictionary = {}
|
||||
|
||||
@@ -31,6 +33,8 @@ func load_from(content_root: String) -> void:
|
||||
npcs = _load_dir(world.path_join("npcs"))
|
||||
quests = _load_dir(world.path_join("quests"))
|
||||
items = _load_dir(world.path_join("items"))
|
||||
callings = _load_dir(world.path_join("callings"))
|
||||
races = _load_dir(world.path_join("races"))
|
||||
canon_entities = _load_dir(world.path_join("canon"))
|
||||
topics = _load_dir(world.path_join("topics"))
|
||||
|
||||
@@ -61,6 +65,10 @@ func has_location(id: String) -> bool: return locations.has(id)
|
||||
func has_npc(id: String) -> bool: return npcs.has(id)
|
||||
func has_quest(id: String) -> bool: return quests.has(id)
|
||||
func has_item(id: String) -> bool: return items.has(id)
|
||||
func calling(id: String) -> Dictionary: return callings.get(id, {})
|
||||
func race(id: String) -> Dictionary: return races.get(id, {})
|
||||
func has_calling(id: String) -> bool: return callings.has(id)
|
||||
func has_race(id: String) -> bool: return races.has(id)
|
||||
func canon(id: String) -> Dictionary: return canon_entities.get(id, {})
|
||||
func topic(id: String) -> Dictionary: return topics.get(id, {})
|
||||
func has_canon(id: String) -> bool: return canon_entities.has(id)
|
||||
|
||||
@@ -91,3 +91,37 @@ func test_no_loaded_topic_has_a_body():
|
||||
|
||||
func test_legacy_npcs_still_load():
|
||||
assert_true(db.has_npc("fenn"))
|
||||
|
||||
|
||||
func test_loads_calling_and_race_content():
|
||||
assert_true(db.has_calling("reaver"))
|
||||
assert_true(db.has_calling("bloodsworn"))
|
||||
assert_true(db.has_race("beastfolk"))
|
||||
assert_eq(db.calling("reaver")["name"], "Reaver")
|
||||
assert_eq(db.race("beastfolk")["name"], "Beastfolk")
|
||||
|
||||
|
||||
func test_calling_content_matches_the_code_table():
|
||||
# The roster exists in code (Callings.IDS — mechanics are rules) and in content
|
||||
# (the blurbs). Nothing at runtime reconciles them, so guard against drift.
|
||||
var content_ids: Array = db.callings.keys()
|
||||
content_ids.sort()
|
||||
var code_ids: Array = Callings.IDS.duplicate()
|
||||
code_ids.sort()
|
||||
assert_eq(content_ids, code_ids)
|
||||
|
||||
|
||||
func test_race_content_matches_the_code_table():
|
||||
var content_ids: Array = db.races.keys()
|
||||
content_ids.sort()
|
||||
var code_ids: Array = Races.IDS.duplicate()
|
||||
code_ids.sort()
|
||||
assert_eq(content_ids, code_ids)
|
||||
|
||||
|
||||
func test_blurb_content_carries_no_mechanics():
|
||||
# Mechanics are code. A hit_die in a content file is a second source of truth.
|
||||
for id in db.callings:
|
||||
for banned in ["hit_die", "saves", "skill_pool", "skill_count", "armor", "talent"]:
|
||||
assert_false(db.callings[id].has(banned),
|
||||
"%s.json leaks the mechanic '%s' — that lives in Callings" % [id, banned])
|
||||
|
||||
Reference in New Issue
Block a user