Fix bug where only net_pickable could disable XRToolPickables

This commit is contained in:
JonShard
2026-08-16 09:07:46 +02:00
parent 9f33ee68c2
commit 08c3950583
3 changed files with 41 additions and 13 deletions
+3
View File
@@ -35,6 +35,9 @@ properties/1/replication_mode = 1
properties/2/path = NodePath("NetPickable:net_held_by") properties/2/path = NodePath("NetPickable:net_held_by")
properties/2/spawn = true properties/2/spawn = true
properties/2/replication_mode = 1 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"] [sub_resource type="Gradient" id="Gradient_s6exe"]
interpolation_mode = 2 interpolation_mode = 2
+22 -4
View File
@@ -24,6 +24,12 @@ var _original_freeze_mode: RigidBody3D.FreezeMode
# branch of apply_held_state() clears while someone else is holding the item. # branch of apply_held_state() clears while someone else is holding the item.
var _original_enabled: bool 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 # 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 current grab. apply_held_state() runs every network tick, so without this
# the guard message repeats for as long as you hold the item. # the guard message repeats for as long as you hold the item.
@@ -47,6 +53,17 @@ func _ready() -> void:
apply_held_state.call_deferred() 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: func _set_net_held_by(value: int) -> void:
var old := net_held_by var old := net_held_by
net_held_by = value 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 # 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 # the disabled item, leaving the zone holding an item with no grab
# driver that then fell out of the station. # 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(): if NetworkManager.is_online():
SweetLogger.debug("{0}: reclaiming ownership, restoring enabled {1}->{2}", [_pickable.name, _pickable.enabled, _original_enabled]) SweetLogger.debug("{0}: reclaiming ownership, restoring enabled {1}->{2}", [_pickable.name, _pickable.enabled, want_enabled_authority])
_pickable.enabled = _original_enabled _pickable.enabled = want_enabled_authority
return return
# A net_held_by/position sync update can race ahead of the # 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 # 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 # 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() # still re-apply if something else perturbs the state (e.g. let_go()
# restoring the collision mask after a force-drop). # 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 \ if _pickable.freeze \
and _pickable.freeze_mode == RigidBody3D.FREEZE_MODE_KINEMATIC \ and _pickable.freeze_mode == RigidBody3D.FREEZE_MODE_KINEMATIC \
and _pickable.collision_mask == 0 \ and _pickable.collision_mask == 0 \
+16 -9
View File
@@ -18,7 +18,7 @@ func _ready() -> void:
SweetLogger.warning("{0} missing chop_audio reference", [name]) SweetLogger.warning("{0} missing chop_audio reference", [name])
if not knife: if not knife:
SweetLogger.warning("{0} missing knife reference", [name]) SweetLogger.warning("{0} missing knife reference", [name])
_hide_all_tools() _disable_all_tools()
func _start_chopping(id: String, work: float) -> void: func _start_chopping(id: String, work: float) -> void:
@@ -33,12 +33,20 @@ func _process(delta: float) -> void:
super.process(delta) super.process(delta)
func _enable_tool(_item: XRToolsPickable):
func _hide_all_tools(): SweetLogger.debug("Enable tool {0} on {1}", [_item.name, name])
knife.visible = false _item.visible = true
knife.enabled = false _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: 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]) SweetLogger.debug("Held a FoodItem that is not choppable, id: {0} result: {1}", [food_item.id, result])
return return
knife.visible = true _enable_tool(knife)
knife.enabled = true
_start_chopping(result, RecipeManager.get_chopping_work(food_item.id)) _start_chopping(result, RecipeManager.get_chopping_work(food_item.id))
# Called from Station base class # Called from Station base class
func on_object_dropped(_item: Node3D) -> void: func on_object_dropped(_item: Node3D) -> void:
SweetLogger.debug("->[]") SweetLogger.debug("->[]")
_hide_all_tools() _disable_all_tools()
reset() reset()
# Called from Station base class # Called from Station base class
func on_work_complete(): func on_work_complete():
SweetLogger.debug("->[]") SweetLogger.debug("->[]")
_hide_all_tools() _disable_all_tools()
convert_item() convert_item()