Refactor Table to use new SharedInventoryStation
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
class_name SharedInventoryStation
|
||||
extends Station
|
||||
|
||||
### Abstract 'class', should never be instantiated ###
|
||||
# Backbone for stations that need to know about neighboring stations of the
|
||||
# same kind (tables, belts).
|
||||
|
||||
## Path to one Area3D probe per direction this station should watch for a
|
||||
## same-kind neighbor. Wired explicitly per concrete scene (e.g. a belt
|
||||
## wires one path, a table wires four).
|
||||
@export var probe_paths: Array[NodePath] = []
|
||||
|
||||
## Resolved from probe_paths in ready().
|
||||
var probes: Array[Area3D] = []
|
||||
|
||||
## Area3D probe -> the SharedInventoryStation neighbor detected through it
|
||||
## (or null if that direction is currently unoccupied).
|
||||
var neighbors: Dictionary = {}
|
||||
|
||||
|
||||
# The child station has to call super._enter_tree() for this to be called
|
||||
func _enter_tree() -> void:
|
||||
super._enter_tree()
|
||||
var properties: Array[NodePath] = [".:position", ".:rotation", ".:visible"]
|
||||
for property_path in properties:
|
||||
sync_config.add_property(property_path)
|
||||
sync_config.property_set_spawn(property_path, false)
|
||||
sync_config.property_set_replication_mode(property_path, SceneReplicationConfig.REPLICATION_MODE_ON_CHANGE)
|
||||
|
||||
|
||||
# The child station has to call super.ready() for this to be called
|
||||
func ready() -> void:
|
||||
super.ready()
|
||||
# Find probes
|
||||
for path in probe_paths:
|
||||
var probe := get_node_or_null(path) as Area3D
|
||||
if probe:
|
||||
probes.append(probe)
|
||||
else:
|
||||
SweetLogger.warning("{0} could not resolve neighbor probe at path {1}", [name, path])
|
||||
if probes.is_empty():
|
||||
SweetLogger.warning("{0} has no neighbor probes wired", [name])
|
||||
for probe in probes:
|
||||
neighbors[probe] = null
|
||||
probe.body_entered.connect(_on_probe_body_entered.bind(probe))
|
||||
probe.body_exited.connect(_on_probe_body_exited.bind(probe))
|
||||
# Init leader state before any probe signal has a chance to fire
|
||||
_broadcast_group_changed.call_deferred()
|
||||
|
||||
|
||||
## Cycle-safe search of the neighbor graph (handles circular belt/table loops)
|
||||
func get_connected_group() -> Array[SharedInventoryStation]:
|
||||
var visited: Dictionary = {get_instance_id(): true}
|
||||
var queue: Array[SharedInventoryStation] = [self]
|
||||
var group: Array[SharedInventoryStation] = []
|
||||
while not queue.is_empty():
|
||||
var current: SharedInventoryStation = queue.pop_front()
|
||||
group.append(current)
|
||||
for neighbor in current.neighbors.values():
|
||||
if neighbor and not visited.has(neighbor.get_instance_id()):
|
||||
visited[neighbor.get_instance_id()] = true
|
||||
queue.append(neighbor)
|
||||
return group
|
||||
|
||||
|
||||
## Deterministic leader: smallest grid position, computed the same on every peer
|
||||
func is_group_leader() -> bool:
|
||||
var leader: SharedInventoryStation = self
|
||||
for member in get_connected_group():
|
||||
if _grid_sort_key(member) < _grid_sort_key(leader):
|
||||
leader = member
|
||||
return leader == self
|
||||
|
||||
|
||||
static func _grid_sort_key(station: SharedInventoryStation) -> Vector2i:
|
||||
var pos := station.global_position
|
||||
return Vector2i(roundi(pos.x / Helper.SNAP_GRID_SIZE), roundi(pos.z / Helper.SNAP_GRID_SIZE))
|
||||
|
||||
|
||||
## Food items this station holds, exposed for neighbors to read; override for multi-zone stations
|
||||
func get_exposed_food_items() -> Array[FoodItem]:
|
||||
var items: Array[FoodItem] = []
|
||||
if snap_zone and snap_zone.picked_up_object:
|
||||
var food_item := Helper.find_food_item(snap_zone.picked_up_object)
|
||||
if food_item:
|
||||
items.append(food_item)
|
||||
return items
|
||||
|
||||
|
||||
## Every food item held anywhere in this station's connected group
|
||||
func get_group_food_items() -> Array[FoodItem]:
|
||||
var items: Array[FoodItem] = []
|
||||
for member in get_connected_group():
|
||||
items.append_array(member.get_exposed_food_items())
|
||||
return items
|
||||
|
||||
|
||||
func _on_probe_body_entered(body: Node3D, probe: Area3D) -> void:
|
||||
var station := body as SharedInventoryStation
|
||||
if not station or station == self:
|
||||
return
|
||||
neighbors[probe] = station
|
||||
_broadcast_group_changed()
|
||||
|
||||
|
||||
func _on_probe_body_exited(body: Node3D, probe: Area3D) -> void:
|
||||
var former: SharedInventoryStation = neighbors.get(probe)
|
||||
if not former or body != former:
|
||||
return
|
||||
neighbors[probe] = null
|
||||
# former is no longer reachable from self, so it needs telling separately
|
||||
former._broadcast_group_changed()
|
||||
_broadcast_group_changed()
|
||||
|
||||
|
||||
## Notifies every member of this station's current connected group (not just the two that changed)
|
||||
func _broadcast_group_changed() -> void:
|
||||
for member in get_connected_group():
|
||||
member._on_group_changed()
|
||||
|
||||
|
||||
## Virtual hook: override to react to this station's group membership/leadership changing
|
||||
func _on_group_changed() -> void:
|
||||
pass
|
||||
Reference in New Issue
Block a user