diff --git a/Items/knife.tscn b/Items/knife.tscn index feff282..294ba21 100644 --- a/Items/knife.tscn +++ b/Items/knife.tscn @@ -35,6 +35,9 @@ properties/1/replication_mode = 1 properties/2/path = NodePath("NetPickable:net_held_by") properties/2/spawn = true properties/2/replication_mode = 1 +properties/3/path = NodePath(".:enabled") +properties/3/spawn = false +properties/3/replication_mode = 1 [sub_resource type="Gradient" id="Gradient_s6exe"] interpolation_mode = 2 diff --git a/Net/net_pickable.gd b/Net/net_pickable.gd index fc7db55..cfeebeb 100644 --- a/Net/net_pickable.gd +++ b/Net/net_pickable.gd @@ -24,6 +24,12 @@ var _original_freeze_mode: RigidBody3D.FreezeMode # branch of apply_held_state() clears while someone else is holding the item. var _original_enabled: bool +# Intent set by external game logic (e.g. a station enabling/disabling a tool), +# independent of hold state. apply_held_state() is the sole writer of the +# pickable's actual `enabled` property, so this is how other systems express +# "should be enabled" without fighting that reconciliation every tick. +var _tool_enabled: bool = true + # Whether the "our own hand still holds this" guard has already been logged for # the current grab. apply_held_state() runs every network tick, so without this # the guard message repeats for as long as you hold the item. @@ -47,6 +53,17 @@ func _ready() -> void: apply_held_state.call_deferred() +## Called by external game logic (e.g. Counter._enable_tool/_disable_tool) to +## express whether this item should be usable right now, independent of hold +## state. Reapplies immediately so the change takes effect without waiting for +## the next net_held_by tick. +func set_tool_enabled(value: bool) -> void: + if _tool_enabled == value: + return + _tool_enabled = value + apply_held_state() + + func _set_net_held_by(value: int) -> void: var old := net_held_by net_held_by = value @@ -95,10 +112,11 @@ func apply_held_state() -> void: # e.g. a plate still got marked dirty) while pick_up() bailed out on # the disabled item, leaving the zone holding an item with no grab # driver that then fell out of the station. - if _pickable.enabled != _original_enabled: + var want_enabled_authority := _original_enabled and _tool_enabled + if _pickable.enabled != want_enabled_authority: if NetworkManager.is_online(): - SweetLogger.debug("{0}: reclaiming ownership, restoring enabled {1}->{2}", [_pickable.name, _pickable.enabled, _original_enabled]) - _pickable.enabled = _original_enabled + SweetLogger.debug("{0}: reclaiming ownership, restoring enabled {1}->{2}", [_pickable.name, _pickable.enabled, want_enabled_authority]) + _pickable.enabled = want_enabled_authority return # A net_held_by/position sync update can race ahead of the # authority-handoff RPC that's about to confirm a grab we just made @@ -125,7 +143,7 @@ func apply_held_state() -> void: # physics-property writes per item per tick. The comparison also means we # still re-apply if something else perturbs the state (e.g. let_go() # restoring the collision mask after a force-drop). - var want_enabled := (net_held_by == 0) + var want_enabled := (net_held_by == 0) and _tool_enabled if _pickable.freeze \ and _pickable.freeze_mode == RigidBody3D.FREEZE_MODE_KINEMATIC \ and _pickable.collision_mask == 0 \ diff --git a/Stations/counter.gd b/Stations/counter.gd index 354e5ef..9342d27 100644 --- a/Stations/counter.gd +++ b/Stations/counter.gd @@ -18,7 +18,7 @@ func _ready() -> void: SweetLogger.warning("{0} missing chop_audio reference", [name]) if not knife: SweetLogger.warning("{0} missing knife reference", [name]) - _hide_all_tools() + _disable_all_tools() func _start_chopping(id: String, work: float) -> void: @@ -33,12 +33,20 @@ func _process(delta: float) -> void: super.process(delta) - -func _hide_all_tools(): - knife.visible = false - knife.enabled = false +func _enable_tool(_item: XRToolsPickable): + SweetLogger.debug("Enable tool {0} on {1}", [_item.name, name]) + _item.visible = true + _item.get_node("NetPickable").set_tool_enabled(true) +func _disable_tool(_item: XRToolsPickable): + SweetLogger.debug("Disable tool {0} on {1}", [_item.name, name]) + _item.visible = false + _item.get_node("NetPickable").set_tool_enabled(false) + +func _disable_all_tools(): + SweetLogger.debug("Disable all tools on {0}", [name]) + _disable_tool(knife) func _on_gesture_area_body_entered(body: Node3D) -> void: @@ -84,20 +92,19 @@ func on_food_item_picked_up(food_item: FoodItem) -> void: SweetLogger.debug("Held a FoodItem that is not choppable, id: {0} result: {1}", [food_item.id, result]) return - knife.visible = true - knife.enabled = true + _enable_tool(knife) _start_chopping(result, RecipeManager.get_chopping_work(food_item.id)) # Called from Station base class func on_object_dropped(_item: Node3D) -> void: SweetLogger.debug("->[]") - _hide_all_tools() + _disable_all_tools() reset() # Called from Station base class func on_work_complete(): SweetLogger.debug("->[]") - _hide_all_tools() + _disable_all_tools() convert_item()