Created
March 16, 2026 20:05
-
-
Save manuq/c9870071e23afd2ef8e77f463949389f to your computer and use it in GitHub Desktop.
add_player_mode_to_scenes.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """Add a PlayerMode node as first child of root to story quest scenes. | |
| For each scene in scenes/quests/story_quests/ that has a Player instance | |
| (uid iu2q66clupc6), this script: | |
| - Adds an ext_resource for PlayerMode.gd | |
| - Inserts a PlayerMode node as first child of the root | |
| - Sets the mode based on the Player node's existing mode property | |
| (0=COZY default, 1=FIGHTING, 2=HOOKING) | |
| """ | |
| import re | |
| import random | |
| import os | |
| PLAYER_MODE_SCRIPT_UID = "uid://pk3ucq7e2eah" | |
| PLAYER_MODE_SCRIPT_PATH = "res://scenes/game_logic/PlayerMode.gd" | |
| # All 32 scenes grouped by mode | |
| fighting_scenes = [ | |
| "scenes/quests/story_quests/after_the_tremor/3_combat/after_the_tremor_combat.tscn", | |
| "scenes/quests/story_quests/champ/1_combat/champ_combat.tscn", | |
| "scenes/quests/story_quests/el_juguete_perdido/2_combat/combat.tscn", | |
| "scenes/quests/story_quests/el_ojo_revelador/2_combat/el_ojo_revelador_combat.tscn", | |
| "scenes/quests/story_quests/renya_beyond_sorrow/1_combat/renya_combat_round_1.tscn", | |
| "scenes/quests/story_quests/renya_beyond_sorrow/1_combat/renya_combat_round_2.tscn", | |
| "scenes/quests/story_quests/renya_beyond_sorrow/1_combat/renya_combat_round_3.tscn", | |
| "scenes/quests/story_quests/stella/2_stella_combat/stella_combat.tscn", | |
| "scenes/quests/story_quests/wizzy_quest/2_combat/wizzy_quest_combat.tscn", | |
| ] | |
| hooking_scenes = [ | |
| "scenes/quests/story_quests/after_the_tremor/2_colegiojuego/after_the_tremor_colegiojuego.tscn", | |
| ] | |
| cozy_scenes = [ | |
| "scenes/quests/story_quests/after_the_tremor/2_sequence_puzzle/2_tallerjuego/minijuego2.tscn", | |
| "scenes/quests/story_quests/champ/2_sequence_puzzle/champ_sequence_puzzle.tscn", | |
| "scenes/quests/story_quests/el_abrigo/1_el_abrigo_sequence_puzzle/el_abrigo_sequence_puzzle.tscn", | |
| "scenes/quests/story_quests/el_abrigo/2_el_abrigo_stealth/el_abrigo_stealth1.tscn", | |
| "scenes/quests/story_quests/el_abrigo/3_el_abrigo_runner/el_abrigo_runner.tscn", | |
| "scenes/quests/story_quests/el_juguete_perdido/1_stealth/stealth.tscn", | |
| "scenes/quests/story_quests/el_juguete_perdido/3_sequence_puzzle/sequence_puzzle.tscn", | |
| "scenes/quests/story_quests/el_ojo_revelador/1_stealth/el_ojo_revelador_stealth.tscn", | |
| "scenes/quests/story_quests/renya_beyond_sorrow/2_sequence_puzzle/renya_sequence_puzzle.tscn", | |
| "scenes/quests/story_quests/renya_beyond_sorrow/3_stealth/renya_beyond_sorrow_stealth.tscn", | |
| "scenes/quests/story_quests/shjourney/3_shjourney_intro/template_intro0.2.tscn", | |
| "scenes/quests/story_quests/shjourney/4_Laberinto/Laberinto.tscn", | |
| "scenes/quests/story_quests/shjourney/5_shjourney_sequence_puzzle/shjourney_sequence_puzzle.tscn", | |
| "scenes/quests/story_quests/shjourney/7_shjourney_combate/Combate.tscn", | |
| "scenes/quests/story_quests/shjourney/8_shjourney_outro/Outro.tscn", | |
| "scenes/quests/story_quests/stella/1_stella_stealth/stella_stealth.tscn", | |
| "scenes/quests/story_quests/stella/3_stella_sequence_puzzle/stella_sequence_puzzle.tscn", | |
| "scenes/quests/story_quests/verso/1_verso_combat_anger/verso_combat.tscn", | |
| "scenes/quests/story_quests/verso/2_verso_stealth_sadness/verso_stealth.tscn", | |
| "scenes/quests/story_quests/verso/3_verso_sequence_puzzle_happiness/verso_sequence_puzzle.tscn", | |
| "scenes/quests/story_quests/wizzy_quest/1_stealth/wizzy_quest_stealth.tscn", | |
| "scenes/quests/story_quests/wizzy_quest/3_sequence_puzzle/wizzy_quest_puzzle_2.tscn", | |
| ] | |
| def get_mode(filepath): | |
| if filepath in fighting_scenes: | |
| return 1 | |
| elif filepath in hooking_scenes: | |
| return 2 | |
| else: | |
| return 0 | |
| def gen_unique_id(): | |
| return random.randint(100000000, 2147483647) | |
| def gen_ext_id(existing_ids): | |
| """Generate a resource ID like '1_pmXXX' that doesn't conflict.""" | |
| while True: | |
| suffix = "".join( | |
| random.choices("abcdefghijklmnopqrstuvwxyz0123456789", k=5) | |
| ) | |
| candidate = f"1_pm{suffix[:3]}" | |
| if candidate not in existing_ids: | |
| return candidate | |
| def process_scene(filepath): | |
| with open(filepath, "r") as f: | |
| content = f.read() | |
| lines = content.split("\n") | |
| # Collect existing ext_resource IDs | |
| existing_ids = set() | |
| for line in lines: | |
| m = re.search(r'id="([^"]+)"', line) | |
| if m: | |
| existing_ids.add(m.group(1)) | |
| ext_id = gen_ext_id(existing_ids) | |
| unique_id = gen_unique_id() | |
| mode = get_mode(filepath) | |
| # Find last ext_resource line | |
| last_ext_idx = -1 | |
| for i, line in enumerate(lines): | |
| if line.startswith("[ext_resource"): | |
| last_ext_idx = i | |
| if last_ext_idx == -1: | |
| print(f"ERROR: No ext_resource found in {filepath}") | |
| return | |
| # Insert PlayerMode ext_resource after last ext_resource | |
| ext_line = ( | |
| f'[ext_resource type="Script" uid="{PLAYER_MODE_SCRIPT_UID}"' | |
| f' path="{PLAYER_MODE_SCRIPT_PATH}" id="{ext_id}"]' | |
| ) | |
| lines.insert(last_ext_idx + 1, ext_line) | |
| # Find root node (first [node without parent=) | |
| root_idx = -1 | |
| for i, line in enumerate(lines): | |
| if line.startswith("[node ") and "parent=" not in line: | |
| root_idx = i | |
| break | |
| if root_idx == -1: | |
| print(f"ERROR: No root node found in {filepath}") | |
| return | |
| # Find end of root node properties (next line starting with '[' or | |
| # empty line before a '[') | |
| insert_idx = root_idx + 1 | |
| while insert_idx < len(lines): | |
| if lines[insert_idx].startswith("[") or ( | |
| lines[insert_idx] == "" | |
| and insert_idx + 1 < len(lines) | |
| and lines[insert_idx + 1].startswith("[") | |
| ): | |
| break | |
| insert_idx += 1 | |
| # Build PlayerMode node block | |
| pm_lines = [ | |
| "", | |
| f'[node name="PlayerMode" type="Node" parent="."' | |
| f" unique_id={unique_id}]", | |
| f'script = ExtResource("{ext_id}")', | |
| ] | |
| if mode != 0: | |
| pm_lines.append(f"mode = {mode}") | |
| pm_lines.append(f'metadata/_custom_type_script = "{PLAYER_MODE_SCRIPT_UID}"') | |
| # Insert | |
| for j, pm_line in enumerate(pm_lines): | |
| lines.insert(insert_idx + j, pm_line) | |
| with open(filepath, "w") as f: | |
| f.write("\n".join(lines)) | |
| mode_name = {0: "COZY", 1: "FIGHTING", 2: "HOOKING"}[mode] | |
| print(f"OK {mode_name:8s} {filepath}") | |
| if __name__ == "__main__": | |
| all_scenes = cozy_scenes + fighting_scenes + hooking_scenes | |
| random.seed(42) # Reproducible unique IDs | |
| for scene in sorted(all_scenes): | |
| process_scene(scene) | |
| print(f"\nProcessed {len(all_scenes)} scenes") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment