Files
Dawn-Godot/.claude/docs/overworld.md
T
2026-06-11 20:42:08 -05:00

4.6 KiB
Raw Blame History

Overworld System

Scene structure

OverworldScene (Node3D)          ← always resident, managed by SCENE singleton
  └─ Map (Node3D)                ← emptied and repopulated on each map change

TestMap (Node3D)                 ← typical map root, extends Node3D
  ├─ Player (Entity)             ← movementType = PLAYER, entityId = "player"
  ├─ NPC/object (Entity) ...     ← interactType drives what happens on interact
  ├─ TestMapBase (StaticBody3D)  ← reusable terrain plane (200×200, collision layer 1)
  └─ Camera3D (OverworldCamera)  ← targetNode → Player

Map transitions

Use OVERWORLD.mapChange(path, destinationNodeName) to switch maps.

OVERWORLD.mapChange("res://overworld/map/SomeMap.tscn", "SpawnPoint")

Flow: fade-out begins → map loads on a background thread → when both complete, OVERWORLD.mapChanged fires → OverworldScene clears Map children and instances the new map → fade-in begins. The destinationNodeName is passed with the signal for the new map to use as a spawn point (not yet wired to player placement — see stubs).

Entity

All overworld objects (player, NPCs, items, triggers) are instances of entity/Entity.tscn with different export values.

Export Purpose
entityId:String UUID; use the Inspector button to regenerate
movementType:MovementType NONE (static), DISABLED, or PLAYER (input-driven)
interactType:InteractType What happens when the player presses Interact nearby
dialogueResource:DialogueResource .dialogue file — required for CONVERSATION
dialogueTitle:String Dialogue section to start from (default "start")
oneTimeItem:ItemResource Item granted on interact — required for ONE_TIME_ITEM
cutscene:CutsceneResource Cutscene to run — required for CUTSCENE

Interaction types

InteractType Behaviour
NONE Not interactable
CONVERSATION Runs dialogueResource from dialogueTitle via DialogueAction
ONE_TIME_ITEM Grants oneTimeItem, then frees the entity
CUTSCENE Queues and starts cutscene
BATTLE_TEST Starts a test battle (hardcoded enemy, for dev use)

To add a new interaction type: add a value to Entity.InteractType, then add the matching match branch in EntityInteractableArea.onInteract() (entity/EntityInteractableArea.gd).

Collision layers

Area Layer Mask Purpose
EntityInteractingArea 0 2 Player's reach — detects nearby interactables
EntityInteractableArea 2 0 Entity's surface — detected by other reaches

The asymmetric setup means entities never trigger themselves.

Movement

EntityMovement (a child Node under Components) handles all physics each frame:

  1. Apply gravity if airborne
  2. Apply friction (velocity.x/z *= delta * FRICTION)
  3. If _canMove() and movementType == PLAYER: read input, compute camera-relative direction, set velocity
  4. move_and_slide()

Movement is blocked (_canMove() → false) when UI.dialogueActive, UI.TEXTBOX is open, or UI.GAME_MENU.isOpen().

Camera-relative direction is derived from the active Camera3D's basis — the camera's Y-zeroed and renormalized X/Z axes map input axes to world axes. The entity faces (look_at) the movement direction each frame.

Camera

OverworldCamera orbits around targetNode using yaw/pitch angles driven by camera_orbit_* inputs.

Export Default Purpose
targetNode:Node3D Node to orbit (assign Player in scene)
pivotOffset:Vector3 (0, 1.2, 0) Orbit point above entity origin
distance:float 10.0 Orbit radius
pitchMin/Max:float -10° / 70° Vertical clamp
orbitSensitivity:float 120.0 Degrees/sec at full input
collisionMask:int Layers the camera avoids (terrain = layer 1)

If the ray from pivot to desired camera position hits collisionMask, the camera is pulled in to just in front of the hit point (with COLLISION_MARGIN = 0.3), clamped to minDistance.

Adding a new map

  1. Create a new scene (Node3D root) in overworld/map/
  2. Add Entity instances, set interactType and relevant exports in the Inspector
  3. Instance TestMapBase (or your own terrain) as a child
  4. Add a Camera3D with OverworldCamera script; set targetNode to the Player entity
  5. Add a Player entity with movementType = PLAYER and entityId = "player"
  6. Switch to it with OVERWORLD.mapChange("res://overworld/map/YourMap.tscn", "")