180 lines
4.6 KiB
GDScript
180 lines
4.6 KiB
GDScript
@tool
|
|
class_name PersistentPocket
|
|
extends XRToolsSnapZone
|
|
|
|
|
|
## Persistent Pocket Node
|
|
##
|
|
## The [PersistentPocket] type holds persistent items managed by the
|
|
## persistence system. The [PersistentPocket] type extends from
|
|
## [XRToolsSnapZone] to allow [PersistentItem] objects to be snapped or
|
|
## removed by the player.
|
|
|
|
|
|
## Enumeration to control pocket behavior when the parent item is held
|
|
enum HeldBehavior {
|
|
IGNORE, ## Ignore picked_up/dropped changes
|
|
ENABLE, ## Enable when picked up
|
|
DISABLE ## Disable when picked up
|
|
}
|
|
|
|
|
|
# Group for world-data properties
|
|
@export_group("World Data")
|
|
|
|
## This property specifies the unique ID of this pocket
|
|
@export var pocket_id : String
|
|
|
|
# Group for options
|
|
@export_group("Options")
|
|
|
|
## Pocket behavior when held
|
|
@export var held_behavior := HeldBehavior.ENABLE : set = _set_held_behavior
|
|
|
|
|
|
# Parent pickable body
|
|
var _parent_body : XRToolsPickable
|
|
|
|
|
|
# Add support for is_xr_class
|
|
func is_xr_class(p_name : String) -> bool:
|
|
return p_name == "PersistentPocket" or super(p_name)
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready():
|
|
super()
|
|
|
|
# Skip initialization if in editor
|
|
if Engine.is_editor_hint():
|
|
return
|
|
|
|
# Search for an ancestor XRToolsPickable
|
|
_parent_body = XRTools.find_xr_ancestor(self, "*", "XRToolsPickable")
|
|
if _parent_body:
|
|
_parent_body.picked_up.connect(_on_picked_up)
|
|
_parent_body.dropped.connect(_on_dropped)
|
|
|
|
# Update the held behavior
|
|
_update_held_behavior()
|
|
|
|
|
|
# Get configuration warnings
|
|
func _get_configuration_warnings() -> PackedStringArray:
|
|
var warnings := PackedStringArray()
|
|
|
|
# Verify pocket ID is set
|
|
if not pocket_id:
|
|
warnings.append("Pocket ID not zet")
|
|
|
|
# Verify pocket is in persistent group
|
|
if not is_in_group("persistent"):
|
|
warnings.append("Pocket not in 'persistent' group")
|
|
|
|
# Return warnings
|
|
return warnings
|
|
|
|
|
|
# Handle notifications
|
|
func _notification(what : int) -> void:
|
|
# Ignore notifications on freeing objects
|
|
if is_queued_for_deletion():
|
|
return
|
|
|
|
match what:
|
|
Persistent.NOTIFICATION_LOAD_STATE:
|
|
_load_state()
|
|
|
|
Persistent.NOTIFICATION_SAVE_STATE:
|
|
_save_state()
|
|
|
|
Persistent.NOTIFICATION_DESTROY:
|
|
_destroy()
|
|
|
|
|
|
# This method loads the pocket state from [PersistentWorld]. If the
|
|
# [PersistentWorld] indicates this pocket holds an item then the item is
|
|
# created and picked up by the pocket.
|
|
func _load_state() -> void:
|
|
# Queue populating the pocket as new nodes cannot be created inside a
|
|
# notification handler.
|
|
_populate_pocket.call_deferred()
|
|
|
|
|
|
# This method saves the state of the pocket to [PersistentWorld].
|
|
func _save_state() -> void:
|
|
# Handle pocket not holding on to PersistentItem
|
|
if not picked_up_object is PersistentItem:
|
|
# Save that the pocket is empty
|
|
PersistentWorld.instance.clear_value(pocket_id)
|
|
return
|
|
|
|
# Get the item_id of the PersistentItem in the pocket
|
|
var item_id : String = picked_up_object.item_id
|
|
|
|
# Save that the pocket holds the item
|
|
PersistentWorld.instance.set_value(pocket_id, item_id)
|
|
|
|
|
|
# This method destroys the pocket and any item inside it.
|
|
func _destroy() -> void:
|
|
# Propagate destruction for anything we hold
|
|
if is_instance_valid(picked_up_object):
|
|
print(self, " propagating destroy to ", picked_up_object.name)
|
|
picked_up_object.propagate_notification(Persistent.NOTIFICATION_DESTROY)
|
|
picked_up_object.queue_free()
|
|
|
|
|
|
# Populate the contents of a pocket
|
|
func _populate_pocket() -> void:
|
|
# Get the ID of the item in the pocket
|
|
var item_id = PersistentWorld.instance.get_value(pocket_id)
|
|
if not item_id is String:
|
|
return
|
|
|
|
# Construct the item for the pocket
|
|
var zone = PersistentZone.find_instance(self)
|
|
var item := zone.create_item_instance(item_id)
|
|
if not item:
|
|
return
|
|
|
|
# Put the item in the pocket
|
|
item.global_transform = global_transform
|
|
pick_up_object.call_deferred(item)
|
|
|
|
|
|
# Called when the parent pickable body is picked up
|
|
func _on_picked_up(_pickable) -> void:
|
|
_update_held_behavior()
|
|
|
|
|
|
# Called when the parent pickable body is dropped
|
|
func _on_dropped(_pickable) -> void:
|
|
_update_held_behavior()
|
|
|
|
|
|
# Called when the held_behavior property has been modified
|
|
func _set_held_behavior(p_held_behavior : HeldBehavior) -> void:
|
|
held_behavior = p_held_behavior
|
|
if is_inside_tree() and _parent_body:
|
|
_update_held_behavior()
|
|
|
|
|
|
# Update the pocket enable
|
|
func _update_held_behavior() -> void:
|
|
# Skip if no valid parent body
|
|
if not is_instance_valid(_parent_body):
|
|
return
|
|
|
|
# Test if the parent pickable is held
|
|
var is_held := _parent_body.is_picked_up()
|
|
|
|
# Update the enabled state based on whether the parent body is held and
|
|
# the desired behavior
|
|
match held_behavior:
|
|
HeldBehavior.ENABLE:
|
|
enabled = is_held
|
|
|
|
HeldBehavior.DISABLE:
|
|
enabled = not is_held
|