NEVER Do (Expert Anti-Patterns)
World Design & Exploration
- NEVER allow "Soft-Locks" where a player is trapped; if they enter via a one-way path ("valve"), they MUST be able to leave using current abilities. Always design fail-safe escape routes.
- NEVER create empty dead ends; if a player backtracks to a remote area, they MUST be rewarded with a collectible, lore, or currency. Empty rooms are design failures.
- NEVER make backtracking purely repetitive; as the player gains movement (Dash/Teleport), traversal through old areas MUST become faster. Open shortcuts to bypass long, early routes.
- NEVER hide the critical path without "crumbs"; use distinct Landmarks, unique lighting, or environmental storytelling to build the player's mental map.
- NEVER design abilities that serve only one purpose; strictly implement dual-use traversal and combat functionality (e.g., a "Dash" that crosses gaps and dodges attacks).
Persistence & Mapping
- NEVER forget to save persistent room state; if a player opens a chest or defeats a boss, that state MUST remain saved when they leave and return.
- NEVER load interconnected rooms synchronously via
load(); strictly useResourceLoader.load_threaded_request()for seamless transitions. - NEVER track global progression within localized room scripts; strictly use Autoload Singletons for global ability flags and world state.
- NEVER use floating-point types for grid coordinates (minimaps/fog); strictly use
Vector2ito prevent precision jitter. - NEVER manipulate the SceneTree directly from a background loading thread; strictly use
call_deferred().
Physics & Controls
- NEVER calculate jump arcs or dashes inside
_process(); strictly use_physics_process()to prevent stutter. - NEVER multiply
CharacterBody2Dvelocity bydeltabeforemove_and_slide(); the engine handles this internally. - NEVER poll
is_action_just_pressed()inside_physics_process()for buffering; strictly capture events in_unhandled_input(). - NEVER use standard strings for high-frequency ability checks; strictly use
StringName(&"dashing") for pointer-speed comparisons. - NEVER iterate through every node to broadcast updates; strictly use
SceneTree.call_group()for efficient mass communication. - NEVER delete active room/player nodes via
free(); strictly usequeue_free()to avoid segmentation faults.
🛠 Expert Components (scripts/)
Golden-path order (MANDATORY reads)
- Persistence / flags — metroid_game_state.gd + persistent_progression_system.gd
- Ability definitions / gates — ability_unlock_resource.gd + progression_gate_manager.gd
- Room stream / switch — background_room_streamer.gd + safe_scene_switcher.gd
- Map fog — minimap_fog_manager.gd (orchestrator) with minimap_fog.gd / minimap_fog_revealer.gd
Original Expert Patterns
- minimap_fog.gd - Grid-based fog of war that tracks visited rooms and persists via global save data.
- progression_gate_manager.gd - Central manager for ability-gated progression (Locks/Keys) and world persistence.
- metroid_game_state.gd - MANDATORY Autoload-shaped world/ability/collectible state (do not paste a local game_state tutorial).
- ability_unlock_resource.gd - MANDATORY Resource definitions for unlockable abilities queried by gates.
- minimap_fog_manager.gd - MANDATORY Vector2i fog orchestration synced to progression/save.
Modular Components
- platformer_jump_buffer.gd - Modular coyote time and jump buffering for high-fidelity movement.
- background_room_streamer.gd - Thread-safe background room preloading using
ResourceLoader. - safe_scene_switcher.gd - Deferred scene transition pattern for stable cross-room world-state switching.
- minimap_fog_revealer.gd - Vector2i-based fog-of-war clearing logic synced to player position.
- persistent_progression_system.gd - Autoload pattern for tracking global ability/collectible flags.
- ability_state_machine.gd - Optimized
StringNamepattern matching for traversal/combat states. - fast_wall_detector.gd - Direct
PhysicsServerqueries for performance-optimized wall detection. - save_station_broadcast.gd - Group-based entity resetting and healing logic on save interaction.
- decoupled_hazard_logic.gd - Interface-style pattern for generic damage interaction.
- smooth_room_camera_transition.gd - Tween-based camera limit interpolation for seamless room movement.
Core Loop
- Exploration → blocked by a lock
- Discovery → key ability / boss
- Acquisition → new traversal/combat tool
- Backtracking → shortcuts + dual-use abilities
- Progression → new biome opens
Skill Chain
| Phase | Skills | Purpose |
|---|---|---|
| 1. Character | godot-characterbody-2d, godot-state-machine-advanced | Tight movement + ability states |
| 2. World | godot-tilemap-mastery, godot-scene-management | Rooms, biomes, threaded transitions |
| 3. Systems | godot-save-load-systems, godot-ability-system | Persist gates/collectibles; unlock keys |
| 4. UI | godot-ui-containers, godot-inventory-system | Map / inventory / HUD |
| 5. Balance | godot-monte-carlo-balancer | Soft-lock risk, backtrack length |
Architecture (script-first — no inline recipes)
1. Game State & Persistence
MANDATORY: metroid_game_state.gd + persistent_progression_system.gd. Rooms never own global ability flags. Room metadata uses resource_local_to_scene so instanced rooms do not share collectible state.
2. Room Transitions & Fast Travel
MANDATORY: background_room_streamer.gd + safe_scene_switcher.gd.
Fast travel must match NEVER (threaded load + deferred swap) — never ResourceLoader.load() / sync change_scene:
gdscriptclass_name FastTravelSystem extends Node var _pending_path: String = "" var _spawn_id: StringName = &"" func travel_to_room(scene_path: String, spawn_id: StringName) -> void: _pending_path = scene_path _spawn_id = spawn_id var err := ResourceLoader.load_threaded_request(scene_path) if err != OK: push_error("Fast travel request failed: %s" % scene_path) return set_process(true) func _process(_delta: float) -> void: var status := ResourceLoader.load_threaded_get_status(_pending_path) if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS: return set_process(false) if status != ResourceLoader.THREAD_LOAD_LOADED: push_error("Fast travel load failed: %s" % _pending_path) return var packed := ResourceLoader.load_threaded_get(_pending_path) as PackedScene # SceneTree work must be deferred — never from a worker thread call_deferred("_swap_room", packed, _spawn_id) func _swap_room(packed: PackedScene, spawn_id: StringName) -> void: GlobalState.target_spawn_id = spawn_id get_tree().change_scene_to_packed(packed)
3. Ability Gating
MANDATORY: ability_unlock_resource.gd + progression_gate_manager.gd + ability_state_machine.gd. Gates query StringName abilities from the Autoload — do not hardcode ability strings in room scripts.
4. Map / Fog
MANDATORY: minimap_fog_manager.gd. Use Vector2i cells only.
Design Principles (from Dreamnoid)
- Ability Versatility — traversal + combat dual use
- Practice Rooms — teach before punish
- Landmarks — mental map without explicit markers
- Item micro-stories — lore without cutscene walls
Common Pitfalls
- Softlocks on one-way valves — always design an escape with current abilities
- Backtracking tedium — shortcuts + faster movement after unlocks
- Empty dead ends — every remote path needs a reward
- Sync room loads — violates NEVER; use threaded request + deferred swap
Expert knowledge (on demand)
LLM-ignorance rule: If a general agent would not know it before reading, load the reference — never delete expert deltas.
- expert-metroidvania-patterns.md — restored baseline pedagogy (architecture, WHY, implementation depth)
- room_metadata.gd
- progression_manager.gd
- fast_travel_system.gd
Reference
Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.
Official Documentation
- Using CharacterBody2D —
move_and_slide, floor detection, and velocity rules for coyote/buffer jumps and dash traversal. - Idle and Physics Processing — keep jump arcs, dashes, and wall slides in
_physics_process; capture buffers in input callbacks. - InputEvent —
_unhandled_inputjump/ability buffering so presses are not lost between physics ticks. - Background loading —
ResourceLoader.load_threaded_requestfor adjacent-room preload without hitch spikes. - Change scenes manually — deferred room swaps, spawn door IDs, and safe
queue_freeof the outgoing room. - Saving games — persist abilities, opened gates, collectibles, and visited map cells across sessions.
- Singletons (Autoload) — global progression/game-state ownership so rooms never keep conflicting ability flags.
- Resources — ability unlock definitions and room metadata as
.tresdata with safe duplication. - Using Tilemaps —
TileMapLayer+Vector2icells for minimap fog revelation and grid room tracking. - Camera2D — room
limit_*bounds and tweened limit handoffs for seamless camera room transitions. - Using Area2D — doors, save stations, and hazard triggers via body_entered without hard scene coupling.
- Groups —
call_groupfor save-station heal/respawn broadcasts instead of walking the whole tree.
Related Skills
Prerequisites
- godot-project-foundations — autoloads, scene layout, and project settings before stacking room streaming and global progression.
- godot-characterbody-2d — tight platformer locomotion is the substrate under ability-gated traversal (dash, wall slide, double jump).
- godot-tilemap-mastery — layered TileMap/TileMapLayer authorship for gameplay collision, landmarks, and minimap fog grids.
- godot-autoload-architecture — singleton ownership patterns for ability flags and world persistence that rooms must not duplicate.
Complements
- godot-scene-management — threaded load queues and deferred room switches that keep interconnected maps hitch-free.
- godot-save-load-systems — durable schemas for collectibles, boss flags, and visited cells across long exploration sessions.
- godot-camera-systems — room limits, RemoteTransform follow, and transition polish beyond basic Camera2D bounds.
- godot-ability-system — unlockable traversal/combat abilities that gates and state machines query as the “keys.”
- godot-state-machine-advanced — hierarchical player states for dash/wall-slide/double-jump without nested
if/elifsprawl. - godot-2d-physics — layers, Area2D doors/hazards, and direct space queries for wall detection and soft-lock-safe valves.
- godot-inventory-system — collectible/key item tracking that feeds map rewards and ability acquisition UI.
Downstream / consumers
- godot-monte-carlo-balancer — simulate ability unlock order, backtrack length, and soft-lock risk once gates and reward density are tunable.
- godot-genre-platformer — pure movement-feel patterns that Metroidvania traversal builds on when stripping ability gating.
- godot-signal-architecture — ability_unlocked / gate_opened / map_revealed buses so HUD and rooms observe progression without owning it.
Master
- godot-master — library router and mirrored module entry; use when discovering peer skills or syncing shared script mirrors after Domain Skill edits.

