Skip to content

Instantly share code, notes, and snippets.

@manuq
Created March 16, 2026 20:05
Show Gist options
  • Select an option

  • Save manuq/c9870071e23afd2ef8e77f463949389f to your computer and use it in GitHub Desktop.

Select an option

Save manuq/c9870071e23afd2ef8e77f463949389f to your computer and use it in GitHub Desktop.
add_player_mode_to_scenes.py
"""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