5.9 KiB
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.tscnis a Godot "inherited scene" ofabstract/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) extendsSharedInventoryStation, which extendsStation.
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
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 Area3Ds, 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 onsync_configin_enter_tree()for straight property replication.
Notes
stations/station.gd(extends "res://stations/hob.gd", noclass_name) is not attached to any scene and isn't part of this hierarchy — it looks like leftover scaffolding.