Files
VRyHungry1/documentation/Stations.md
T
JonShard acb96933a7 Docs
2026-08-17 16:09:08 +02:00

123 lines
5.9 KiB
Markdown

# Stations
## Overview
A "station" is any interactable kitchen fixture a player works at: `Counter`, `Hob`, `Sink`, `Table`, `DirtStation`, and the item dispensers. They share networking, sound, and snap-zone plumbing through a common script base, and share their node layout through a common base scene.
Two independent inheritance mechanisms combine to build a concrete station, e.g. `Table`:
- **Scene inheritance** — `table.tscn` is a Godot "inherited scene" of `abstract/station.tscn`, so it gets the same child nodes (snap zone, synchronizer, audio players, progress bar) for free.
- **Script inheritance** — `table.gd` (attached as a script override on that inherited scene) extends `SharedInventoryStation`, which extends `Station`.
These two axes are independent: which base scene a `.tscn` inherits from is unrelated to which class its attached script extends.
## Scene inheritance
`abstract/station.tscn` is the base scene every station scene inherits from:
```
Station (Node3D)
├─ StationMovement (drag-to-move handle, res://stations/StationMovement.tscn)
├─ MultiplayerSynchronizer
├─ AudioStreamPlayer3DNotification
├─ AudioStreamPlayer3DAmbient
├─ ProgressBar3D
└─ SnapZone
```
Each concrete station scene inherits this scene and overrides the root node's `script` property to attach its own class:
| Scene | Script attached |
|---|---|
| `Counter.tscn` | `stations/counter.gd` (`Counter`) |
| `Hob.tscn` | `stations/hob.gd` (`Hob`) |
| `sink.tscn` | `stations/sink.gd` (`Sink`) |
| `table.tscn` | `stations/table.gd` (`Table`) |
| `dirt_station.tscn` | `stations/dirt_station.gd` (`DirtStation`) |
| `BurgerBunsDispenser.tscn`, `cube_side_dispense.tscn`, `plate_dispenser.tscn`, `potato_dispenser.tscn`, `raw_burger_dispenser.tscn` | `stations/item_dispenser.gd` (`ItemDispenser`) |
`StationMovement` (`stations/station_movement.gd`) is a sibling node composed into the base scene, not a station subclass — it's what lets players pick the whole station up and move it in build mode.
## Script (class) inheritance
```mermaid
classDiagram
direction TB
Node3D <|-- Station
Station <|-- WorkStation
Station <|-- SharedInventoryStation
Station <|-- DirtStation
Station <|-- ItemDispenser
WorkStation <|-- Hob
WorkStation <|-- Sink
WorkStation <|-- Counter
SharedInventoryStation <|-- Table
class Station {
<<abstract>>
+snap_zone : XRToolsSnapZone
+synchronizer : MultiplayerSynchronizer
+enabled : bool
+on_object_picked_up(item)
+on_object_dropped(item)
+refresh_display()
}
class WorkStation {
<<abstract>>
+current_work : float
+max_work : float
+result_id : String
+add_work(work)
+convert_item()
}
class SharedInventoryStation {
<<abstract>>
+probes : Area3D[]
+neighbors : Dictionary
+get_connected_group()
+is_group_leader()
}
class Hob {
+cook_speed : float
}
class Sink {
+wash_speed : float
}
class Counter {
+chop_work_steps : float
}
class DirtStation
class ItemDispenser {
+item_scene : PackedScene
}
class Table {
+_state : TableState
+place_order()
}
```
| Class | File | Role |
|---|---|---|
| `Station` | `abstract/station.gd` | Base for every station. Owns the snap zone, sets up the `MultiplayerSynchronizer`'s replication config, propagates pickup/drop events to clients over RPC, and exposes virtual hooks (`on_object_picked_up`, `on_object_dropped`, `on_food_item_picked_up`, `on_food_item_dropped`, `refresh_display`). |
| `WorkStation` | `abstract/work_station.gd` | Base for stations that convert a held `FoodItem` over time via a synced work meter (`current_work`/`max_work`/`result_id`). `add_work()` routes through the server via RPC, and `convert_item()` swaps the held item for the recipe result on completion. |
| `Hob` | `stations/hob.gd` | Cooks food using `RecipeManager` cooking recipes; plays a cook animation while active. |
| `Sink` | `stations/sink.gd` | Washes a dirty `PlateController` over time; drives water/bubble effect nodes. |
| `Counter` | `stations/counter.gd` | Chops food when a knife gesture area detects a fast-enough swipe. |
| `SharedInventoryStation` | `abstract/shared_inventory_station.gd` | Base for stations that need to know about same-kind neighbors. Resolves `probe_paths` into neighbor `Area3D`s, builds a connected-group graph (cycle-safe, for belt/table loops), and computes a deterministic group leader. |
| `Table` | `stations/table.gd` | The only concrete `SharedInventoryStation`. Runs the customer-order finite state machine (`EMPTY → THINKING → ORDERING → WAITING_PRIMARY → WAITING_FRIEND → EATING`); the connected group's leader orchestrates state, followers mirror it. |
| `DirtStation` | `stations/dirt_station.gd` | Marks a dropped plate as dirty. Extends `Station` directly (no work meter). |
| `ItemDispenser` | `stations/item_dispenser.gd` | Server keeps a fresh instance of `item_scene` spawned into its snap zone whenever it's empty. Extends `Station` directly. |
## Networking pattern
Every station follows the same client/server split, established in `Station`:
- The server (`NetworkManager.owns_world()`) is authoritative for state changes.
- A client calls a public method (e.g. `add_work`, `place_order`); if it isn't the server, that method forwards the call via `*_id.rpc_id(1)` and returns.
- The server applies the change locally, then RPCs the result back out to clients (e.g. `_clients_on_object_picked_up_handler`, `_everyone_on_work_complete`).
- Frequently-changing fields (`current_work`, `result_id`, table `_state`, etc.) are also registered on `sync_config` in `_enter_tree()` for straight property replication.
## Notes
- `stations/station.gd` (`extends "res://stations/hob.gd"`, no `class_name`) is not attached to any scene and isn't part of this hierarchy — it looks like leftover scaffolding.