From cd5b75b871902715fbd024c7bfaba5d136a0653d Mon Sep 17 00:00:00 2001 From: Natalie Date: Wed, 29 Apr 2026 15:52:36 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20remove=20unused=20audio?= =?UTF-8?q?=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../audio/sfx/fauna/herbivore_attack.ogg | Bin 12453 -> 0 bytes .../assets/audio/sfx/wonder_built.ogg | Bin 13539 -> 0 bytes public/games/age-of-dwarves/data/audio.json | 120 +++++------ .../assets => resources}/audio/LICENSES.md | 4 +- .../audio/music/defeat.ogg | Bin .../audio/music/golden_age.ogg | Bin .../audio/music/overworld_ascension.ogg | Bin .../audio/music/overworld_awakening.ogg | Bin .../audio/music/overworld_craft.ogg | Bin .../audio/music/overworld_industry.ogg | Bin .../audio/music/overworld_kingdoms.ogg | Bin .../audio/music/victory.ogg | Bin .../audio/music/victory_culture_a.ogg | Bin .../audio/music/victory_culture_b.ogg | Bin .../audio/music/victory_culture_c.ogg | Bin .../audio/music/victory_domination_a.ogg | Bin .../audio/music/victory_domination_b.ogg | Bin .../audio/music/victory_domination_c.ogg | Bin .../audio/music/victory_economic_a.ogg | Bin .../audio/music/victory_economic_b.ogg | Bin .../audio/music/victory_science_a.ogg | Bin .../audio/music/victory_science_b.ogg | Bin .../sfx/buildings/build_complete_civic.ogg | Bin .../sfx/buildings/build_complete_def.ogg | Bin .../sfx/buildings/build_complete_mil.ogg | Bin .../sfx/buildings/build_complete_prod.ogg | Bin .../audio/sfx/buildings/culture_complete.ogg | Bin .../sfx/buildings/diplomacy_complete.ogg | Bin .../audio/sfx/buildings/generic_complete.ogg | Bin .../sfx/buildings/infrastructure_complete.ogg | Bin .../audio/sfx/buildings/research_complete.ogg | Bin .../audio/sfx/buildings/wonder_built.ogg | Bin 0 -> 5576 bytes .../audio/sfx/buildings}/wonder_built_own.ogg | Bin .../sfx/buildings}/wonder_built_rival.ogg | Bin .../audio/sfx/city}/city_founded.ogg | Bin .../audio/sfx/city/city_grew.ogg | Bin .../audio/sfx/city/city_starved.ogg | Bin .../audio/sfx/combat}/combat_hit.ogg | Bin .../audio/sfx/combat/combat_started.ogg | Bin .../audio/sfx/combat}/unit_defeated.ogg | Bin .../audio/sfx/combat}/unit_killed.ogg | Bin .../audio/sfx/combat}/unit_victorious.ogg | Bin .../audio/sfx/era}/defeat_stinger.ogg | Bin .../audio/sfx/era}/era_advanced.ogg | Bin .../audio/sfx/era}/golden_age_swell.ogg | Bin .../audio/sfx/era}/victory_fanfare.ogg | Bin .../audio/sfx/fauna/apex_attack.ogg | Bin .../audio/sfx/fauna/apex_death.ogg | Bin .../audio/sfx/fauna/apex_hit.ogg | Bin .../audio/sfx/fauna/apex_roar.ogg | Bin .../audio/sfx/fauna/herbivore_attack.ogg | Bin 0 -> 11129 bytes .../audio/sfx/fauna/herbivore_call.ogg | Bin .../audio/sfx/fauna/herbivore_death.ogg | Bin .../audio/sfx/fauna/herbivore_hit.ogg | Bin .../audio/sfx/fauna/omnivore_attack.ogg | Bin .../audio/sfx/fauna/omnivore_death.ogg | Bin .../audio/sfx/fauna/omnivore_hit.ogg | Bin .../audio/sfx/fauna/omnivore_spawn.ogg | Bin .../audio/sfx/fauna/predator_attack_01.ogg | Bin .../audio/sfx/fauna/predator_attack_02.ogg | Bin .../audio/sfx/fauna/predator_death.ogg | Bin .../audio/sfx/fauna/predator_hurt_01.ogg | Bin .../audio/sfx/fauna/predator_hurt_02.ogg | Bin .../audio/sfx/fauna/predator_spawn.ogg | Bin .../audio/sfx/fauna/spawn.ogg | Bin .../audio/sfx/generic/attack.ogg | Bin .../audio/sfx/generic/complete.ogg | Bin .../audio/sfx/generic/death.ogg | Bin .../audio/sfx/generic/hit.ogg | Bin .../audio/sfx/ui}/border_expanded.ogg | Bin .../audio/sfx/ui}/culture_researched.ogg | Bin .../audio/sfx/ui}/research_start.ogg | Bin .../audio/sfx/ui}/tech_researched.ogg | Bin .../audio/sfx/ui}/turn_ended.ogg | Bin .../audio/sfx/ui}/turn_started.ogg | Bin .../audio/sfx/ui}/unit_moved.ogg | Bin .../audio/sfx/ui}/unit_promoted.ogg | Bin .../audio/sfx/units/civilian/death.ogg | Bin .../audio/sfx/units/melee/attack_01.ogg | Bin .../audio/sfx/units/melee/attack_02.ogg | Bin .../audio/sfx/units/melee/attack_03.ogg | Bin .../audio/sfx/units/melee/death_01.ogg | Bin .../audio/sfx/units/melee/death_02.ogg | Bin .../audio/sfx/units/melee/hit_01.ogg | Bin .../audio/sfx/units/melee/hit_02.ogg | Bin .../audio/sfx/units/melee/hit_03.ogg | Bin .../audio/sfx/units/melee/spawn.ogg} | Bin .../audio/sfx/units/ranged/death_01.ogg | Bin .../audio/sfx/units/ranged/fire_01.ogg | Bin .../audio/sfx/units/ranged/fire_02.ogg | Bin .../audio/sfx/units/ranged/hit_01.ogg | Bin .../audio/sfx/units/ranged/hit_02.ogg | Bin .../audio/sfx/units/ranged/spawn.ogg} | Bin .../audio/sfx/units/siege/bombard_01.ogg | Bin .../audio/sfx/units/siege/bombard_02.ogg | Bin .../audio/sfx/units/siege/death.ogg} | Bin .../audio/sfx/units/siege/hit.ogg} | Bin .../audio/sfx/units/support/attack.ogg} | Bin .../audio/sfx/units/support/death.ogg} | Bin .../audio/sfx/units/support/hit.ogg} | Bin .../audio/sfx/weather/blizzard.ogg | Bin .../audio/sfx/weather/drought.ogg | Bin .../audio/sfx/weather/heat_wave.ogg | Bin .../audio/sfx/weather/hurricane.ogg | Bin .../audio/sfx/weather/storm.ogg | Bin .../audio/sfx/weather/tornado.ogg | Bin .../assets => resources}/audio/sources.csv | 68 +++---- tools/audio-batch-11-quality-fixes-2.tsv | 9 + tools/audio-reorganize.py | 192 ++++++++++++++++++ 109 files changed, 297 insertions(+), 96 deletions(-) delete mode 100644 public/games/age-of-dwarves/assets/audio/sfx/fauna/herbivore_attack.ogg delete mode 100644 public/games/age-of-dwarves/assets/audio/sfx/wonder_built.ogg rename public/{games/age-of-dwarves/assets => resources}/audio/LICENSES.md (99%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/defeat.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/golden_age.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/overworld_ascension.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/overworld_awakening.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/overworld_craft.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/overworld_industry.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/overworld_kingdoms.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_culture_a.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_culture_b.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_culture_c.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_domination_a.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_domination_b.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_domination_c.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_economic_a.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_economic_b.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_science_a.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/music/victory_science_b.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/build_complete_civic.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/build_complete_def.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/build_complete_mil.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/build_complete_prod.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/culture_complete.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/diplomacy_complete.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/generic_complete.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/infrastructure_complete.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/buildings/research_complete.ogg (100%) create mode 100644 public/resources/audio/sfx/buildings/wonder_built.ogg rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/buildings}/wonder_built_own.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/buildings}/wonder_built_rival.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/city}/city_founded.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/city/city_grew.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/city/city_starved.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/combat}/combat_hit.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/combat/combat_started.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/combat}/unit_defeated.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/combat}/unit_killed.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/combat}/unit_victorious.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/era}/defeat_stinger.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/era}/era_advanced.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/era}/golden_age_swell.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/era}/victory_fanfare.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/apex_attack.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/apex_death.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/apex_hit.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/apex_roar.ogg (100%) create mode 100644 public/resources/audio/sfx/fauna/herbivore_attack.ogg rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/herbivore_call.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/herbivore_death.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/herbivore_hit.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/omnivore_attack.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/omnivore_death.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/omnivore_hit.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/omnivore_spawn.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/predator_attack_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/predator_attack_02.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/predator_death.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/predator_hurt_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/predator_hurt_02.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/predator_spawn.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/fauna/spawn.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/generic/attack.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/generic/complete.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/generic/death.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/generic/hit.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/ui}/border_expanded.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/ui}/culture_researched.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/ui}/research_start.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/ui}/tech_researched.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/ui}/turn_ended.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/ui}/turn_started.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/ui}/unit_moved.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx => resources/audio/sfx/ui}/unit_promoted.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/civilian/death.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/melee/attack_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/melee/attack_02.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/melee/attack_03.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/melee/death_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/melee/death_02.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/melee/hit_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/melee/hit_02.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/melee/hit_03.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx/units/melee_spawn.ogg => resources/audio/sfx/units/melee/spawn.ogg} (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/ranged/death_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/ranged/fire_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/ranged/fire_02.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/ranged/hit_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/ranged/hit_02.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx/units/ranged_spawn.ogg => resources/audio/sfx/units/ranged/spawn.ogg} (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/siege/bombard_01.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/units/siege/bombard_02.ogg (100%) rename public/{games/age-of-dwarves/assets/audio/sfx/units/siege_death.ogg => resources/audio/sfx/units/siege/death.ogg} (100%) rename public/{games/age-of-dwarves/assets/audio/sfx/units/siege_hit.ogg => resources/audio/sfx/units/siege/hit.ogg} (100%) rename public/{games/age-of-dwarves/assets/audio/sfx/units/support_attack.ogg => resources/audio/sfx/units/support/attack.ogg} (100%) rename public/{games/age-of-dwarves/assets/audio/sfx/units/support_death.ogg => resources/audio/sfx/units/support/death.ogg} (100%) rename public/{games/age-of-dwarves/assets/audio/sfx/units/support_hit.ogg => resources/audio/sfx/units/support/hit.ogg} (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/weather/blizzard.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/weather/drought.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/weather/heat_wave.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/weather/hurricane.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/weather/storm.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sfx/weather/tornado.ogg (100%) rename public/{games/age-of-dwarves/assets => resources}/audio/sources.csv (82%) create mode 100644 tools/audio-batch-11-quality-fixes-2.tsv create mode 100644 tools/audio-reorganize.py diff --git a/public/games/age-of-dwarves/assets/audio/sfx/fauna/herbivore_attack.ogg b/public/games/age-of-dwarves/assets/audio/sfx/fauna/herbivore_attack.ogg deleted file mode 100644 index 580077dbad291e762160a947013d352fa101ee99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12453 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 G1pf=h;7ZZ} diff --git a/public/games/age-of-dwarves/data/audio.json b/public/games/age-of-dwarves/data/audio.json index 7cd2ad88..6df1af7d 100644 --- a/public/games/age-of-dwarves/data/audio.json +++ b/public/games/age-of-dwarves/data/audio.json @@ -2,91 +2,91 @@ "schema_version": 2, "sfx": { "turn_started": { - "stream": "audio/sfx/turn_started.ogg", + "stream": "audio/sfx/ui/turn_started.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "UI window-maximize tone — turn-start cue." + "description": "UI window-maximize tone \u2014 turn-start cue." }, "turn_ended": { - "stream": "audio/sfx/turn_ended.ogg", + "stream": "audio/sfx/ui/turn_ended.ogg", "volume_db": -8.0, "bus": "SFX", - "description": "UI window-minimize tone — end-turn commit." + "description": "UI window-minimize tone \u2014 end-turn commit." }, "city_founded": { - "stream": "audio/sfx/city_founded.ogg", + "stream": "audio/sfx/city/city_founded.ogg", "volume_db": -4.0, "bus": "SFX", "description": "Stone-on-stone founding chime, bell tail." }, "tech_researched": { - "stream": "audio/sfx/tech_researched.ogg", + "stream": "audio/sfx/ui/tech_researched.ogg", "volume_db": -5.0, "bus": "SFX", "description": "Bright scholarly chime when a tech completes." }, "unit_killed": { - "stream": "audio/sfx/unit_killed.ogg", + "stream": "audio/sfx/combat/unit_killed.ogg", "volume_db": -6.0, "bus": "SFX", "description": "Neutral combat death thud \u2014 plays when neither side is the local human (AI vs AI, AI vs wild)." }, "unit_defeated": { - "stream": "audio/sfx/unit_defeated.ogg", + "stream": "audio/sfx/combat/unit_defeated.ogg", "volume_db": -4.0, "bus": "SFX", "description": "Somber wood thud \u2014 local human's unit died. Layered on top of the species death sound." }, "unit_victorious": { - "stream": "audio/sfx/unit_victorious.ogg", + "stream": "audio/sfx/combat/unit_victorious.ogg", "volume_db": -4.0, "bus": "SFX", "description": "Bright metallic ting \u2014 local human scored a kill. Layered on top of the species death sound." }, "wonder_built": { - "stream": "audio/sfx/wonder_built.ogg", + "stream": "audio/sfx/buildings/wonder_built.ogg", "volume_db": -3.0, "bus": "SFX", - "description": "Generic wonder-built cue \u2014 used when neither own/rival variant exists in the manifest." + "description": "Deep wood toll \u2014 generic wonder-built cue (fallback when neither own/rival variant exists)." }, "wonder_built.own": { - "stream": "audio/sfx/wonder_built_own.ogg", + "stream": "audio/sfx/buildings/wonder_built_own.ogg", "volume_db": -2.0, "bus": "SFX", "description": "Triumphant brass fanfare when YOUR wonder completes (~5s, full ensemble)." }, "wonder_built.rival": { - "stream": "audio/sfx/wonder_built_rival.ogg", + "stream": "audio/sfx/buildings/wonder_built_rival.ogg", "volume_db": -8.0, "bus": "SFX", "description": "Distant ominous bell when a rival wonder completes (short, restrained, somewhere else in the world)." }, "era_advanced": { - "stream": "audio/sfx/era_advanced.ogg", + "stream": "audio/sfx/era/era_advanced.ogg", "volume_db": -3.0, "bus": "SFX", "description": "Deep bell toll on era change." }, "combat_hit": { - "stream": "audio/sfx/combat_hit.ogg", + "stream": "audio/sfx/combat/combat_hit.ogg", "volume_db": -8.0, "bus": "SFX", "description": "Generic combat impact \u2014 endpoint of the categorical ladder for unit hit events." }, "unit_moved": { - "stream": "audio/sfx/unit_moved.ogg", + "stream": "audio/sfx/ui/unit_moved.ogg", "volume_db": -18.0, "bus": "SFX", "description": "Soft footstep tick on unit movement (throttled)." }, "defeat_stinger": { - "stream": "audio/sfx/defeat_stinger.ogg", + "stream": "audio/sfx/era/defeat_stinger.ogg", "volume_db": -3.0, "bus": "SFX", "description": "Somber descending stinger on defeat / player_eliminated." }, "victory_fanfare": { - "stream": "audio/sfx/victory_fanfare.ogg", + "stream": "audio/sfx/era/victory_fanfare.ogg", "volume_db": -2.0, "bus": "SFX", "description": "Full brass fanfare on victory_achieved." @@ -98,7 +98,7 @@ "description": "Heavy bell toll before first blow lands." }, "unit_promoted": { - "stream": "audio/sfx/unit_promoted.ogg", + "stream": "audio/sfx/ui/unit_promoted.ogg", "volume_db": -5.0, "bus": "SFX", "description": "Bright UI confirmation chime on unit promotion." @@ -113,28 +113,28 @@ "stream": "audio/sfx/city/city_starved.ogg", "volume_db": -8.0, "bus": "SFX", - "description": "UI error tone — starvation warning." + "description": "UI error tone \u2014 starvation warning." }, "golden_age_swell": { - "stream": "audio/sfx/golden_age_swell.ogg", + "stream": "audio/sfx/era/golden_age_swell.ogg", "volume_db": -3.0, "bus": "SFX", "description": "Slow brass crescendo on golden-age start." }, "border_expanded": { - "stream": "audio/sfx/border_expanded.ogg", + "stream": "audio/sfx/ui/border_expanded.ogg", "volume_db": -10.0, "bus": "SFX", "description": "Soft brass on border-tile claim." }, "research_start": { - "stream": "audio/sfx/research_start.ogg", + "stream": "audio/sfx/ui/research_start.ogg", "volume_db": -12.0, "bus": "SFX", "description": "Soft tap on research-start; deliberately quiet." }, "culture_researched": { - "stream": "audio/sfx/culture_researched.ogg", + "stream": "audio/sfx/ui/culture_researched.ogg", "volume_db": -5.0, "bus": "SFX", "description": "Bright UI confirmation chime on cultural-tradition completion." @@ -183,7 +183,7 @@ "volume_db": -6.0, "bus": "SFX", "pitch_jitter": 0.05, - "description": "Quick light release — bow approximation (no real bowstring sample available in our CC0 packs)." + "description": "Quick light release \u2014 bow approximation (no real bowstring sample available in our CC0 packs)." }, "unit.ranged.hit": { "streams": [ @@ -308,19 +308,19 @@ "stream": "audio/sfx/weather/blizzard.ogg", "volume_db": -8.0, "bus": "SFX", - "description": "Ethereal high-frequency loop — icy wind cue." + "description": "Ethereal high-frequency loop \u2014 icy wind cue." }, "weather.heat_wave": { "stream": "audio/sfx/weather/heat_wave.ogg", "volume_db": -10.0, "bus": "SFX", - "description": "Shimmering mid-frequency drone — heat-wave cue." + "description": "Shimmering mid-frequency drone \u2014 heat-wave cue." }, "weather.drought": { "stream": "audio/sfx/weather/drought.ogg", "volume_db": -12.0, "bus": "SFX", - "description": "Dry low-frequency drone — drought cue." + "description": "Dry low-frequency drone \u2014 drought cue." }, "weather.tornado": { "stream": "audio/sfx/weather/tornado.ogg", @@ -333,148 +333,148 @@ "bus": "SFX" }, "unit.melee.spawn": { - "stream": "audio/sfx/units/melee_spawn.ogg", + "stream": "audio/sfx/units/melee/spawn.ogg", "volume_db": -8.0, "bus": "SFX", - "description": "Muffled wood thump — a melee unit musters into play." + "description": "Muffled wood thump \u2014 a melee unit musters into play." }, "unit.ranged.spawn": { - "stream": "audio/sfx/units/ranged_spawn.ogg", + "stream": "audio/sfx/units/ranged/spawn.ogg", "volume_db": -8.0, "bus": "SFX", - "description": "Quick wood tap — a ranged unit takes position." + "description": "Quick wood tap \u2014 a ranged unit takes position." }, "unit.siege.hit": { - "stream": "audio/sfx/units/siege_hit.ogg", + "stream": "audio/sfx/units/siege/hit.ogg", "volume_db": -5.0, "bus": "SFX", - "description": "Strained plank creak — siege engine takes damage." + "description": "Strained plank creak \u2014 siege engine takes damage." }, "unit.siege.death": { - "stream": "audio/sfx/units/siege_death.ogg", + "stream": "audio/sfx/units/siege/death.ogg", "volume_db": -4.0, "bus": "SFX", - "description": "Heavy plate collapse — siege engine destroyed." + "description": "Heavy plate collapse \u2014 siege engine destroyed." }, "unit.support.attack": { - "stream": "audio/sfx/units/support_attack.ogg", + "stream": "audio/sfx/units/support/attack.ogg", "volume_db": -7.0, "bus": "SFX", - "description": "Soft string pluck — support unit's contributory action." + "description": "Soft string pluck \u2014 support unit's contributory action." }, "unit.support.hit": { - "stream": "audio/sfx/units/support_hit.ogg", + "stream": "audio/sfx/units/support/hit.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Soft body impact — support unit takes damage." + "description": "Soft body impact \u2014 support unit takes damage." }, "unit.support.death": { - "stream": "audio/sfx/units/support_death.ogg", + "stream": "audio/sfx/units/support/death.ogg", "volume_db": -5.0, "bus": "SFX", - "description": "Soft heavy fall — support unit killed." + "description": "Soft heavy fall \u2014 support unit killed." }, "fauna.apex_predator.hit": { "stream": "audio/sfx/fauna/apex_hit.ogg", "volume_db": -5.0, "bus": "SFX", - "description": "Pained snarl — apex predator takes damage." + "description": "Pained snarl \u2014 apex predator takes damage." }, "fauna.herbivore.attack": { "stream": "audio/sfx/fauna/herbivore_attack.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Panicked headbutt grunt — cornered herbivore charges." + "description": "Pained grunt \u2014 cornered herbivore charges." }, "fauna.herbivore.hit": { "stream": "audio/sfx/fauna/herbivore_hit.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Bleat of pain — herbivore wounded." + "description": "Bleat of pain \u2014 herbivore wounded." }, "fauna.omnivore.spawn": { "stream": "audio/sfx/fauna/omnivore_spawn.ogg", "volume_db": -8.0, "bus": "SFX", - "description": "Inquisitive chitter — omnivore enters the map." + "description": "Inquisitive chitter \u2014 omnivore enters the map." }, "fauna.omnivore.attack": { "stream": "audio/sfx/fauna/omnivore_attack.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Aggressive bark — omnivore lunges." + "description": "Aggressive bark \u2014 omnivore lunges." }, "fauna.omnivore.hit": { "stream": "audio/sfx/fauna/omnivore_hit.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Yelp — omnivore takes damage." + "description": "Yelp \u2014 omnivore takes damage." }, "fauna.omnivore.death": { "stream": "audio/sfx/fauna/omnivore_death.ogg", "volume_db": -5.0, "bus": "SFX", - "description": "Final roar trailing off — omnivore killed." + "description": "Final roar trailing off \u2014 omnivore killed." }, "building.culture.complete": { "stream": "audio/sfx/buildings/culture_complete.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Resonant wood-stop — culture building completed." + "description": "Resonant wood-stop \u2014 culture building completed." }, "building.diplomacy.complete": { "stream": "audio/sfx/buildings/diplomacy_complete.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Brass-plate ring — diplomacy building completed." + "description": "Brass-plate ring \u2014 diplomacy building completed." }, "building.infrastructure.complete": { "stream": "audio/sfx/buildings/infrastructure_complete.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Stone settle — infrastructure building completed." + "description": "Stone settle \u2014 infrastructure building completed." }, "building.research.complete": { "stream": "audio/sfx/buildings/research_complete.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Light scholarly tap — research building completed." + "description": "Light scholarly tap \u2014 research building completed." }, "building.complete": { "stream": "audio/sfx/buildings/generic_complete.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Stone-on-plate impact — kind-only fallback for any building category." + "description": "Stone-on-plate impact \u2014 kind-only fallback for any building category." }, "complete": { "stream": "audio/sfx/generic/complete.ogg", "volume_db": -7.0, "bus": "SFX", - "description": "Plate impact — bare-kind fallback for any 'complete' event." + "description": "Plate impact \u2014 bare-kind fallback for any 'complete' event." }, "attack": { "stream": "audio/sfx/generic/attack.ogg", "volume_db": -7.0, "bus": "SFX", - "description": "Generic attack swing — last-resort fallback for unclassified entities." + "description": "Generic attack swing \u2014 last-resort fallback for unclassified entities." }, "hit": { "stream": "audio/sfx/generic/hit.ogg", "volume_db": -7.0, "bus": "SFX", - "description": "Generic impact — last-resort fallback for unclassified entities." + "description": "Generic impact \u2014 last-resort fallback for unclassified entities." }, "death": { "stream": "audio/sfx/generic/death.ogg", "volume_db": -6.0, "bus": "SFX", - "description": "Generic fall thud — last-resort fallback for unclassified entities." + "description": "Generic fall thud \u2014 last-resort fallback for unclassified entities." }, "spawn": { "stream": "audio/sfx/fauna/spawn.ogg", "volume_db": -9.0, "bus": "SFX", - "description": "Brush rustle — bare-kind fallback (aliases wild_spawn texture for unclassified spawn events)." + "description": "Brush rustle \u2014 bare-kind fallback (aliases wild_spawn texture for unclassified spawn events)." } }, "music": { @@ -701,4 +701,4 @@ ] } } -} \ No newline at end of file +} diff --git a/public/games/age-of-dwarves/assets/audio/LICENSES.md b/public/resources/audio/LICENSES.md similarity index 99% rename from public/games/age-of-dwarves/assets/audio/LICENSES.md rename to public/resources/audio/LICENSES.md index 37c25bd4..572e6add 100644 --- a/public/games/age-of-dwarves/assets/audio/LICENSES.md +++ b/public/resources/audio/LICENSES.md @@ -50,7 +50,7 @@ Each row records one `.ogg` shipped under `public/games/age-of-dwarves/assets/au | `audio/sfx/fauna/apex_death.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/80-CC0-creature-SFX_0.zip#scream_02.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-27 | | `audio/sfx/fauna/apex_hit.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/80-CC0-creature-SFX_0.zip#hurt_05.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-29 | | `audio/sfx/fauna/apex_roar.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/80-CC0-creature-SFX_0.zip#roar_02.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-27 | -| `audio/sfx/fauna/herbivore_attack.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/80-CC0-creature-SFX_0.zip#monster_02.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-29 | +| `audio/sfx/fauna/herbivore_attack.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/80-CC0-creature-SFX_0.zip#grunt_01.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-29 | | `audio/sfx/fauna/herbivore_call.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/80-CC0-creature-SFX_0.zip#cute_05.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-27 | | `audio/sfx/fauna/herbivore_death.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/80-CC0-creature-SFX_0.zip#hurt_04.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-27 | | `audio/sfx/fauna/herbivore_hit.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/80-CC0-creature-SFX_0.zip#hurt_03.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-29 | @@ -109,7 +109,7 @@ Each row records one `.ogg` shipped under `public/games/age-of-dwarves/assets/au | `audio/sfx/weather/hurricane.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/sfx_loops.zip#ambient_02.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-27 | | `audio/sfx/weather/storm.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/sfx_100_v2.zip#sfx100v2_thunder_01.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-28 | | `audio/sfx/weather/tornado.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/sfx_loops.zip#ambient_03.ogg) | rubberduck (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-27 | -| `audio/sfx/wonder_built.ogg` | CC0-1.0 | [link](https://kenney.nl/media/pages/assets/impact-sounds/8aa7b545c9-1677589768/kenney_impact-sounds.zip#Audio/impactBell_heavy_001.ogg) | Kenney (Impact Sounds) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-27 | +| `audio/sfx/wonder_built.ogg` | CC0-1.0 | [link](https://kenney.nl/media/pages/assets/impact-sounds/8aa7b545c9-1677589768/kenney_impact-sounds.zip#Audio/impactWood_heavy_000.ogg) | Kenney (Impact Sounds) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-29 | | `audio/sfx/wonder_built_own.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/fanfare_0.ogg) | Spring Spring (OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | 2026-04-28 | | `audio/sfx/wonder_built_rival.ogg` | CC0-1.0 | [link](https://kenney.nl/media/pages/assets/impact-sounds/8aa7b545c9-1677589768/kenney_impact-sounds.zip#Audio/impactBell_heavy_002.ogg) | Kenney (Impact Sounds) | loudnorm I=-22/TP=-6+ogg 128kbps (extra-quiet for distant feel) | 2026-04-28 | | `c/victory.ogg` | CC0-1.0 | [link](https://opengameart.org/sites/default/files/JRPG%20Music%20Pack%20%235%20%5BAction%5D%20by%20Juhani%20Junkala.zip#Action3 - Preparing For Battle.ogg) | Juhani Junkala (SubspaceAudio | OpenGameArt) | loudnorm I=-16/TP=-3+ogg 128kbps | diff --git a/public/games/age-of-dwarves/assets/audio/music/defeat.ogg b/public/resources/audio/music/defeat.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/defeat.ogg rename to public/resources/audio/music/defeat.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/golden_age.ogg b/public/resources/audio/music/golden_age.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/golden_age.ogg rename to public/resources/audio/music/golden_age.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/overworld_ascension.ogg b/public/resources/audio/music/overworld_ascension.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/overworld_ascension.ogg rename to public/resources/audio/music/overworld_ascension.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/overworld_awakening.ogg b/public/resources/audio/music/overworld_awakening.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/overworld_awakening.ogg rename to public/resources/audio/music/overworld_awakening.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/overworld_craft.ogg b/public/resources/audio/music/overworld_craft.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/overworld_craft.ogg rename to public/resources/audio/music/overworld_craft.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/overworld_industry.ogg b/public/resources/audio/music/overworld_industry.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/overworld_industry.ogg rename to public/resources/audio/music/overworld_industry.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/overworld_kingdoms.ogg b/public/resources/audio/music/overworld_kingdoms.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/overworld_kingdoms.ogg rename to public/resources/audio/music/overworld_kingdoms.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory.ogg b/public/resources/audio/music/victory.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory.ogg rename to public/resources/audio/music/victory.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_culture_a.ogg b/public/resources/audio/music/victory_culture_a.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_culture_a.ogg rename to public/resources/audio/music/victory_culture_a.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_culture_b.ogg b/public/resources/audio/music/victory_culture_b.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_culture_b.ogg rename to public/resources/audio/music/victory_culture_b.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_culture_c.ogg b/public/resources/audio/music/victory_culture_c.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_culture_c.ogg rename to public/resources/audio/music/victory_culture_c.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_domination_a.ogg b/public/resources/audio/music/victory_domination_a.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_domination_a.ogg rename to public/resources/audio/music/victory_domination_a.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_domination_b.ogg b/public/resources/audio/music/victory_domination_b.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_domination_b.ogg rename to public/resources/audio/music/victory_domination_b.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_domination_c.ogg b/public/resources/audio/music/victory_domination_c.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_domination_c.ogg rename to public/resources/audio/music/victory_domination_c.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_economic_a.ogg b/public/resources/audio/music/victory_economic_a.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_economic_a.ogg rename to public/resources/audio/music/victory_economic_a.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_economic_b.ogg b/public/resources/audio/music/victory_economic_b.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_economic_b.ogg rename to public/resources/audio/music/victory_economic_b.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_science_a.ogg b/public/resources/audio/music/victory_science_a.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_science_a.ogg rename to public/resources/audio/music/victory_science_a.ogg diff --git a/public/games/age-of-dwarves/assets/audio/music/victory_science_b.ogg b/public/resources/audio/music/victory_science_b.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/music/victory_science_b.ogg rename to public/resources/audio/music/victory_science_b.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_civic.ogg b/public/resources/audio/sfx/buildings/build_complete_civic.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_civic.ogg rename to public/resources/audio/sfx/buildings/build_complete_civic.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_def.ogg b/public/resources/audio/sfx/buildings/build_complete_def.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_def.ogg rename to public/resources/audio/sfx/buildings/build_complete_def.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_mil.ogg b/public/resources/audio/sfx/buildings/build_complete_mil.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_mil.ogg rename to public/resources/audio/sfx/buildings/build_complete_mil.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_prod.ogg b/public/resources/audio/sfx/buildings/build_complete_prod.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/build_complete_prod.ogg rename to public/resources/audio/sfx/buildings/build_complete_prod.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/culture_complete.ogg b/public/resources/audio/sfx/buildings/culture_complete.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/culture_complete.ogg rename to public/resources/audio/sfx/buildings/culture_complete.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/diplomacy_complete.ogg b/public/resources/audio/sfx/buildings/diplomacy_complete.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/diplomacy_complete.ogg rename to public/resources/audio/sfx/buildings/diplomacy_complete.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/generic_complete.ogg b/public/resources/audio/sfx/buildings/generic_complete.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/generic_complete.ogg rename to public/resources/audio/sfx/buildings/generic_complete.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/infrastructure_complete.ogg b/public/resources/audio/sfx/buildings/infrastructure_complete.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/infrastructure_complete.ogg rename to public/resources/audio/sfx/buildings/infrastructure_complete.ogg diff --git a/public/games/age-of-dwarves/assets/audio/sfx/buildings/research_complete.ogg b/public/resources/audio/sfx/buildings/research_complete.ogg similarity index 100% rename from public/games/age-of-dwarves/assets/audio/sfx/buildings/research_complete.ogg rename to public/resources/audio/sfx/buildings/research_complete.ogg diff --git a/public/resources/audio/sfx/buildings/wonder_built.ogg b/public/resources/audio/sfx/buildings/wonder_built.ogg new file mode 100644 index 0000000000000000000000000000000000000000..513aa065347a3ac9caa78283988f475b4eb7ae80 GIT binary patch literal 5576 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/.ogg + public/resources/audio/music/.ogg + public/resources/audio/sources.csv + public/resources/audio/LICENSES.md + +Categories: ui, city, combat, era, buildings, units/, fauna, +weather, generic. +""" + +import json, csv, io, shutil, sys, re +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +OLD_ROOT = REPO_ROOT / "public/games/age-of-dwarves/assets/audio" +NEW_ROOT = REPO_ROOT / "public/resources/audio" +MANIFEST = REPO_ROOT / "public/games/age-of-dwarves/data/audio.json" + +# Map of old asset-relative path -> new asset-relative path. The asset-relative +# part is what shows up as `audio/...` in the manifest streams + sources.csv. +RENAMES: dict[str, str] = { + # ── flat sfx top-level → categorical subfolders ───────────────────── + "audio/sfx/turn_started.ogg": "audio/sfx/ui/turn_started.ogg", + "audio/sfx/turn_ended.ogg": "audio/sfx/ui/turn_ended.ogg", + "audio/sfx/unit_moved.ogg": "audio/sfx/ui/unit_moved.ogg", + "audio/sfx/unit_promoted.ogg": "audio/sfx/ui/unit_promoted.ogg", + "audio/sfx/border_expanded.ogg": "audio/sfx/ui/border_expanded.ogg", + "audio/sfx/research_start.ogg": "audio/sfx/ui/research_start.ogg", + "audio/sfx/tech_researched.ogg": "audio/sfx/ui/tech_researched.ogg", + "audio/sfx/culture_researched.ogg": "audio/sfx/ui/culture_researched.ogg", + + "audio/sfx/city_founded.ogg": "audio/sfx/city/city_founded.ogg", + + "audio/sfx/combat_hit.ogg": "audio/sfx/combat/combat_hit.ogg", + "audio/sfx/unit_killed.ogg": "audio/sfx/combat/unit_killed.ogg", + "audio/sfx/unit_defeated.ogg": "audio/sfx/combat/unit_defeated.ogg", + "audio/sfx/unit_victorious.ogg": "audio/sfx/combat/unit_victorious.ogg", + + "audio/sfx/era_advanced.ogg": "audio/sfx/era/era_advanced.ogg", + "audio/sfx/golden_age_swell.ogg": "audio/sfx/era/golden_age_swell.ogg", + "audio/sfx/victory_fanfare.ogg": "audio/sfx/era/victory_fanfare.ogg", + "audio/sfx/defeat_stinger.ogg": "audio/sfx/era/defeat_stinger.ogg", + + "audio/sfx/wonder_built.ogg": "audio/sfx/buildings/wonder_built.ogg", + "audio/sfx/wonder_built_own.ogg": "audio/sfx/buildings/wonder_built_own.ogg", + "audio/sfx/wonder_built_rival.ogg": "audio/sfx/buildings/wonder_built_rival.ogg", + + # ── normalize unit subcategory paths (flat-but-named → nested) ────── + "audio/sfx/units/melee_spawn.ogg": "audio/sfx/units/melee/spawn.ogg", + "audio/sfx/units/ranged_spawn.ogg": "audio/sfx/units/ranged/spawn.ogg", + "audio/sfx/units/siege_hit.ogg": "audio/sfx/units/siege/hit.ogg", + "audio/sfx/units/siege_death.ogg": "audio/sfx/units/siege/death.ogg", + "audio/sfx/units/support_attack.ogg":"audio/sfx/units/support/attack.ogg", + "audio/sfx/units/support_hit.ogg": "audio/sfx/units/support/hit.ogg", + "audio/sfx/units/support_death.ogg": "audio/sfx/units/support/death.ogg", +} + +def step_move_files() -> tuple[int, int]: + """Move every audio file from OLD_ROOT to NEW_ROOT, applying RENAMES.""" + moved = 0 + skipped = 0 + for src in OLD_ROOT.rglob("*"): + if not src.is_file(): + continue + rel = src.relative_to(OLD_ROOT) + # The manifest path is "audio/..." — equivalent to the relative path + # under OLD_ROOT prefixed with "audio/". So a file at + # OLD_ROOT/sfx/turn_started.ogg matches "audio/sfx/turn_started.ogg". + manifest_key = f"audio/{rel.as_posix()}" + new_manifest_key = RENAMES.get(manifest_key, manifest_key) + # Special-case: LICENSES.md and sources.csv at OLD_ROOT root keep + # their basename, no "audio/" prefix. + if rel.parent == Path("."): + new_path = NEW_ROOT / rel.name + else: + assert new_manifest_key.startswith("audio/") + new_path = NEW_ROOT / new_manifest_key[len("audio/"):] + new_path.parent.mkdir(parents=True, exist_ok=True) + if new_path.exists(): + skipped += 1 + continue + shutil.move(str(src), str(new_path)) + moved += 1 + return moved, skipped + + +def step_update_manifest() -> int: + """Rewrite audio.json stream paths.""" + with open(MANIFEST) as f: + data = json.load(f) + rewrites = 0 + for key, entry in data.get("sfx", {}).items(): + if "stream" in entry and entry["stream"] in RENAMES: + entry["stream"] = RENAMES[entry["stream"]] + rewrites += 1 + if "streams" in entry: + new_list = [] + for s in entry["streams"]: + if s in RENAMES: + new_list.append(RENAMES[s]) + rewrites += 1 + else: + new_list.append(s) + entry["streams"] = new_list + # Music tracks didn't move (they were never in the flat-confusion zone), + # but defensive. + for track in data.get("music", {}).get("tracks", []): + if "stream" in track and track["stream"] in RENAMES: + track["stream"] = RENAMES[track["stream"]] + rewrites += 1 + with open(MANIFEST, "w") as f: + json.dump(data, f, indent=2) + f.write("\n") + return rewrites + + +def step_update_sources_csv() -> int: + """Rewrite output_path column in sources.csv.""" + csv_path = NEW_ROOT / "sources.csv" + if not csv_path.exists(): + return 0 + head_lines: list[str] = [] + body_lines: list[str] = [] + with open(csv_path) as f: + for ln in f: + if ln.startswith("#"): + head_lines.append(ln) + else: + body_lines.append(ln) + reader = csv.reader(io.StringIO("".join(body_lines))) + rows = list(reader) + hdr, body = rows[0], rows[1:] + op_idx = hdr.index("output_path") + rewrites = 0 + for r in body: + if r[op_idx] in RENAMES: + r[op_idx] = RENAMES[r[op_idx]] + rewrites += 1 + with open(csv_path, "w") as f: + f.writelines(head_lines) + w = csv.writer(f) + w.writerow(hdr) + w.writerows(body) + return rewrites + + +def step_remove_old_root() -> None: + """Delete OLD_ROOT if empty after the move.""" + if not OLD_ROOT.exists(): + return + leftover = list(OLD_ROOT.rglob("*")) + leftover_files = [p for p in leftover if p.is_file()] + if leftover_files: + print(f" WARN: {len(leftover_files)} files remain at {OLD_ROOT}") + for p in leftover_files[:5]: + print(f" {p}") + return + shutil.rmtree(OLD_ROOT) + # Walk up and prune empty parent dirs (assets/) but never above public/games/age-of-dwarves + parent = OLD_ROOT.parent # .../assets + if parent.is_dir() and not any(parent.iterdir()): + parent.rmdir() + + +def main() -> None: + NEW_ROOT.mkdir(parents=True, exist_ok=True) + moved, skipped = step_move_files() + print(f"moved {moved} files, skipped {skipped} (already at destination)") + rewrites = step_update_manifest() + print(f"manifest rewrites: {rewrites}") + csv_rewrites = step_update_sources_csv() + print(f"sources.csv rewrites: {csv_rewrites}") + step_remove_old_root() + print("done.") + + +if __name__ == "__main__": + main()