240 lines
7.3 KiB
GDScript
240 lines
7.3 KiB
GDScript
@tool
|
|
class_name PersistentZone
|
|
extends XRToolsSceneBase
|
|
|
|
|
|
## Persistent Zone Node
|
|
##
|
|
## The [PersistentNode] class is an extension of [XRToolsSceneBase] which
|
|
## manages the state of the zones [PersistentItem] objects through the
|
|
## persistence system.
|
|
|
|
|
|
# Group for world-data properties
|
|
@export_group("World Data")
|
|
|
|
## This property specifies the persistent zone information
|
|
@export var zone_info : PersistentZoneInfo
|
|
|
|
|
|
# Add support for is_xr_class
|
|
func is_xr_class(p_name : String) -> bool:
|
|
return p_name == "PersistentZone" or super(p_name)
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready() -> void:
|
|
# call the base
|
|
super()
|
|
|
|
|
|
# Get configuration warnings
|
|
func _get_configuration_warnings() -> PackedStringArray:
|
|
var warnings := PackedStringArray()
|
|
|
|
# Verify zone info is set
|
|
if not zone_info:
|
|
warnings.append("Zone ID not zet")
|
|
|
|
# Return warnings
|
|
return warnings
|
|
|
|
|
|
## Handle zone loaded
|
|
func scene_loaded(user_data = null):
|
|
super(user_data)
|
|
|
|
# Save the current zone
|
|
GameState.current_zone = self
|
|
|
|
# Find all PersistentItem instances designed into the zone
|
|
var items_in_zone := {}
|
|
for node in XRTools.find_xr_children(self, "*", "PersistentItem"):
|
|
items_in_zone[node.item_id] = node
|
|
|
|
# Find the zone items the PersistentWorld thinks should be in this zone.
|
|
var zone_items = PersistentWorld.instance.get_value(zone_info.zone_id)
|
|
|
|
# Free items designed into the zone but PersistentWorld thinks should
|
|
# be removed.
|
|
if zone_items is Array:
|
|
for item_id in items_in_zone:
|
|
if not zone_items.has(item_id):
|
|
var item : PersistentItem = items_in_zone[item_id]
|
|
item.get_parent().remove_child(item)
|
|
item.queue_free()
|
|
|
|
# Load world-state for all items in the zone
|
|
propagate_notification(Persistent.NOTIFICATION_LOAD_STATE)
|
|
|
|
# Create items missing from the zone but PersistentWorld thinks should be
|
|
# present.
|
|
if zone_items is Array:
|
|
for item_id in zone_items:
|
|
if not items_in_zone.has(item_id):
|
|
create_item_instance(item_id)
|
|
|
|
# Create items held by the players left hand
|
|
var left_pickup := XRToolsFunctionPickup.find_left($XROrigin3D)
|
|
var left_item_id = PersistentWorld.instance.get_value("player.left_hand")
|
|
if left_pickup and left_item_id is String:
|
|
var left_instance := create_item_instance(left_item_id)
|
|
if left_instance:
|
|
left_instance.global_transform = left_pickup.global_transform
|
|
left_pickup._pick_up_object.call_deferred(left_instance)
|
|
|
|
# Create items held by the players right hand
|
|
var right_pickup := XRToolsFunctionPickup.find_right($XROrigin3D)
|
|
var right_item_id = PersistentWorld.instance.get_value("player.right_hand")
|
|
if right_pickup and right_item_id is String:
|
|
var right_instance := create_item_instance(right_item_id)
|
|
if right_instance:
|
|
right_instance.global_transform = right_pickup.global_transform
|
|
right_pickup._pick_up_object.call_deferred(right_instance)
|
|
|
|
|
|
## Handle zone exiting
|
|
func scene_exiting(user_data = null):
|
|
super(user_data)
|
|
|
|
# Ensure the zone state is saved before exiting the zone
|
|
save_world_state()
|
|
|
|
# Clear the current zone
|
|
GameState.current_zone = self
|
|
|
|
|
|
## This method saves the state of the zone to the [PersistentWorld]. This gets
|
|
## called upon exiting the zone; but it should also be called before saving
|
|
## the game.
|
|
func save_world_state() -> void:
|
|
# Save world-state for all items in the zone
|
|
propagate_notification(Persistent.NOTIFICATION_SAVE_STATE)
|
|
|
|
# Identify items held directly by the zone
|
|
var items_in_zone : Array[String] = []
|
|
for node in get_tree().get_nodes_in_group("persistent"):
|
|
if is_item_held_by_zone(node):
|
|
items_in_zone.append(node.item_id)
|
|
|
|
# Save the items held by the zone
|
|
PersistentWorld.instance.set_value(zone_info.zone_id, items_in_zone)
|
|
|
|
# Handle items held in the players left hand
|
|
var left_pickup := XRToolsFunctionPickup.find_left($XROrigin3D)
|
|
var left_item := _get_held_persistent_item(left_pickup)
|
|
if left_item:
|
|
# The player.left_hand holds the item
|
|
PersistentWorld.instance.set_value("player.left_hand", left_item.item_id)
|
|
else:
|
|
# The player.left_hand is empty
|
|
PersistentWorld.instance.clear_value("player.left_hand")
|
|
|
|
# Handle items held in the players right hand
|
|
var right_pickup := XRToolsFunctionPickup.find_right($XROrigin3D)
|
|
var right_item := _get_held_persistent_item(right_pickup)
|
|
if right_item:
|
|
# The player.right_hand holds the item
|
|
PersistentWorld.instance.set_value("player.right_hand", right_item.item_id)
|
|
else:
|
|
# The player.right_hand is empty
|
|
PersistentWorld.instance.clear_value("player.right_hand")
|
|
|
|
|
|
## Find the [PersistentZone] containing a given node
|
|
static func find_instance(node : Node) -> PersistentZone:
|
|
return XRTools.find_xr_ancestor(
|
|
node,
|
|
"*",
|
|
"PersistentZone") as PersistentZone
|
|
|
|
|
|
# Create a [PersistentItem] from its [param item_id]. This is used when
|
|
# loading a scene that contains an item carried by the user from a different
|
|
# scene.
|
|
func create_item_instance(item_id : String) -> PersistentItem:
|
|
# Get the items state information
|
|
var state = PersistentWorld.instance.get_value(item_id)
|
|
if not state is Dictionary:
|
|
push_warning("Item %s not in world-data" % item_id)
|
|
return null
|
|
|
|
# Get the items type_id
|
|
var item_type_id = state.get("type")
|
|
if not item_type_id is String:
|
|
push_warning("Item %s does not define type" % item_id)
|
|
return null
|
|
|
|
# Get the PersistentItemType
|
|
var item_type := PersistentWorld.instance.item_database.get_type(item_type_id)
|
|
if not item_type:
|
|
push_warning("Item type %s not in database" % item_type_id)
|
|
return null
|
|
|
|
# Load the item scene
|
|
var item_scene : PackedScene = load(item_type.instance_scene)
|
|
if not item_scene:
|
|
push_warning("Item scene %s not valid" % item_type.instance_scene)
|
|
return null
|
|
|
|
# Construct the item
|
|
var item : PersistentItem = item_scene.instantiate()
|
|
if not item:
|
|
push_warning("Item scene %s not valid" % item_type.instance_scene)
|
|
return null
|
|
|
|
# Initialize the item
|
|
item.item_id = item_id
|
|
item.item_type = item_type
|
|
item.propagate_notification(Persistent.NOTIFICATION_LOAD_STATE)
|
|
add_child(item)
|
|
return item
|
|
|
|
|
|
# This method returns true if the node is an item held by a zone rather than
|
|
# being held by some sort of persistent object such as a PersistentPocket or
|
|
# an XRToolsFunctionPickup.
|
|
static func is_item_held_by_zone(node : Node) -> bool:
|
|
# Skip if not valid
|
|
if not is_instance_valid(node):
|
|
return false
|
|
|
|
# Skip if not an PersistentItem
|
|
if not node is PersistentItem:
|
|
return false
|
|
|
|
# If the node isn't held by anything valid then it's held by the zone
|
|
if not is_instance_valid(node.get_picked_up_by()):
|
|
return true
|
|
|
|
# If held by a PersistentPocket then it's not held by the zone
|
|
if node.get_picked_up_by() is PersistentPocket:
|
|
return false
|
|
|
|
# If held by an XRToolsFunctionPickup the it's not held by the zone
|
|
if node.get_picked_up_by() is XRToolsFunctionPickup:
|
|
return false
|
|
|
|
# Node is held by a non-persistent mechanism in the zone
|
|
push_warning("Item ", node.item_id, " held by non-persistent ", node.get_picked_up_by())
|
|
return true
|
|
|
|
|
|
# This method returns the persistent item primarily held by the pickup
|
|
static func _get_held_persistent_item(pickup : XRToolsFunctionPickup) -> PersistentItem:
|
|
# Fail if no pickup
|
|
if not is_instance_valid(pickup):
|
|
return null
|
|
|
|
# Fail if item is not a PersistentItem
|
|
var item := pickup.picked_up_object as PersistentItem
|
|
if not item:
|
|
return null
|
|
|
|
# Fail if not active pickup, but merely second-hand grab
|
|
if item.get_picked_up_by() != pickup:
|
|
return null
|
|
|
|
# Return the item
|
|
return item
|