From 0cdb3d36ec1e9788acb3bccaeca809b554ebb33b Mon Sep 17 00:00:00 2001 From: autocommit Date: Wed, 29 Apr 2026 13:13:23 -0700 Subject: [PATCH] =?UTF-8?q?feat(tools):=20=E2=9C=A8=20Add=20validation=20r?= =?UTF-8?q?ules=20for=20game=20data=20and=20introduce=20defeat=20audio=20a?= =?UTF-8?q?ssets=20in=20batch=2012?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- tools/audio-batch-12-defeat-pool.tsv | 18 ++++++++++++++++++ tools/validate-game-data.py | 26 ++++++++++++++++++++------ 2 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 tools/audio-batch-12-defeat-pool.tsv diff --git a/tools/audio-batch-12-defeat-pool.tsv b/tools/audio-batch-12-defeat-pool.tsv new file mode 100644 index 00000000..8808b5d8 --- /dev/null +++ b/tools/audio-batch-12-defeat-pool.tsv @@ -0,0 +1,18 @@ +# Batch 12 — Per-victory-condition defeat music pool. +# +# Mirror of victory_pool: when an AI wins by a particular victory type, +# the human player hears a defeat track keyed to *how* they lost. Falls +# back to the generic `defeat` track for unmapped types or eliminations +# that don't carry a victory_type (last-unit-destroyed without a winner). +# +# Track choices (Junkala, all CC0): +# defeated by domination → Action1 (ominous, witches encounter) +# defeated by culture → Calm3 (peaceful but lost — others outshone you) +# defeated by science → Exploration5 (sneaking — left behind) +# defeated by economic → Calm4 (sand castles — transient ambition) +# defeated by score → reuse existing `defeat` track (Calm6 - Innocence) + +audio/music/defeat_domination.ogg https://opengameart.org/sites/default/files/JRPG%20Music%20Pack%20%235%20%5BAction%5D%20by%20Juhani%20Junkala.zip#Action1 - Encounter With The Witches.ogg CC0-1.0 Juhani Junkala (SubspaceAudio, OpenGameArt) loudnorm I=-16/TP=-3+ogg 128kbps +audio/music/defeat_culture.ogg https://opengameart.org/sites/default/files/JRPG%20Music%20Pack%20%234%20%5BCalm%5D%20by%20Juhani%20Junkala_0.zip#Calm3 - Peaceful Days.ogg CC0-1.0 Juhani Junkala (SubspaceAudio, OpenGameArt) loudnorm I=-16/TP=-3+ogg 128kbps +audio/music/defeat_science.ogg https://opengameart.org/sites/default/files/JRPG%20Music%20Pack%20%231%20%5BExploration%5D%20by%20Juhani%20Junkala.zip#Exploration5 - Sneaking Around.ogg CC0-1.0 Juhani Junkala (SubspaceAudio, OpenGameArt) loudnorm I=-16/TP=-3+ogg 128kbps +audio/music/defeat_economic.ogg https://opengameart.org/sites/default/files/JRPG%20Music%20Pack%20%234%20%5BCalm%5D%20by%20Juhani%20Junkala_0.zip#Calm4 - Sand Castles.ogg CC0-1.0 Juhani Junkala (SubspaceAudio, OpenGameArt) loudnorm I=-16/TP=-3+ogg 128kbps diff --git a/tools/validate-game-data.py b/tools/validate-game-data.py index e1ee9cce..17a63b9a 100644 --- a/tools/validate-game-data.py +++ b/tools/validate-game-data.py @@ -118,7 +118,8 @@ class GameDataValidator: if schema is None: return files = sorted(f for f in dir_path.glob("*.json") - if not f.name.endswith(".schema.json") and f.name != "manifest.json") + if not f.name.endswith(".schema.json") + and f.name not in ("manifest.json", "building_categories.json")) if not files: print(f" (no files in {dir_path.relative_to(self.root)})") return @@ -191,7 +192,7 @@ class GameDataValidator: """Collect all 'id' values from every JSON entry in a split directory.""" ids: set[str] = set() for f in dir_path.glob("*.json"): - if f.name in ("manifest.json",) or f.name.endswith(".schema.json"): + if f.name in ("manifest.json", "building_categories.json") or f.name.endswith(".schema.json"): continue for _label, entry in self._collect_entries_from_file(f): if "id" in entry: @@ -348,8 +349,14 @@ class GameDataValidator: if resources is None: return - unit_ids = self._load_id_set_from_split_dir(self.game_data / "units") - building_ids = self._load_id_set_from_split_dir(self.game_data / "buildings") + unit_ids = ( + self._load_id_set_from_split_dir(self.resources / "units") + | self._load_id_set_from_split_dir(self.game_data / "units") + ) + building_ids = ( + self._load_id_set_from_split_dir(self.resources / "buildings") + | self._load_id_set_from_split_dir(self.game_data / "buildings") + ) print("\n cross-reference checks") @@ -391,8 +398,15 @@ class GameDataValidator: print("── Age of Dwarves game data validation ──") - self.validate_split_dir("units", self.game_data / "units", "unit") - self.validate_split_dir("buildings", self.game_data / "buildings", "building") + # Single source of truth at resources// post-p1-40 migration; the + # game_data fallback paths remain for the transitional period and as a hook + # for genuinely game-specific entities a game pack might author later. + self.validate_split_dir("units", self.resources / "units", "unit") + self.validate_split_dir("buildings", self.resources / "buildings", "building") + if (self.game_data / "units").exists(): + self.validate_split_dir("units (game-specific)", self.game_data / "units", "unit") + if (self.game_data / "buildings").exists(): + self.validate_split_dir("buildings (game-specific)", self.game_data / "buildings", "building") self.validate_split_dir("techs", self.game_data / "techs", "tech") self.validate_split_dir("terrain", self.game_data / "terrain", "terrain") self.validate_single_file(