diff options
Diffstat (limited to 'addons/escoria-core/game/escoria.gd')
| -rw-r--r-- | addons/escoria-core/game/escoria.gd | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/addons/escoria-core/game/escoria.gd b/addons/escoria-core/game/escoria.gd new file mode 100644 index 0000000..9da2e9f --- /dev/null +++ b/addons/escoria-core/game/escoria.gd @@ -0,0 +1,313 @@ +@tool +extends Node +class_name Escoria +## The main Escoria script. + + +## Signal sent when pause menu has to be displayed[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal request_pause_menu + + +## Name of the Escoria core plugin +const ESCORIA_CORE_PLUGIN_NAME: String = "escoria-core" + + +## The main scene +@onready var main = $main + + +# Called by Save Manager signal "game_is_loading" when a savegame is being loaded. +func _on_game_is_loading(): + escoria.logger.info(self, "SAVEGAME IS LOADING") + + +# Called by Save Manager signal "game_finished_loading" when a savegame has loaded. +func _on_game_finished_loading(): + escoria.logger.info(self, "SAVEGAME FINISHED LOADING") + + +# Constructor. Inits all Escoria's managers accessible from `escoria` Godot global. +func _init(): + escoria.inventory_manager = ESCInventoryManager.new() + escoria.action_manager = ESCActionManager.new() + escoria.event_manager = ESCEventManager.new() + escoria.globals_manager = ESCGlobalsManager.new() + add_child(escoria.event_manager) + escoria.object_manager = ESCObjectManager.new() + escoria.command_registry = ESCCommandRegistry.new() + escoria.resource_cache = ESCResourceCache.new() + escoria.save_manager = ESCSaveManager.new() + + escoria.save_manager.game_is_loading.connect(_on_game_is_loading) + escoria.save_manager.game_finished_loading.connect(_on_game_finished_loading) + + escoria.inputs_manager = ESCInputsManager.new() + escoria.settings_manager = ESCSettingsManager.new() + escoria.interpreter_factory = preload("res://addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd").new() + + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_SCENE + ).is_empty(): + escoria.logger.error( + self, + "Project setting '%s' is not set!" % ESCProjectSettingsManager.GAME_SCENE + ) + else: + escoria.game_scene = escoria.resource_cache.get_resource( + ESCProjectSettingsManager.get_setting(ESCProjectSettingsManager.GAME_SCENE) + ).instantiate() + + +# Ready method. Loads settings and Escoria's start script (ESC or ASH). +func _ready(): + add_child(escoria.resource_cache) + + _handle_direct_scene_run() + + escoria.settings_manager.load_settings() + escoria.settings_manager.apply_settings() + + escoria.room_manager.register_reserved_globals() + escoria.inputs_manager.register_core() + + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_START_SCRIPT + ).is_empty(): + escoria.logger.error( + self, + "Project setting '%s' is not set!" + % ESCProjectSettingsManager.GAME_START_SCRIPT + ) + escoria.start_script = escoria.esc_compiler.load_esc_file( + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_START_SCRIPT + ) + ) + + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.ACTION_DEFAULT_SCRIPT + ).is_empty(): + escoria.logger.info( + self, + "Project setting '%s' is not set. No action defaults will be used." + % ESCProjectSettingsManager.ACTION_DEFAULT_SCRIPT + ) + else: + escoria.action_default_script = escoria.esc_compiler.load_esc_file( + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.ACTION_DEFAULT_SCRIPT + ) + ) + + escoria.main = main + _perform_plugins_checks() + + +## Verifies that the game is configured with required plugin(s). If a required plugin is missing (or disabled) we stop immediately.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _perform_plugins_checks(): + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DIALOG_MANAGERS + ).is_empty(): + escoria.logger.error( + self, + "No dialog manager configured. Please add a dialog manager plugin." + ) + + +# Manage notifications received from OS. +# +# #### Parameters +# - what: the notification constant received (usually defined in MainLoop) +func _notification(what: int): + match what: + NOTIFICATION_WM_CLOSE_REQUEST, NOTIFICATION_WM_GO_BACK_REQUEST: + escoria.logger.close_logs() + escoria.is_quitting = true + get_tree().quit() + + +## Initializer called by Escoria's main_scene as very very first event EVER. Usually you'll want to show some logos animations before spawning the main menu in the escoria/main/game_start_script 's `:init` event[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func init(): + # Don't show the UI until we're ready in order to avoid a sometimes-noticeable + # blink. The UI will be "shown" later via a visibility update to the first room. + escoria.game_scene.escoria_hide_ui() + run_event_from_script(escoria.start_script, escoria.event_manager.EVENT_INIT) + + +## Input function to manage specific input keys.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`InputEvent`|The input event to manage.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _input(event: InputEvent): + if InputMap.has_action(ESCInputsManager.ESC_SHOW_DEBUG_PROMPT) \ + and event.is_action_pressed(ESCInputsManager.ESC_SHOW_DEBUG_PROMPT): + main.get_node("layers/debug_layer/esc_prompt_popup").popup() + + if event.is_action_pressed("ui_cancel"): + request_pause_menu.emit() + pass + + +## Runs the event "event_name" from the "script" ESC script.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |script|`ESCScript`|ESC script containing the event to run. The script must have been loaded.|yes|[br] +## |event_name|`String`|Name of the event to run|yes|[br] +## |from_statement_id|`int`|Statement id to start from (default 0)|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns Nothing. Waits for the event to finish before returning. (`Variant`) +func run_event_from_script(script: ESCScript, event_name: String, from_statement_id: int = 0): + if script == null: + escoria.logger.error( + self, + "Requested event %s on unloaded script %s." % [event_name, script] + + "Please load the ESC script using esc_compiler.load_esc_file()." + ) + + if not _event_exists_in_script(script, event_name): + return + + escoria.event_manager.queue_event(script.events[event_name]) + var rc = await escoria.event_manager.event_finished + while rc[1] != event_name: + rc = await escoria.event_manager.event_finished + + if rc[0] != ESCExecution.RC_OK: + escoria.logger.error( + self, + "Start event of the start script returned unsuccessful: %d." % rc[0] + ) + return + + +## Checks for the existence of both mandatory and optional events within a specified script.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |script|`ESCScript`|The script in which to check for the existence of the given event.|yes|[br] +## |event_name|`String`|The name of the event to check for inside the given script.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True iff event_name exists within script. Method will terminate execution of the program if the specified event is required and doesn't exist. (`bool`) +func _event_exists_in_script(script: ESCScript, event_name: String) -> bool: + if script.events.has(event_name): + return true + + if event_name in escoria.event_manager.REQUIRED_EVENTS: + if script.filename: + escoria.logger.error( + self, + "The script '%s' is missing a required event: '%s'." % [script.filename, event_name] + ) + else: + escoria.logger.error( + self, + "The required event '%s' requested by internal script is missing." % event_name + ) + else: + if script.filename: + escoria.logger.warn( + self, + "The script '%s' is missing the requested event: '%s'." % [script.filename, event_name] + ) + else: + escoria.logger.warn( + self, + "The event '%s' requested by internal script is missing." % event_name + ) + + return false + + +## Called from escoria autoload to start a new game.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func new_game(): + escoria.event_manager.interrupt() # interrupt first because we're going to reset the interpreter + escoria.game_scene.escoria_show_ui() + escoria.globals_manager.clear() + escoria.interpreter_factory.reset_interpreter() + escoria.main.clear_previous_scene() + escoria.creating_new_game = true + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_FORCE_LAST_SCENE_NULL, + true, + true + ) + run_event_from_script(escoria.start_script, escoria.event_manager.EVENT_NEW_GAME) + + +## Function called to quit the game.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func quit(): + get_tree().root.propagate_notification(NOTIFICATION_WM_CLOSE_REQUEST) + + +# Handle anything necessary if the game started a scene directly. +func _handle_direct_scene_run() -> void: + var current_scene: Node = get_tree().get_current_scene() + if escoria.is_direct_room_run and current_scene is ESCRoom: + escoria.object_manager.set_current_room(current_scene) + + +## Used by game.gd to determine whether the game scene is ready to take inputs from the _input() function. To do so, the current_scene must be set, the game scene must be set, and the game scene must've been notified that the room is ready.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns true if game scene is ready for inputs. (`bool`) +func is_ready_for_inputs() -> bool: + return main.current_scene and main.current_scene.game \ + and main.current_scene.game.room_ready_for_inputs |
