First commit 🎉
This commit is contained in:
commit
43ea213f9b
728 changed files with 37080 additions and 0 deletions
533
addons/popochiu/engine/objects/room/popochiu_room.gd
Normal file
533
addons/popochiu/engine/objects/room/popochiu_room.gd
Normal file
|
@ -0,0 +1,533 @@
|
|||
@tool
|
||||
@icon("res://addons/popochiu/icons/room.png")
|
||||
class_name PopochiuRoom
|
||||
extends Node2D
|
||||
## Each scene of the game. Is composed by Props, Hotspots, Regions, Markers, Walkable areas, and
|
||||
## Characters.
|
||||
##
|
||||
## Characters can move through it in the spaces defined by walkable areas, interact with its props
|
||||
## and hotspots, react to its regions, and move to its markers.
|
||||
|
||||
## The identifier of the object used in scripts.
|
||||
@export var script_name := ""
|
||||
## Whether this room should add the Player-controlled Character (PC) to its [b]$Characters[/b] node
|
||||
## when the room is loaded.
|
||||
@export var has_player := true
|
||||
## If [code]true[/code] the whole GUI will be hidden when the room is loaded. Useful for cutscenes,
|
||||
## splash screens and when showing game menus or popups.
|
||||
@export var hide_gui := false
|
||||
@export_category("Room size")
|
||||
## Defines the room's width. If this exceeds from the project's viewport width, this value is used
|
||||
## to calculate the camera limits, ensuring it follows the player as they move within the room.
|
||||
@export var width: int = 0
|
||||
## Defines the room's height. If this exceeds from the project's viewport height, this value is used
|
||||
## to calculate the camera limits, ensuring it follows the player as they move within the room.
|
||||
@export var height: int = 0
|
||||
## @deprecated
|
||||
@export_category("Camera limits")
|
||||
## @deprecated
|
||||
## If this different from [constant INF], the value will define the left limit of the camera
|
||||
## relative to the native game resolution. I.e. if your native game resolution is 320x180, and the
|
||||
## background (size) of the room is 448x180, the left limit of the camera should be -64 (this is the
|
||||
## difference between 320 and 448).
|
||||
## [br][br][i]Set this on rooms that are bigger than the native game resolution so the camera will
|
||||
## follow the character.[/i]
|
||||
@export var limit_left := INF
|
||||
## @deprecated
|
||||
## If this different from [constant INF], the value will define the right limit of the camera
|
||||
## relative to the native game resolution. I.e. if your native game resolution is 320x180, and the
|
||||
## background (size) of the room is 448x180, the right limit of the camera should be 384 (320 + 64
|
||||
## (this is the difference between 320 and 448)).
|
||||
## [br][br][i]Set this on rooms that are bigger than the native game resolution so the camera will
|
||||
## follow the character.[/i]
|
||||
@export var limit_right := INF
|
||||
## @deprecated
|
||||
## If this different from [constant INF], the value will define the top limit of the camera
|
||||
## relative to the native game resolution.
|
||||
## [br][br][i]Set this on rooms that are bigger than the native game resolution so the camera will
|
||||
## follow the character.[/i]
|
||||
@export var limit_top := INF
|
||||
## @deprecated
|
||||
## If this different from [constant INF], the value will define the bottom limit of the camera
|
||||
## relative to the native game resolution.
|
||||
## [br][br][i]Set this on rooms that are bigger than the native game resolution so the camera will
|
||||
## follow the character.[/i]
|
||||
@export var limit_bottom := INF
|
||||
# This category is used by the Aseprite Importer in order to allow the creation of a section in the
|
||||
# Inspector for it.
|
||||
@export_category("Aseprite")
|
||||
|
||||
## Whether this is the room in which players are. When [code]true[/code], the room starts processing
|
||||
## unhandled inputs.
|
||||
var is_current := false : set = set_is_current
|
||||
|
||||
var _nav_path: PopochiuWalkableArea = null
|
||||
# It contains the information of the characters moving around the room. Each entry has the form:
|
||||
# PopochiuCharacter.ID: int = {
|
||||
# character: PopochiuCharacter,
|
||||
# path: PackedVector2Array
|
||||
# }
|
||||
var _moving_characters := {}
|
||||
# Stores the children defined in the Editor"s Scene tree for each character inside $Characters to
|
||||
# add them to the corresponding PopochiuCharacter instance when the room is loaded in runtime.
|
||||
var _characters_children := {}
|
||||
|
||||
|
||||
#region Godot ######################################################################################
|
||||
func _enter_tree() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
y_sort_enabled = false
|
||||
$Props.y_sort_enabled = false
|
||||
$Characters.y_sort_enabled = false
|
||||
|
||||
if width == 0:
|
||||
width = ProjectSettings.get_setting(PopochiuResources.DISPLAY_WIDTH)
|
||||
if height == 0:
|
||||
height = ProjectSettings.get_setting(PopochiuResources.DISPLAY_HEIGHT)
|
||||
|
||||
return
|
||||
else:
|
||||
y_sort_enabled = true
|
||||
$Props.y_sort_enabled = true
|
||||
$Characters.y_sort_enabled = true
|
||||
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint(): return
|
||||
|
||||
if not get_tree().get_nodes_in_group("walkable_areas").is_empty():
|
||||
_nav_path = get_tree().get_nodes_in_group("walkable_areas")[0]
|
||||
NavigationServer2D.map_set_active(_nav_path.map_rid, true)
|
||||
|
||||
set_process_unhandled_input(false)
|
||||
set_physics_process(false)
|
||||
|
||||
# Connect to singletons signals
|
||||
PopochiuUtils.g.blocked.connect(_on_gui_blocked)
|
||||
PopochiuUtils.g.unblocked.connect(_on_gui_unblocked)
|
||||
|
||||
PopochiuUtils.r.room_readied(self)
|
||||
|
||||
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
return [
|
||||
{
|
||||
name = "popochiu_placeholder",
|
||||
type = TYPE_NIL,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
func _physics_process(delta):
|
||||
if _moving_characters.is_empty(): return
|
||||
|
||||
for character_id in _moving_characters:
|
||||
var moving_character_data: Dictionary = _moving_characters[character_id]
|
||||
var walk_distance = (
|
||||
moving_character_data.character as PopochiuCharacter
|
||||
).walk_speed * delta
|
||||
|
||||
_move_along_path(walk_distance, moving_character_data)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent):
|
||||
if (
|
||||
not PopochiuUtils.get_click_or_touch_index(event) in [
|
||||
MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT
|
||||
]
|
||||
or (not event is InputEventScreenTouch and PopochiuUtils.e.hovered)
|
||||
):
|
||||
return
|
||||
|
||||
# Fix #224 Item should be removed only if the click was done anywhere in the room when the
|
||||
# cursor is not hovering another object
|
||||
if PopochiuUtils.i.active:
|
||||
# Wait so PopochiuClickable can handle the interaction
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
|
||||
PopochiuUtils.i.set_active_item()
|
||||
return
|
||||
|
||||
if has_player and is_instance_valid(PopochiuUtils.c.player) and PopochiuUtils.c.player.can_move:
|
||||
# Set this property to null in order to cancel any running interaction with a
|
||||
# PopochiuClickable (check PopochiuCharacter.walk_to_clicked(...))
|
||||
PopochiuUtils.e.clicked = null
|
||||
|
||||
if PopochiuUtils.c.player.is_moving:
|
||||
PopochiuUtils.c.player.move_ended.emit()
|
||||
|
||||
PopochiuUtils.c.player.walk(get_local_mouse_position())
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when Popochiu loads the room. At this point the room is in the tree but it is not visible.
|
||||
func _on_room_entered() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called when the room-changing transition finishes. At this point the room is visible.
|
||||
func _on_room_transition_finished() -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Called before Popochiu unloads the room. At this point the room is in the tree but it is not
|
||||
## visible, it is not processing inputs, and has no childrens in the [b]$Characters[/b] node.
|
||||
func _on_room_exited() -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Called by Popochiu before moving the Player-controlled Character (PC) to another room.
|
||||
## By default, characters are only removed (not deleted) to keep their instances in memory.
|
||||
func exit_room() -> void:
|
||||
set_physics_process(false)
|
||||
|
||||
for c in $Characters.get_children():
|
||||
c.position_stored = null
|
||||
|
||||
for character_child: Node in c.get_children():
|
||||
if character_child.owner != c:
|
||||
character_child.queue_free()
|
||||
|
||||
$Characters.remove_child(c)
|
||||
|
||||
_on_room_exited()
|
||||
|
||||
|
||||
## Adds the instance (in memory) of [param chr] to the [b]$Characters[/b] node and connects to its
|
||||
## [signal PopochiuCharacter.started_walk_to] and [signal PopochiuCharacter.stopped_walk] signals.
|
||||
## It also adds to it any children of the character in the Editor"s Scene tree. The [b]idle[/b]
|
||||
## animation is triggered.
|
||||
func add_character(chr: PopochiuCharacter) -> void:
|
||||
$Characters.add_child(chr)
|
||||
|
||||
# Fix #191 by checking if the character had children defined in the Room's Scene (Editor)
|
||||
if _characters_children.has(chr.script_name):
|
||||
# Add child nodes (defined in the Scene tree of the room) to the instance of the character
|
||||
for child: Node in _characters_children[chr.script_name]:
|
||||
chr.add_child(child)
|
||||
|
||||
#warning-ignore:return_value_discarded
|
||||
chr.started_walk_to.connect(_update_navigation_path)
|
||||
chr.stopped_walk.connect(_clear_navigation_path.bind(chr))
|
||||
|
||||
update_characters_position(chr)
|
||||
|
||||
# Fix #385: Ignore character following if the follower is the same as the player-controlled
|
||||
# character.
|
||||
if chr.follow_player and chr != PopochiuUtils.c.player:
|
||||
PopochiuUtils.c.player.started_walk_to.connect(_follow_player.bind(chr))
|
||||
|
||||
chr.idle()
|
||||
|
||||
|
||||
## Removes [param chr] the [b]$Characters[/b] node without destroying it.
|
||||
func remove_character(chr: PopochiuCharacter) -> void:
|
||||
$Characters.remove_child(chr)
|
||||
|
||||
|
||||
## Hides all its [PopochiuProp]s.
|
||||
func hide_props() -> void:
|
||||
for prop: PopochiuProp in get_props():
|
||||
prop.hide()
|
||||
|
||||
|
||||
## Checks if the [PopochiuCharacter], whose property [member PopochiuCharacter.script_name] matches
|
||||
## [param character_name], is inside the [b]$Characters[/b] node.
|
||||
func has_character(character_name: String) -> bool:
|
||||
var result := false
|
||||
|
||||
for c in $Characters.get_children():
|
||||
if (c as PopochiuCharacter).script_name == character_name:
|
||||
result = true
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
|
||||
## Called by Popochiu when loading the room to assign its camera limits to the player camera.
|
||||
func setup_camera() -> void:
|
||||
if width > 0 and width > PopochiuUtils.e.width:
|
||||
var h_diff: int = (PopochiuUtils.e.width - width) / 2
|
||||
PopochiuUtils.e.camera.limit_left = h_diff
|
||||
PopochiuUtils.e.camera.limit_right = PopochiuUtils.e.width - h_diff
|
||||
if height > 0 and height > PopochiuUtils.e.height:
|
||||
var v_diff: int = (PopochiuUtils.e.height - height) / 2
|
||||
PopochiuUtils.e.camera.limit_top = -v_diff
|
||||
PopochiuUtils.e.camera.limit_bottom = PopochiuUtils.e.height - v_diff
|
||||
|
||||
|
||||
## Remove all children from the [b]$Characters[/b] node, storing the children of each node to later
|
||||
## assign them to the corresponding [PopochiuCharacter] when the room is loaded.
|
||||
func clean_characters() -> void:
|
||||
for c in $Characters.get_children():
|
||||
if not c is PopochiuCharacter: continue
|
||||
|
||||
_characters_children[c.script_name] = []
|
||||
|
||||
for character_child: Node in c.get_children():
|
||||
if not character_child.owner == self: continue
|
||||
|
||||
c.remove_child(character_child)
|
||||
_characters_children[c.script_name].append(character_child)
|
||||
|
||||
c.queue_free()
|
||||
|
||||
|
||||
## Updates the position of [param character] in the room, and then updates its scale.
|
||||
func update_characters_position(character: PopochiuCharacter):
|
||||
character.update_position()
|
||||
character.update_scale()
|
||||
|
||||
|
||||
## Returns the [Marker2D] which [member Node.name] matches [param marker_name].
|
||||
func get_marker(marker_name: String) -> Marker2D:
|
||||
var marker: Marker2D = get_node_or_null("Markers/" + marker_name)
|
||||
if marker:
|
||||
return marker
|
||||
PopochiuUtils.print_error("Marker %s not found" % marker_name)
|
||||
return null
|
||||
|
||||
|
||||
## Returns the [b]global position[/b] of the [Marker2D] which [member Node.name] matches
|
||||
## [param marker_name].
|
||||
func get_marker_position(marker_name: String) -> Vector2:
|
||||
var marker := get_marker(marker_name)
|
||||
return marker.global_position if marker != null else Vector2.ZERO
|
||||
|
||||
|
||||
## Returns the [PopochiuProp] which [member PopochiuClickable.script_name] matches
|
||||
## [param prop_name].
|
||||
func get_prop(prop_name: String) -> PopochiuProp:
|
||||
for p in get_tree().get_nodes_in_group("props"):
|
||||
if p.script_name == prop_name or p.name == prop_name:
|
||||
return p as PopochiuProp
|
||||
PopochiuUtils.print_error("Prop %s not found" % prop_name)
|
||||
return null
|
||||
|
||||
|
||||
## Returns the [PopochiuHotspot] which [member PopochiuClickable.script_name] matches
|
||||
## [param hotspot_name].
|
||||
func get_hotspot(hotspot_name: String) -> PopochiuHotspot:
|
||||
for h in get_tree().get_nodes_in_group("hotspots"):
|
||||
if h.script_name == hotspot_name or h.name == hotspot_name:
|
||||
return h
|
||||
PopochiuUtils.print_error("Hotspot %s not found" % hotspot_name)
|
||||
return null
|
||||
|
||||
|
||||
## Returns the [PopochiuRegion] which [member PopochiuRegion.script_name] matches
|
||||
## [param region_name].
|
||||
func get_region(region_name: String) -> PopochiuRegion:
|
||||
for r in get_tree().get_nodes_in_group("regions"):
|
||||
if r.script_name == region_name or r.name == region_name:
|
||||
return r
|
||||
PopochiuUtils.print_error("Region %s not found" % region_name)
|
||||
return null
|
||||
|
||||
|
||||
## Returns the [PopochiuWalkableArea] which [member PopochiuWalkableArea.script_name] matches
|
||||
## [param walkable_area_name].
|
||||
func get_walkable_area(walkable_area_name: String) -> PopochiuWalkableArea:
|
||||
for wa in get_tree().get_nodes_in_group("walkable_areas"):
|
||||
if wa.name == walkable_area_name:
|
||||
return wa
|
||||
PopochiuUtils.print_error("Walkable area %s not found" % walkable_area_name)
|
||||
return null
|
||||
|
||||
|
||||
## Returns all the [PopochiuProp]s in the room.
|
||||
func get_props() -> Array:
|
||||
return get_tree().get_nodes_in_group("props")
|
||||
|
||||
|
||||
## Returns all the [PopochiuHotspot]s in the room.
|
||||
func get_hotspots() -> Array:
|
||||
return get_tree().get_nodes_in_group("hotspots")
|
||||
|
||||
|
||||
## Returns all the [PopochiuRegion]s in the room.
|
||||
func get_regions() -> Array:
|
||||
return get_tree().get_nodes_in_group("regions")
|
||||
|
||||
|
||||
## Returns all the [Marker2D]s in the room.
|
||||
func get_markers() -> Array:
|
||||
return $Markers.get_children()
|
||||
|
||||
|
||||
## Returns all the [PopochiuWalkableArea]s in the room.
|
||||
func get_walkable_areas() -> Array:
|
||||
return get_tree().get_nodes_in_group("walkable_areas")
|
||||
|
||||
|
||||
## Returns the current active [PopochiuWalkableArea].
|
||||
func get_active_walkable_area() -> PopochiuWalkableArea:
|
||||
return _nav_path
|
||||
|
||||
|
||||
## Returns the [member PopochiuWalkableArea.script_name] of current active [PopochiuWalkableArea].
|
||||
func get_active_walkable_area_name() -> String:
|
||||
return _nav_path.script_name
|
||||
|
||||
|
||||
## Returns all the [PopochiuCharacter]s in the room.
|
||||
func get_characters() -> Array:
|
||||
var characters := []
|
||||
|
||||
for c in $Characters.get_children():
|
||||
if c is PopochiuCharacter:
|
||||
characters.append(c)
|
||||
|
||||
return characters
|
||||
|
||||
|
||||
## Returns the number of characters in the room.
|
||||
func get_characters_count() -> int:
|
||||
return $Characters.get_child_count()
|
||||
|
||||
|
||||
## Sets as active the [PopochiuWalkableArea] which [member Node.name] matches
|
||||
## [param walkable_area_name].
|
||||
func set_active_walkable_area(walkable_area_name: String) -> void:
|
||||
var active_walkable_area = $WalkableAreas.get_node(walkable_area_name)
|
||||
if active_walkable_area != null:
|
||||
_nav_path = active_walkable_area
|
||||
else:
|
||||
PopochiuUtils.print_error("Can't set %s as active walkable area" % walkable_area_name)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region SetGet #####################################################################################
|
||||
func set_is_current(value: bool) -> void:
|
||||
is_current = value
|
||||
set_process_unhandled_input(is_current)
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _on_gui_blocked() -> void:
|
||||
set_process_unhandled_input(false)
|
||||
|
||||
|
||||
func _on_gui_unblocked() -> void:
|
||||
set_process_unhandled_input(true)
|
||||
|
||||
|
||||
func _move_along_path(distance_to_move: float, moving_character_data: Dictionary):
|
||||
var last_character_position: Vector2 =(
|
||||
moving_character_data.character.position_stored
|
||||
if moving_character_data.character.position_stored
|
||||
else moving_character_data.character.position
|
||||
)
|
||||
|
||||
while moving_character_data.path.size():
|
||||
var distance_to_next_navigation_point = last_character_position.distance_to(
|
||||
moving_character_data.path[0]
|
||||
)
|
||||
|
||||
# The character haven't reached the next navigation point so we update
|
||||
# its position along the line between the last and the next navigation point
|
||||
if distance_to_move <= distance_to_next_navigation_point:
|
||||
moving_character_data.character.turn_towards(moving_character_data.path[0])
|
||||
var next_position = last_character_position.lerp(
|
||||
moving_character_data.path[0], distance_to_move / distance_to_next_navigation_point
|
||||
)
|
||||
if moving_character_data.character.anti_glide_animation:
|
||||
moving_character_data.character.position_stored = next_position
|
||||
else:
|
||||
moving_character_data.character.position = next_position
|
||||
# Scale the character depending on the new position
|
||||
moving_character_data.character.update_scale()
|
||||
# We are still walking towards the next navigation point
|
||||
# so we don't need to update the path information
|
||||
return
|
||||
|
||||
# We reached the next navigation point
|
||||
# Remove the last navigation point from the path
|
||||
# and recalculate the distance to the next one
|
||||
distance_to_move -= distance_to_next_navigation_point
|
||||
last_character_position = moving_character_data.path[0]
|
||||
moving_character_data.path.remove_at(0)
|
||||
|
||||
|
||||
moving_character_data.character.position = last_character_position
|
||||
moving_character_data.character.update_scale()
|
||||
_clear_navigation_path(moving_character_data.character)
|
||||
|
||||
|
||||
func _update_navigation_path(
|
||||
character: PopochiuCharacter, start_position: Vector2, end_position: Vector2
|
||||
):
|
||||
if not _nav_path:
|
||||
PopochiuUtils.print_error("No walkable areas in this room")
|
||||
return
|
||||
|
||||
_moving_characters[character.get_instance_id()] = {}
|
||||
var moving_character_data: Dictionary = _moving_characters[character.get_instance_id()]
|
||||
moving_character_data.character = character
|
||||
|
||||
# TODO: Use a Dictionary so more than one character can move around at the
|
||||
# same time. Or maybe each character should handle its own movement? (;¬_¬)
|
||||
if character.ignore_walkable_areas:
|
||||
# if the character can ignore WAs, just move over a straight line
|
||||
moving_character_data.path = PackedVector2Array([start_position, end_position])
|
||||
else:
|
||||
# if the character is forced into WAs, delegate pathfinding to the active WA
|
||||
moving_character_data.path = NavigationServer2D.map_get_path(
|
||||
_nav_path.map_rid, start_position, end_position, true
|
||||
)
|
||||
|
||||
# TODO: Use NavigationAgent2D target_location and get_next_location() to
|
||||
# maybe improve characters movement with obstacles avoidance?
|
||||
#NavigationServer2D.agent_set_map(character.agent.get_rid(), _nav_path.map_rid)
|
||||
#character.agent.target_location = end_position
|
||||
#_path = character.agent.get_nav_path()
|
||||
#set_physics_process(true)
|
||||
#return
|
||||
|
||||
if moving_character_data.path.is_empty():
|
||||
return
|
||||
|
||||
# If the path is not empty it has at least two points: the start and the end
|
||||
# so we can safely say index 1 is available.
|
||||
# The character should face the direction of the next point in the path, then...
|
||||
character.face_direction(moving_character_data.path[1])
|
||||
# ... we remove the first point of the path since it is the character's current position
|
||||
moving_character_data.path.remove_at(0)
|
||||
|
||||
set_physics_process(true)
|
||||
|
||||
|
||||
func _clear_navigation_path(character: PopochiuCharacter) -> void:
|
||||
# INFO: fixes "function signature mismatch in Web export" error thrown when clearing an empty
|
||||
# Array
|
||||
if not _moving_characters.has(character.get_instance_id()):
|
||||
return
|
||||
|
||||
_moving_characters.erase(character.get_instance_id())
|
||||
character.idle()
|
||||
character.move_ended.emit()
|
||||
|
||||
|
||||
func _follow_player(
|
||||
character: PopochiuCharacter,
|
||||
start_position: Vector2,
|
||||
end_position: Vector2,
|
||||
follower: PopochiuCharacter
|
||||
):
|
||||
var follower_end_position := Vector2.ZERO
|
||||
if end_position.x > follower.position.x:
|
||||
follower_end_position = end_position - follower.follow_player_offset
|
||||
else:
|
||||
follower_end_position = end_position + follower.follow_player_offset
|
||||
follower.walk_to(follower_end_position)
|
||||
|
||||
|
||||
#endregion
|
1
addons/popochiu/engine/objects/room/popochiu_room.gd.uid
Normal file
1
addons/popochiu/engine/objects/room/popochiu_room.gd.uid
Normal file
|
@ -0,0 +1 @@
|
|||
uid://dd3vd1i10q2fr
|
19
addons/popochiu/engine/objects/room/popochiu_room.tscn
Normal file
19
addons/popochiu/engine/objects/room/popochiu_room.tscn
Normal file
|
@ -0,0 +1,19 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://cfml3lrhiixmi"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/popochiu/engine/objects/room/popochiu_room.gd" id="1"]
|
||||
|
||||
[node name="Room" type="Node2D"]
|
||||
script = ExtResource("1")
|
||||
popochiu_placeholder = null
|
||||
|
||||
[node name="WalkableAreas" type="Node2D" parent="."]
|
||||
|
||||
[node name="Props" type="Node2D" parent="."]
|
||||
|
||||
[node name="Hotspots" type="Node2D" parent="."]
|
||||
|
||||
[node name="Regions" type="Node2D" parent="."]
|
||||
|
||||
[node name="Markers" type="Node2D" parent="."]
|
||||
|
||||
[node name="Characters" type="Node2D" parent="."]
|
188
addons/popochiu/engine/objects/room/popochiu_room_data.gd
Normal file
188
addons/popochiu/engine/objects/room/popochiu_room_data.gd
Normal file
|
@ -0,0 +1,188 @@
|
|||
@icon("res://addons/popochiu/icons/room.png")
|
||||
class_name PopochiuRoomData
|
||||
extends Resource
|
||||
## This class is used to store information when saving and loading the game. It also ensures that
|
||||
## the data remains throughout the game's execution.
|
||||
##
|
||||
## It also has data of the [PopochiuProp]s, [PopochiuHotspot]s, [PopochiuWalkableArea]s,
|
||||
## [PopochiuRegion]s, and [PopochiuCharacter]s in a [PopochiuRoom].
|
||||
|
||||
## The identifier of the object used in scripts.
|
||||
@export var script_name := ""
|
||||
## The path to the scene file to be used when adding the character to the game during runtime.
|
||||
@export_file("*.tscn") var scene := ""
|
||||
## Whether the room was already visited by the player.
|
||||
@export var visited := false
|
||||
## Whether this is the first time the player visits the room.
|
||||
@export var visited_first_time := false
|
||||
## The number of times the player has visited this room.
|
||||
@export var visited_times := 0
|
||||
|
||||
## Stores data about the [PopochiuProp]s in the room.
|
||||
var props := {}
|
||||
## Stores data about the [PopochiuHotspot]s in the room.
|
||||
var hotspots := {}
|
||||
## Stores data about the [PopochiuWalkableArea]s in the room.
|
||||
var walkable_areas := {}
|
||||
## Stores data about the [PopochiuRegion]s in the room.
|
||||
var regions := {}
|
||||
## Stores data about the [PopochiuCharacter]s in the room. To see the stored data by default, check
|
||||
## [method save_characters].
|
||||
var characters := {}
|
||||
|
||||
|
||||
#region Virtual ####################################################################################
|
||||
## Called when the game is saved.
|
||||
## [i]Virtual[/i].
|
||||
func _on_save() -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
## Called when the game is loaded. The structure of [param data] is the same returned by
|
||||
## [method _on_save].
|
||||
## [i]Virtual[/i].
|
||||
func _on_load(_data: Dictionary) -> void:
|
||||
pass
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public #####################################################################################
|
||||
## Use this to store custom data when saving the game. The returned [Dictionary] must contain only
|
||||
## JSON supported types: [bool], [int], [float], [String].
|
||||
func on_save() -> Dictionary:
|
||||
return _on_save()
|
||||
|
||||
|
||||
## Called when the game is loaded. [param data] will have the same structure you defined for the
|
||||
## returned [Dictionary] by [method _on_save].
|
||||
func on_load(data: Dictionary) -> void:
|
||||
_on_load(data)
|
||||
|
||||
|
||||
## Stores the data of each of the children inside [b]$WalkableAreas[/b], [b]$Props[/b],
|
||||
## [b]$Hotspots[/b], [b]$Regions[/b], and [b]$Characters[/b].
|
||||
func save_children_states() -> void:
|
||||
if PopochiuUtils.r.current and PopochiuUtils.r.current.state == self:
|
||||
for t in PopochiuResources.ROOM_CHILDREN:
|
||||
for node in PopochiuUtils.r.current.call("get_" + t):
|
||||
if node is PopochiuProp and not node.clickable: continue
|
||||
|
||||
_save_object_state(
|
||||
node,
|
||||
PopochiuResources["%s_IGNORE" % (t as String).to_upper()],
|
||||
get(t)
|
||||
)
|
||||
|
||||
# Save the state of characters
|
||||
save_characters()
|
||||
|
||||
return
|
||||
|
||||
var base_dir := resource_path.get_base_dir()
|
||||
var dependencies_paths: Array = Array(ResourceLoader.get_dependencies(
|
||||
resource_path.replace(".tres", ".tscn")
|
||||
)).map(
|
||||
func (dependency: String) -> String:
|
||||
return dependency.get_slice("::", 2)
|
||||
)
|
||||
|
||||
for t in PopochiuResources.ROOM_CHILDREN:
|
||||
if (get(t) as Dictionary).is_empty():
|
||||
var category := (t as String).replace(" ", "")
|
||||
var objs_path := "%s/%s" % [base_dir, category]
|
||||
|
||||
var dir := DirAccess.open(objs_path)
|
||||
|
||||
if not dir: continue
|
||||
|
||||
dir.include_hidden = false
|
||||
dir.include_navigational = false
|
||||
|
||||
dir.list_dir_begin()
|
||||
|
||||
var folder_name := dir.get_next()
|
||||
|
||||
while folder_name != "":
|
||||
if dir.current_is_dir():
|
||||
# ---- Fix #320 ----------------------------------------------------------------
|
||||
# Ignore objects that are not part of the dependencies of the room. This is the
|
||||
# scenario where an object from the room was removed from the tree but not from
|
||||
# the file system
|
||||
var scene_path := "%s/%s/%s_%s.tscn" % [
|
||||
objs_path,
|
||||
folder_name,
|
||||
category.trim_suffix("s"),
|
||||
folder_name,
|
||||
]
|
||||
if not scene_path in dependencies_paths:
|
||||
folder_name = dir.get_next()
|
||||
continue
|
||||
# ---------------------------------------------------------------- Fix #320 ----
|
||||
|
||||
var script_path := scene_path.replace("tscn", "gd")
|
||||
if not FileAccess.file_exists(script_path):
|
||||
folder_name = dir.get_next()
|
||||
continue
|
||||
|
||||
var node: Node2D = load(script_path).new()
|
||||
node.script_name = folder_name.to_pascal_case()
|
||||
|
||||
_save_object_state(
|
||||
node,
|
||||
PopochiuResources["%s_IGNORE" % (t as String).to_upper()],
|
||||
get(t)
|
||||
)
|
||||
|
||||
node.free()
|
||||
|
||||
folder_name = dir.get_next()
|
||||
|
||||
|
||||
## Save room data related to the characters in the room. The stored data contains:
|
||||
## [codeblock]{
|
||||
## x = PopochiuCharacter.position.x
|
||||
## y = PopochiuCharacter.position.y
|
||||
## facing = PopochiuCharacter._looking_dir
|
||||
## visible = PopochiuCharacter.visible
|
||||
## modulate = PopochiuCharacter.modulate
|
||||
## self_modulate = PopochiuCharacter.self_modulate
|
||||
## light_mask = PopochiuCharacter.light_mask
|
||||
## }[/codeblock]
|
||||
func save_characters() -> void:
|
||||
for character: PopochiuCharacter in PopochiuUtils.r.current.get_characters():
|
||||
characters[character.script_name] = {
|
||||
x = character.position.x,
|
||||
y = character.position.y,
|
||||
facing = character._looking_dir,
|
||||
visible = character.visible,
|
||||
modulate = character.modulate.to_html(),
|
||||
self_modulate = character.self_modulate.to_html(),
|
||||
light_mask = character.light_mask,
|
||||
baseline = character.baseline,
|
||||
# Store this values using built-in types
|
||||
walk_to_point = {
|
||||
x = character.walk_to_point.x,
|
||||
y = character.walk_to_point.y,
|
||||
},
|
||||
look_at_point = {
|
||||
x = character.look_at_point.x,
|
||||
y = character.look_at_point.y,
|
||||
},
|
||||
# TODO: Store the state of the current animation (and more data if
|
||||
# necessary)
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private ####################################################################################
|
||||
func _save_object_state(node: Node2D, ignore: Array, target: Dictionary) -> void:
|
||||
var state := {}
|
||||
PopochiuResources.store_properties(state, node, ignore)
|
||||
|
||||
# Add the PopochiuProp state to the room's props
|
||||
target[node.script_name] = state
|
||||
|
||||
|
||||
#endregion
|
|
@ -0,0 +1 @@
|
|||
uid://cdp20a2ljso2e
|
Loading…
Add table
Add a link
Reference in a new issue