264 lines
7.7 KiB
GDScript
264 lines
7.7 KiB
GDScript
# Escoria main room handling and scene switcher
|
|
extends Node
|
|
|
|
# This script is basically the scene-switcher.
|
|
|
|
|
|
# Signal sent when the room is loaded and ready.
|
|
signal room_ready
|
|
|
|
|
|
# Global id of the last scene the player was before current scene
|
|
var last_scene_global_id: String
|
|
|
|
# Current scene room being displayed
|
|
var current_scene: Node
|
|
|
|
# Scene that was previously the current scene.
|
|
var previous_scene: Node
|
|
|
|
# The Escoria context currently in wait state
|
|
var wait_level
|
|
|
|
# Reference to the scene transition node
|
|
onready var scene_transition: ESCTransitionPlayer
|
|
|
|
|
|
# Connect the wait timer event
|
|
func _ready() -> void:
|
|
scene_transition = ESCTransitionPlayer.new()
|
|
$layers/curtain.add_child(scene_transition)
|
|
$layers/wait_timer.connect("timeout", self, "_on_wait_finished")
|
|
|
|
|
|
func _exit_tree():
|
|
$layers/curtain.remove_child(scene_transition)
|
|
scene_transition.queue_free()
|
|
|
|
# Set current scene
|
|
#
|
|
# #### Parameters
|
|
#
|
|
# - p_scene: Scene to set
|
|
func set_scene(p_scene: Node) -> void:
|
|
if !p_scene:
|
|
escoria.logger.error(
|
|
self,
|
|
"Can't change to an empty scene. Please specify the scene name."
|
|
)
|
|
|
|
previous_scene = current_scene
|
|
|
|
if is_instance_valid(previous_scene):
|
|
_disable_collisions()
|
|
|
|
if not p_scene.is_inside_tree():
|
|
# Set the scene's visiblity for :setup events if the new room is not the
|
|
# same one as the current room.
|
|
if not _is_same_scene(current_scene, p_scene):
|
|
p_scene.visible = false
|
|
|
|
escoria.object_manager.set_current_room(p_scene)
|
|
add_child(p_scene)
|
|
|
|
# In cases where the room being created doesn't return because of a
|
|
# coroutine, finish_current_scene_init() will already have been called
|
|
# and so we don't want to risk repeating ourselves.
|
|
if p_scene == current_scene:
|
|
return
|
|
|
|
# This actually moves the scene closest to the root node, but will
|
|
# still be drawn behind the next node, which should be the previous
|
|
# room.
|
|
move_child(p_scene, 0)
|
|
|
|
current_scene = p_scene
|
|
|
|
|
|
# Only called by the room manager in the case where it hasn't executed a
|
|
# coroutine prior to calling set_scene_finish().
|
|
#
|
|
# ### Parameters
|
|
#
|
|
# - p_scene: The scene currently being initialized by set_scene.
|
|
func finish_current_scene_init(p_scene: Node) -> void:
|
|
if is_a_parent_of(p_scene):
|
|
move_child(p_scene, 0)
|
|
|
|
current_scene = p_scene
|
|
|
|
|
|
# Completes the room swap and should be called by the room manager at the
|
|
# appropriate time.
|
|
func set_scene_finish() -> void:
|
|
# Final check for the critical game scene's existence.
|
|
check_game_scene_methods()
|
|
|
|
# Make our new scene visible.
|
|
current_scene.visible = true
|
|
|
|
clear_previous_scene()
|
|
emit_signal("room_ready")
|
|
|
|
|
|
|
|
# Cleanup the previous scene if there was one.
|
|
func clear_previous_scene() -> void:
|
|
if previous_scene == null:
|
|
return
|
|
|
|
escoria.action_manager.clear_current_action()
|
|
escoria.action_manager.clear_current_tool()
|
|
|
|
if escoria.game_scene.get_parent() == previous_scene:
|
|
previous_scene.remove_child(escoria.game_scene)
|
|
|
|
previous_scene.visible = false
|
|
previous_scene.get_parent().remove_child(previous_scene)
|
|
|
|
previous_scene.queue_free()
|
|
previous_scene = null
|
|
|
|
|
|
# Triggered, when the wait has finished
|
|
func _on_wait_finished() -> void:
|
|
escoria.esc_level_runner.finished(wait_level)
|
|
|
|
|
|
# Set the camera limits
|
|
#
|
|
# #### Parameters
|
|
#
|
|
# * camera_limits_id: The id of the room's camera limits to set
|
|
# * scene: The scene to set the camera limits for. We make this optional since
|
|
# most times it'll be current_scene that needs setting; however, e.g. when
|
|
# starting up Escoria, we might not have already set the current_scene.
|
|
func set_camera_limits(camera_limit_id: int = 0, scene: Node = current_scene) -> void:
|
|
var limits = {}
|
|
var last_available_camera_limit = scene.camera_limits.size() - 1
|
|
if camera_limit_id > last_available_camera_limit:
|
|
escoria.logger.error(
|
|
self,
|
|
"Camera limit %d requested. Last available camera limit is %d." % [
|
|
camera_limit_id,
|
|
last_available_camera_limit
|
|
]
|
|
)
|
|
var scene_camera_limits = scene.camera_limits[camera_limit_id]
|
|
if scene_camera_limits.size.x == 0 and scene_camera_limits.size.y == 0:
|
|
var area = Rect2()
|
|
for child in scene.get_children():
|
|
if child is ESCBackground:
|
|
area = child.get_full_area_rect2()
|
|
break
|
|
|
|
# if the background is smaller than the viewport, we want the camera
|
|
# to stick centered on the background
|
|
if area.size.x == 0 or area.size.y == 0 \
|
|
or area.size < get_viewport().size:
|
|
escoria.logger.warn(
|
|
self,
|
|
"Defined camera is smaller than the viewport. Using viewport size."
|
|
)
|
|
area.size = get_viewport().size
|
|
|
|
escoria.logger.info(
|
|
self,
|
|
"Setting camera limits from scene " + area
|
|
)
|
|
limits = ESCCameraLimits.new(
|
|
area.position.x,
|
|
area.position.x + area.size.x,
|
|
area.position.y,
|
|
area.position.y + area.size.y
|
|
)
|
|
else:
|
|
limits = ESCCameraLimits.new(
|
|
scene_camera_limits.position.x,
|
|
scene_camera_limits.position.x + \
|
|
scene_camera_limits.size.x,
|
|
scene_camera_limits.position.y,
|
|
scene_camera_limits.position.y + \
|
|
scene_camera_limits.size.y
|
|
)
|
|
escoria.logger.info(
|
|
self,
|
|
"Setting camera limits using configured parameters " + str(scene_camera_limits)
|
|
)
|
|
|
|
escoria.object_manager.get_object(
|
|
escoria.object_manager.CAMERA
|
|
).node.set_limits(limits)
|
|
|
|
|
|
func save_game(p_savegame_res: Resource) -> void:
|
|
p_savegame_res.main = {
|
|
ESCSaveGame.MAIN_LAST_SCENE_GLOBAL_ID_KEY: last_scene_global_id,
|
|
ESCSaveGame.MAIN_CURRENT_SCENE_FILENAME_KEY: current_scene.filename \
|
|
if current_scene != null \
|
|
else "No current scene (not loaded yet)"
|
|
}
|
|
|
|
|
|
# Sanity check that the game.tscn scene's root node script MUST
|
|
# implement the following methods. If they do not exist, stop immediately.
|
|
# Implement them, even if empty
|
|
func check_game_scene_methods():
|
|
assert(current_scene.game.has_method("left_click_on_bg"))
|
|
assert(current_scene.game.has_method("right_click_on_bg"))
|
|
assert(current_scene.game.has_method("left_double_click_on_bg"))
|
|
|
|
assert(current_scene.game.has_method("element_focused"))
|
|
assert(current_scene.game.has_method("element_unfocused"))
|
|
|
|
assert(current_scene.game.has_method("left_click_on_item"))
|
|
assert(current_scene.game.has_method("right_click_on_item"))
|
|
assert(current_scene.game.has_method("left_double_click_on_item"))
|
|
|
|
assert(current_scene.game.has_method("open_inventory"))
|
|
assert(current_scene.game.has_method("close_inventory"))
|
|
|
|
assert(current_scene.game.has_method("left_click_on_inventory_item"))
|
|
assert(current_scene.game.has_method("right_click_on_inventory_item"))
|
|
assert(current_scene.game.has_method("left_double_click_on_inventory_item"))
|
|
|
|
assert(current_scene.game.has_method("inventory_item_focused"))
|
|
assert(current_scene.game.has_method("inventory_item_unfocused"))
|
|
|
|
assert(current_scene.game.has_method("mousewheel_action"))
|
|
|
|
assert(current_scene.game.has_method("hide_ui"))
|
|
assert(current_scene.game.has_method("show_ui"))
|
|
assert(current_scene.game.has_method("escoria_hide_ui"))
|
|
assert(current_scene.game.has_method("escoria_show_ui"))
|
|
assert(current_scene.game.has_method("_on_event_done"))
|
|
|
|
|
|
# Determines whether two scenes represent the same room.
|
|
#
|
|
# ### Parameters
|
|
#
|
|
# - scene_1: Scene to be compared.
|
|
# - scene_2: Other scene to be compared.
|
|
#
|
|
# **Returns** true iff the two scenes represent the same room.
|
|
func _is_same_scene(scene_1: Node, scene_2: Node) -> bool:
|
|
if scene_1 is ESCRoom and scene_2 is ESCRoom:
|
|
return scene_1.global_id == scene_2.global_id
|
|
|
|
return false
|
|
|
|
|
|
# Disable collisions in the previous scene so if we have two scenes in the same
|
|
# game tree, collisions won't result.
|
|
func _disable_collisions() -> void:
|
|
var items_to_disable = previous_scene.get_tree().get_nodes_in_group(ESCItem.GROUP_ITEM_CAN_COLLIDE)
|
|
|
|
for item in items_to_disable:
|
|
if is_instance_valid(item.collision):
|
|
item.collision.disabled = true
|
|
if item is Area2D:
|
|
item.monitoring = false
|
|
item.monitorable = false
|
|
|