diff options
Diffstat (limited to 'addons/escoria-core/game/main.gd')
| -rw-r--r-- | addons/escoria-core/game/main.gd | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/addons/escoria-core/game/main.gd b/addons/escoria-core/game/main.gd new file mode 100644 index 0000000..e9c6f84 --- /dev/null +++ b/addons/escoria-core/game/main.gd @@ -0,0 +1,404 @@ +## Escoria main scene script. +## +## This script is basically the scene-switcher. +extends Node + + +## Signal sent when the room is loaded and ready.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +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.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready() -> void: + scene_transition = ESCTransitionPlayer.new() + $layers/curtain.add_child(scene_transition) + $layers/wait_timer.connect("timeout", Callable(self, "_on_wait_finished")) + + +## Called when the node is removed from the scene tree.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _exit_tree(): + $layers/curtain.remove_child(scene_transition) + scene_transition.queue_free() + +## Sets the current scene[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_scene|`Node`|Scene to set|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +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 + last_scene_global_id = p_scene.global_id + + +## Only called by the room manager in the case where it hasn't executed a coroutine prior to calling set_scene_finish(). ### Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_scene|`Node`|The scene currently being initialized by set_scene.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func finish_current_scene_init(p_scene: Node) -> void: + if is_ancestor_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.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +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() + room_ready.emit() + + +## Cleanup the previous scene if there was one.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +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.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_wait_finished() -> void: + escoria.esc_level_runner.finished(wait_level) + + +## Set the camera limits[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |camera_limit_id|`int`|Index of the camera limit configuration to apply.|no|[br] +## |scene|`Node`|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. camera_limits_id The id of the room's camera limits to set|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +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, + "Camera3D 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 " + str(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) + + +## Save the game state to the provided savegame resource.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_savegame_res|`Resource`|The savegame resource to write to.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +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.scene_file_path \ + 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[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +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.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |scene_1|`Node`|Scene to be compared.|yes|[br] +## |scene_2|`Node`|Other scene to be compared.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns true iff the two scenes represent the same room. (`bool`) +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.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _disable_collisions() -> void: + var items_to_disable = previous_scene.get_tree().get_nodes_in_group(escoria.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 + +# ################################################################### +# Facades for current_scene + +## Hide the UI of the current scene.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func hide_ui() -> void: + if escoria.main.current_scene != null: + escoria.main.current_scene.game.hide_ui() + +## Hide the current scene.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func hide_current_scene() -> void: + if escoria.main.current_scene != null: + escoria.main.current_scene.hide() + +## Show the UI of the current scene.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func show_ui() -> void: + if escoria.main.current_scene != null: + escoria.main.current_scene.game.show_ui() + +## Show the current scene.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func show_current_scene() -> void: + if escoria.main.current_scene != null: + escoria.main.current_scene.show() |
