Add some UI
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
# 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.
|
||||
|
||||
```gdscript
|
||||
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](stubs.md)).
|
||||
|
||||
## Entity
|
||||
|
||||
All overworld objects (player, NPCs, items, triggers) are instances of [entity/Entity.tscn](../../overworld/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](../../overworld/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", "")`
|
||||
Reference in New Issue
Block a user