From 9d74b49ab62908d4acf53dde444413886b8b28e5 Mon Sep 17 00:00:00 2001 From: Radio Date: Thu, 4 Jun 2026 15:15:11 +0300 Subject: escoria setup and old branch archives --- action_defaults.esc | 5 + .../_test/deprecated/test_esc_compiler.gd | 300 ++++ .../_test/deprecated/test_esc_compiler.gd.uid | 1 + .../_test/deprecated/test_esc_compiler.tscn | 43 + addons/escoria-core/_test/test_migrations.gd | 20 + addons/escoria-core/_test/test_migrations.gd.uid | 1 + addons/escoria-core/_test/test_migrations.tscn | 25 + addons/escoria-core/_test/testversions/1.0.1.gd | 5 + .../escoria-core/_test/testversions/1.0.1.gd.uid | 1 + addons/escoria-core/_test/testversions/1.1.0.gd | 5 + .../escoria-core/_test/testversions/1.1.0.gd.uid | 1 + addons/escoria-core/buses/default_bus_layout.tres | 27 + addons/escoria-core/design/esc_background.svg | 17 + .../escoria-core/design/esc_background.svg.import | 43 + addons/escoria-core/design/esc_exit.svg | 24 + addons/escoria-core/design/esc_exit.svg.import | 43 + addons/escoria-core/design/esc_item.svg | 14 + addons/escoria-core/design/esc_item.svg.import | 43 + addons/escoria-core/design/esc_location.svg | 15 + addons/escoria-core/design/esc_location.svg.import | 43 + addons/escoria-core/design/esc_player.svg | 14 + addons/escoria-core/design/esc_player.svg.import | 43 + addons/escoria-core/design/esc_room.svg | 18 + addons/escoria-core/design/esc_room.svg.import | 43 + addons/escoria-core/design/esc_terrain.svg | 14 + addons/escoria-core/design/esc_terrain.svg.import | 43 + addons/escoria-core/design/escoria-logo-small.png | Bin 0 -> 32865 bytes .../design/escoria-logo-small.png.import | 40 + addons/escoria-core/design/escoria-logo.png | Bin 0 -> 112530 bytes addons/escoria-core/design/escoria-logo.png.import | 40 + addons/escoria-core/design/escoria-logo.svg | 594 +++++++ addons/escoria-core/design/escoria-logo.svg.import | 43 + addons/escoria-core/design/icons.sketch | Bin 0 -> 12533 bytes .../escoria-core/game/assets/images/no_image.png | Bin 0 -> 1108 bytes .../game/assets/images/no_image.png.import | 40 + .../escoria-core/game/assets/images/no_image.xcf | Bin 0 -> 2852 bytes addons/escoria-core/game/assets/images/white.png | Bin 0 -> 174 bytes .../game/assets/images/white.png.import | 40 + .../game/core-scripts/behaviors/esc_movable.gd | 627 +++++++ .../game/core-scripts/behaviors/esc_movable.gd.uid | 1 + .../escoria-core/game/core-scripts/esc/README.md | 253 +++ .../game/core-scripts/esc/commands/accept_input.gd | 97 ++ .../core-scripts/esc/commands/accept_input.gd.uid | 1 + .../game/core-scripts/esc/commands/anim.gd | 94 + .../game/core-scripts/esc/commands/anim.gd.uid | 1 + .../game/core-scripts/esc/commands/anim_block.gd | 99 ++ .../core-scripts/esc/commands/anim_block.gd.uid | 1 + .../game/core-scripts/esc/commands/block_say.gd | 84 + .../core-scripts/esc/commands/block_say.gd.uid | 1 + .../game/core-scripts/esc/commands/camera_push.gd | 144 ++ .../core-scripts/esc/commands/camera_push.gd.uid | 1 + .../core-scripts/esc/commands/camera_push_block.gd | 159 ++ .../esc/commands/camera_push_block.gd.uid | 1 + .../core-scripts/esc/commands/camera_set_limits.gd | 98 ++ .../esc/commands/camera_set_limits.gd.uid | 1 + .../core-scripts/esc/commands/camera_set_pos.gd | 94 + .../esc/commands/camera_set_pos.gd.uid | 1 + .../esc/commands/camera_set_pos_block.gd | 110 ++ .../esc/commands/camera_set_pos_block.gd.uid | 1 + .../core-scripts/esc/commands/camera_set_target.gd | 92 + .../esc/commands/camera_set_target.gd.uid | 1 + .../esc/commands/camera_set_target_block.gd | 110 ++ .../esc/commands/camera_set_target_block.gd.uid | 1 + .../core-scripts/esc/commands/camera_set_zoom.gd | 73 + .../esc/commands/camera_set_zoom.gd.uid | 1 + .../esc/commands/camera_set_zoom_block.gd | 106 ++ .../esc/commands/camera_set_zoom_block.gd.uid | 1 + .../esc/commands/camera_set_zoom_height.gd | 90 + .../esc/commands/camera_set_zoom_height.gd.uid | 1 + .../esc/commands/camera_set_zoom_height_block.gd | 104 ++ .../commands/camera_set_zoom_height_block.gd.uid | 1 + .../game/core-scripts/esc/commands/camera_shift.gd | 127 ++ .../core-scripts/esc/commands/camera_shift.gd.uid | 1 + .../esc/commands/camera_shift_block.gd | 145 ++ .../esc/commands/camera_shift_block.gd.uid | 1 + .../game/core-scripts/esc/commands/change_scene.gd | 109 ++ .../core-scripts/esc/commands/change_scene.gd.uid | 1 + .../esc/commands/change_scene_godot.gd | 106 ++ .../esc/commands/change_scene_godot.gd.uid | 1 + .../game/core-scripts/esc/commands/custom.gd | 125 ++ .../game/core-scripts/esc/commands/custom.gd.uid | 1 + .../game/core-scripts/esc/commands/dec_global.gd | 90 + .../core-scripts/esc/commands/dec_global.gd.uid | 1 + .../core-scripts/esc/commands/enable_terrain.gd | 71 + .../esc/commands/enable_terrain.gd.uid | 1 + .../core-scripts/esc/commands/end_block_say.gd | 85 + .../core-scripts/esc/commands/end_block_say.gd.uid | 1 + .../game/core-scripts/esc/commands/hide_menu.gd | 105 ++ .../core-scripts/esc/commands/hide_menu.gd.uid | 1 + .../game/core-scripts/esc/commands/inc_global.gd | 93 + .../core-scripts/esc/commands/inc_global.gd.uid | 1 + .../core-scripts/esc/commands/inventory_add.gd | 89 + .../core-scripts/esc/commands/inventory_add.gd.uid | 1 + .../core-scripts/esc/commands/inventory_remove.gd | 88 + .../esc/commands/inventory_remove.gd.uid | 1 + .../game/core-scripts/esc/commands/play_snd.gd | 80 + .../game/core-scripts/esc/commands/play_snd.gd.uid | 1 + .../core-scripts/esc/commands/print_internal.gd | 62 + .../esc/commands/print_internal.gd.uid | 1 + .../game/core-scripts/esc/commands/queue_event.gd | 159 ++ .../core-scripts/esc/commands/queue_event.gd.uid | 1 + .../core-scripts/esc/commands/queue_resource.gd | 89 + .../esc/commands/queue_resource.gd.uid | 1 + .../game/core-scripts/esc/commands/rand_global.gd | 83 + .../core-scripts/esc/commands/rand_global.gd.uid | 1 + .../game/core-scripts/esc/commands/repeat.gd | 58 + .../game/core-scripts/esc/commands/repeat.gd.uid | 1 + .../game/core-scripts/esc/commands/save_game.gd | 96 ++ .../core-scripts/esc/commands/save_game.gd.uid | 1 + .../game/core-scripts/esc/commands/say.gd | 156 ++ .../game/core-scripts/esc/commands/say.gd.uid | 1 + .../game/core-scripts/esc/commands/sched_event.gd | 149 ++ .../core-scripts/esc/commands/sched_event.gd.uid | 1 + .../game/core-scripts/esc/commands/set_active.gd | 84 + .../core-scripts/esc/commands/set_active.gd.uid | 1 + .../esc/commands/set_active_if_exists.gd | 64 + .../esc/commands/set_active_if_exists.gd.uid | 1 + .../game/core-scripts/esc/commands/set_angle.gd | 91 + .../core-scripts/esc/commands/set_angle.gd.uid | 1 + .../core-scripts/esc/commands/set_animations.gd | 112 ++ .../esc/commands/set_animations.gd.uid | 1 + .../core-scripts/esc/commands/set_direction.gd | 106 ++ .../core-scripts/esc/commands/set_direction.gd.uid | 1 + .../game/core-scripts/esc/commands/set_global.gd | 97 ++ .../core-scripts/esc/commands/set_global.gd.uid | 1 + .../game/core-scripts/esc/commands/set_globals.gd | 65 + .../core-scripts/esc/commands/set_globals.gd.uid | 1 + .../core-scripts/esc/commands/set_gui_visible.gd | 64 + .../esc/commands/set_gui_visible.gd.uid | 1 + .../core-scripts/esc/commands/set_interactive.gd | 84 + .../esc/commands/set_interactive.gd.uid | 1 + .../esc/commands/set_item_custom_data.gd | 66 + .../esc/commands/set_item_custom_data.gd.uid | 1 + .../game/core-scripts/esc/commands/set_speed.gd | 82 + .../core-scripts/esc/commands/set_speed.gd.uid | 1 + .../game/core-scripts/esc/commands/set_state.gd | 88 + .../core-scripts/esc/commands/set_state.gd.uid | 1 + .../game/core-scripts/esc/commands/show_menu.gd | 111 ++ .../core-scripts/esc/commands/show_menu.gd.uid | 1 + .../game/core-scripts/esc/commands/slide.gd | 165 ++ .../game/core-scripts/esc/commands/slide.gd.uid | 1 + .../game/core-scripts/esc/commands/slide_block.gd | 51 + .../core-scripts/esc/commands/slide_block.gd.uid | 1 + .../game/core-scripts/esc/commands/spawn.gd | 126 ++ .../game/core-scripts/esc/commands/spawn.gd.uid | 1 + .../game/core-scripts/esc/commands/stop.gd | 58 + .../game/core-scripts/esc/commands/stop.gd.uid | 1 + .../game/core-scripts/esc/commands/stop_snd.gd | 113 ++ .../game/core-scripts/esc/commands/stop_snd.gd.uid | 1 + .../game/core-scripts/esc/commands/teleport.gd | 103 ++ .../game/core-scripts/esc/commands/teleport.gd.uid | 1 + .../game/core-scripts/esc/commands/teleport_pos.gd | 99 ++ .../core-scripts/esc/commands/teleport_pos.gd.uid | 1 + .../game/core-scripts/esc/commands/transition.gd | 119 ++ .../core-scripts/esc/commands/transition.gd.uid | 1 + .../game/core-scripts/esc/commands/turn_to.gd | 97 ++ .../game/core-scripts/esc/commands/turn_to.gd.uid | 1 + .../game/core-scripts/esc/commands/wait.gd | 96 ++ .../game/core-scripts/esc/commands/wait.gd.uid | 1 + .../game/core-scripts/esc/commands/walk.gd | 108 ++ .../game/core-scripts/esc/commands/walk.gd.uid | 1 + .../game/core-scripts/esc/commands/walk_block.gd | 110 ++ .../core-scripts/esc/commands/walk_block.gd.uid | 1 + .../game/core-scripts/esc/commands/walk_to_pos.gd | 99 ++ .../core-scripts/esc/commands/walk_to_pos.gd.uid | 1 + .../core-scripts/esc/commands/walk_to_pos_block.gd | 100 ++ .../esc/commands/walk_to_pos_block.gd.uid | 1 + .../core-scripts/esc/compiler/esc_break_counter.gd | 49 + .../esc/compiler/esc_break_counter.gd.uid | 1 + .../core-scripts/esc/compiler/esc_environment.gd | 221 +++ .../esc/compiler/esc_environment.gd.uid | 1 + .../core-scripts/esc/compiler/esc_grammar_expr.gd | 22 + .../esc/compiler/esc_grammar_expr.gd.uid | 1 + .../core-scripts/esc/compiler/esc_grammar_exprs.gd | 724 ++++++++ .../esc/compiler/esc_grammar_exprs.gd.uid | 1 + .../core-scripts/esc/compiler/esc_grammar_stmt.gd | 22 + .../esc/compiler/esc_grammar_stmt.gd.uid | 1 + .../core-scripts/esc/compiler/esc_grammar_stmts.gd | 1017 +++++++++++ .../esc/compiler/esc_grammar_stmts.gd.uid | 1 + .../core-scripts/esc/compiler/esc_interpreter.gd | 936 ++++++++++ .../esc/compiler/esc_interpreter.gd.uid | 1 + .../esc/compiler/esc_interpreter_factory.gd | 31 + .../esc/compiler/esc_interpreter_factory.gd.uid | 1 + .../core-scripts/esc/compiler/esc_parse_error.gd | 3 + .../esc/compiler/esc_parse_error.gd.uid | 1 + .../game/core-scripts/esc/compiler/esc_parser.gd | 975 +++++++++++ .../core-scripts/esc/compiler/esc_parser.gd.uid | 1 + .../game/core-scripts/esc/compiler/esc_resolver.gd | 506 ++++++ .../core-scripts/esc/compiler/esc_resolver.gd.uid | 1 + .../game/core-scripts/esc/compiler/esc_scanner.gd | 393 +++++ .../core-scripts/esc/compiler/esc_scanner.gd.uid | 1 + .../esc/compiler/esc_script_builder.gd | 117 ++ .../esc/compiler/esc_script_builder.gd.uid | 1 + .../game/core-scripts/esc/compiler/esc_token.gd | 144 ++ .../core-scripts/esc/compiler/esc_token.gd.uid | 1 + .../core-scripts/esc/compiler/esc_token_type.gd | 42 + .../esc/compiler/esc_token_type.gd.uid | 1 + .../game/core-scripts/esc/design-principles.md | 40 + .../game/core-scripts/esc/esc_action_manager.gd | 914 ++++++++++ .../core-scripts/esc/esc_action_manager.gd.uid | 1 + .../game/core-scripts/esc/esc_command_registry.gd | 58 + .../core-scripts/esc/esc_command_registry.gd.uid | 1 + .../game/core-scripts/esc/esc_compiler.gd | 204 +++ .../game/core-scripts/esc/esc_compiler.gd.uid | 1 + .../game/core-scripts/esc/esc_event_manager.gd | 752 ++++++++ .../game/core-scripts/esc/esc_event_manager.gd.uid | 1 + .../game/core-scripts/esc/esc_globals_manager.gd | 228 +++ .../core-scripts/esc/esc_globals_manager.gd.uid | 1 + .../game/core-scripts/esc/esc_inventory_manager.gd | 93 + .../core-scripts/esc/esc_inventory_manager.gd.uid | 1 + .../game/core-scripts/esc/esc_object_manager.gd | 703 ++++++++ .../core-scripts/esc/esc_object_manager.gd.uid | 1 + .../game/core-scripts/esc/esc_room_manager.gd | 606 +++++++ .../game/core-scripts/esc/esc_room_manager.gd.uid | 1 + .../core-scripts/esc/types/esc_base_command.gd | 145 ++ .../core-scripts/esc/types/esc_base_command.gd.uid | 1 + .../esc/types/esc_camera_base_command.gd | 43 + .../esc/types/esc_camera_base_command.gd.uid | 1 + .../game/core-scripts/esc/types/esc_command.gd | 160 ++ .../game/core-scripts/esc/types/esc_command.gd.uid | 1 + .../esc/types/esc_command_argument_descriptor.gd | 264 +++ .../types/esc_command_argument_descriptor.gd.uid | 1 + .../game/core-scripts/esc/types/esc_dialog.gd | 80 + .../game/core-scripts/esc/types/esc_dialog.gd.uid | 1 + .../core-scripts/esc/types/esc_dialog_option.gd | 71 + .../esc/types/esc_dialog_option.gd.uid | 1 + .../game/core-scripts/esc/types/esc_event.gd | 148 ++ .../game/core-scripts/esc/types/esc_event.gd.uid | 1 + .../game/core-scripts/esc/types/esc_execution.gd | 21 + .../core-scripts/esc/types/esc_execution.gd.uid | 1 + .../game/core-scripts/esc/types/esc_object.gd | 202 +++ .../game/core-scripts/esc/types/esc_object.gd.uid | 1 + .../core-scripts/esc/types/esc_room_container.gd | 16 + .../esc/types/esc_room_container.gd.uid | 1 + .../core-scripts/esc/types/esc_room_objects.gd | 7 + .../core-scripts/esc/types/esc_room_objects.gd.uid | 1 + .../core-scripts/esc/types/esc_room_objects_key.gd | 24 + .../esc/types/esc_room_objects_key.gd.uid | 1 + .../core-scripts/esc/types/esc_room_terrains.gd | 7 + .../esc/types/esc_room_terrains.gd.uid | 1 + .../core-scripts/esc/types/esc_scheduled_event.gd | 63 + .../esc/types/esc_scheduled_event.gd.uid | 1 + .../game/core-scripts/esc/types/esc_script.gd | 11 + .../game/core-scripts/esc/types/esc_script.gd.uid | 1 + .../game/core-scripts/esc/types/esc_statement.gd | 184 ++ .../core-scripts/esc/types/esc_statement.gd.uid | 1 + .../game/core-scripts/esc_animation_player.gd | 276 +++ .../game/core-scripts/esc_animation_player.gd.uid | 1 + .../game/core-scripts/esc_background.gd | 191 ++ .../game/core-scripts/esc_background.gd.uid | 1 + .../game/core-scripts/esc_dialog_location.gd | 24 + .../game/core-scripts/esc_dialog_location.gd.uid | 1 + addons/escoria-core/game/core-scripts/esc_exit.gd | 100 ++ .../escoria-core/game/core-scripts/esc_exit.gd.uid | 1 + addons/escoria-core/game/core-scripts/esc_game.gd | 774 +++++++++ .../escoria-core/game/core-scripts/esc_game.gd.uid | 1 + .../game/core-scripts/esc_interaction_location.gd | 23 + .../core-scripts/esc_interaction_location.gd.uid | 1 + .../game/core-scripts/esc_inventory_item.gd | 29 + .../game/core-scripts/esc_inventory_item.gd.uid | 1 + addons/escoria-core/game/core-scripts/esc_item.gd | 1407 +++++++++++++++ .../escoria-core/game/core-scripts/esc_item.gd.uid | 1 + .../escoria-core/game/core-scripts/esc_location.gd | 142 ++ .../game/core-scripts/esc_location.gd.uid | 1 + .../escoria-core/game/core-scripts/esc_player.gd | 37 + .../game/core-scripts/esc_player.gd.uid | 1 + .../game/core-scripts/esc_resource_cache.gd | 146 ++ .../game/core-scripts/esc_resource_cache.gd.uid | 1 + addons/escoria-core/game/core-scripts/esc_room.gd | 255 +++ .../escoria-core/game/core-scripts/esc_room.gd.uid | 1 + .../escoria-core/game/core-scripts/esc_terrain.gd | 417 +++++ .../game/core-scripts/esc_terrain.gd.uid | 1 + .../escoria-core/game/core-scripts/esc_tooltip.gd | 311 ++++ .../game/core-scripts/esc_tooltip.gd.uid | 1 + .../game/core-scripts/esc_walk_context.gd | 42 + .../game/core-scripts/esc_walk_context.gd.uid | 1 + .../game/core-scripts/migrations/esc_migration.gd | 49 + .../core-scripts/migrations/esc_migration.gd.uid | 1 + .../migrations/esc_migration_manager.gd | 208 +++ .../migrations/esc_migration_manager.gd.uid | 1 + .../game/core-scripts/plugins/escoria_plugin.gd | 126 ++ .../core-scripts/plugins/escoria_plugin.gd.uid | 1 + .../core-scripts/resources/esc_animationname.gd | 10 + .../resources/esc_animationname.gd.uid | 1 + .../resources/esc_animationresource.gd | 120 ++ .../resources/esc_animationresource.gd.uid | 1 + .../core-scripts/resources/esc_directionangle.gd | 10 + .../resources/esc_directionangle.gd.uid | 1 + .../resources/esc_resource_descriptor.gd | 25 + .../resources/esc_resource_descriptor.gd.uid | 1 + .../core-scripts/save_data/esc_save_manager.gd | 620 +++++++ .../core-scripts/save_data/esc_save_manager.gd.uid | 1 + .../game/core-scripts/save_data/esc_savegame.gd | 45 + .../core-scripts/save_data/esc_savegame.gd.uid | 1 + .../core-scripts/save_data/esc_savesettings.gd | 50 + .../core-scripts/save_data/esc_savesettings.gd.uid | 1 + .../core-scripts/save_data/esc_settings_manager.gd | 295 ++++ .../save_data/esc_settings_manager.gd.uid | 1 + .../game/core-scripts/utils/esc_utils.gd | 95 + .../game/core-scripts/utils/esc_utils.gd.uid | 1 + addons/escoria-core/game/esc_autoload.gd | 248 +++ addons/escoria-core/game/esc_autoload.gd.uid | 1 + .../escoria-core/game/esc_dependency_injector.gd | 29 + .../game/esc_dependency_injector.gd.uid | 1 + addons/escoria-core/game/esc_inputs_manager.gd | 886 ++++++++++ addons/escoria-core/game/esc_inputs_manager.gd.uid | 1 + .../game/esc_project_settings_manager.gd | 211 +++ .../game/esc_project_settings_manager.gd.uid | 1 + addons/escoria-core/game/escoria.gd | 313 ++++ addons/escoria-core/game/escoria.gd.uid | 1 + addons/escoria-core/game/escoria.tscn | 11 + addons/escoria-core/game/main.gd | 404 +++++ addons/escoria-core/game/main.gd.uid | 1 + addons/escoria-core/game/main.tscn | 52 + addons/escoria-core/game/main_scene.gd | 27 + addons/escoria-core/game/main_scene.gd.uid | 1 + addons/escoria-core/game/main_scene.tscn | 6 + .../game/scenes/camera_player/camera.tscn | 9 + .../game/scenes/camera_player/esc_camera.gd | 525 ++++++ .../game/scenes/camera_player/esc_camera.gd.uid | 1 + .../game/scenes/camera_player/esc_camera_limits.gd | 40 + .../scenes/camera_player/esc_camera_limits.gd.uid | 1 + .../game/scenes/dialogs/esc_dialog_manager.gd | 164 ++ .../game/scenes/dialogs/esc_dialog_manager.gd.uid | 1 + .../scenes/dialogs/esc_dialog_options_chooser.gd | 60 + .../dialogs/esc_dialog_options_chooser.gd.uid | 1 + .../game/scenes/dialogs/esc_dialog_player.gd | 256 +++ .../game/scenes/dialogs/esc_dialog_player.gd.uid | 1 + .../game/scenes/esc_prompt/esc_prompt_popup.gd | 184 ++ .../game/scenes/esc_prompt/esc_prompt_popup.gd.uid | 1 + .../game/scenes/esc_prompt/esc_prompt_popup.tscn | 33 + .../game/scenes/inventory/inventory_ui.gd | 197 +++ .../game/scenes/inventory/inventory_ui.gd.uid | 1 + .../game/scenes/sound/esc_ambient_player.gd | 61 + .../game/scenes/sound/esc_ambient_player.gd.uid | 1 + .../game/scenes/sound/esc_ambient_player.tscn | 19 + .../game/scenes/sound/esc_music_player.gd | 81 + .../game/scenes/sound/esc_music_player.gd.uid | 1 + .../game/scenes/sound/esc_music_player.tscn | 15 + .../game/scenes/sound/esc_sound_player.gd | 93 + .../game/scenes/sound/esc_sound_player.gd.uid | 1 + .../game/scenes/sound/esc_sound_player.tscn | 20 + .../game/scenes/sound/esc_speech_player.gd | 111 ++ .../game/scenes/sound/esc_speech_player.gd.uid | 1 + .../game/scenes/sound/esc_speech_player.tscn | 18 + .../scenes/transitions/esc_transition_player.gd | 195 +++ .../transitions/esc_transition_player.gd.uid | 1 + .../game/scenes/transitions/masks/curtain.png | Bin 0 -> 149684 bytes .../scenes/transitions/masks/curtain.png.import | 40 + .../game/scenes/transitions/masks/from_center.png | Bin 0 -> 139993 bytes .../transitions/masks/from_center.png.import | 40 + .../game/scenes/transitions/masks/shards.png | Bin 0 -> 82906 bytes .../scenes/transitions/masks/shards.png.import | 40 + .../scenes/transitions/shaders/curtain.material | Bin 0 -> 594 bytes .../scenes/transitions/shaders/fade_black.material | Bin 0 -> 478 bytes .../scenes/transitions/shaders/fade_white.material | Bin 0 -> 477 bytes .../transitions/shaders/from_center.material | Bin 0 -> 598 bytes .../scenes/transitions/shaders/shards.material | Bin 0 -> 593 bytes .../game/scenes/transitions/transition.tscn | 12 + addons/escoria-core/game/tween3.gd | 264 +++ addons/escoria-core/game/tween3.gd.uid | 1 + .../escoria-core/patterns/state_machine/state.gd | 81 + .../patterns/state_machine/state.gd.uid | 1 + .../patterns/state_machine/state_machine.gd | 147 ++ .../patterns/state_machine/state_machine.gd.uid | 1 + addons/escoria-core/plugin.cfg | 7 + addons/escoria-core/plugin.gd | 613 +++++++ addons/escoria-core/plugin.gd.uid | 1 + addons/escoria-core/testing/NotoSans-Regular.ttf | Bin 0 -> 313144 bytes .../testing/NotoSans-Regular.ttf.import | 36 + addons/escoria-core/testing/README | 8 + .../escoria-core/testing/player_angles_finder.gd | 231 +++ .../testing/player_angles_finder.gd.uid | 1 + .../escoria-core/testing/player_angles_finder.tscn | 137 ++ .../testing/rtl_screen_offset_testing.gd | 174 ++ .../testing/rtl_screen_offset_testing.gd.uid | 1 + .../testing/rtl_screen_offset_testing.tscn | 73 + addons/escoria-core/tools/logging/esc_log_level.gd | 35 + .../tools/logging/esc_log_level.gd.uid | 1 + addons/escoria-core/tools/logging/esc_logger.gd | 462 +++++ .../escoria-core/tools/logging/esc_logger.gd.uid | 1 + .../escoria-core/tools/logging/esc_safe_logging.gd | 60 + .../tools/logging/esc_safe_logging.gd.uid | 1 + .../analyzers/esc_exit_scene_script_analyzer.gd | 151 ++ .../esc_exit_scene_script_analyzer.gd.uid | 1 + .../analyzers/esc_script_analyzer.gd | 387 +++++ .../analyzers/esc_script_analyzer.gd.uid | 1 + .../tools/script_analysis/esc_ashes_analyzer.gd | 52 + .../script_analysis/esc_ashes_analyzer.gd.uid | 1 + .../tools/script_analysis/esc_static_analyzers.gd | 19 + .../script_analysis/esc_static_analyzers.gd.uid | 1 + .../ui_library/fonts/caslonantique.tres | 7 + .../ui_library/fonts/caslonantique.ttf | Bin 0 -> 147756 bytes .../ui_library/fonts/caslonantique.ttf.import | 36 + .../ui_library/inventory/esc_inventory_button.gd | 178 ++ .../inventory/esc_inventory_button.gd.uid | 1 + .../inventory/esc_inventory_container.gd | 102 ++ .../inventory/esc_inventory_container.gd.uid | 1 + .../ui_library/menus/load_save/SaveGamesSorter.gd | 11 + .../menus/load_save/SaveGamesSorter.gd.uid | 1 + .../ui_library/menus/load_save/load/load_game.gd | 58 + .../menus/load_save/load/load_game.gd.uid | 1 + .../ui_library/menus/load_save/load/load_game.tscn | 44 + .../load_save/load_save_slot/load_save_slot.gd | 7 + .../load_save/load_save_slot/load_save_slot.gd.uid | 1 + .../load_save/load_save_slot/load_save_slot.tscn | 35 + .../load_save/save/overwrite_confirm_popup.gd | 14 + .../load_save/save/overwrite_confirm_popup.gd.uid | 1 + .../load_save/save/overwrite_confirm_popup.tscn | 63 + .../ui_library/menus/load_save/save/save_game.gd | 90 + .../menus/load_save/save/save_game.gd.uid | 1 + .../ui_library/menus/load_save/save/save_game.tscn | 56 + .../menus/load_save/save/save_name_popup.gd | 21 + .../menus/load_save/save/save_name_popup.gd.uid | 1 + .../menus/load_save/save/save_name_popup.tscn | 71 + .../ui_library/menus/main_menu/main_menu.gd | 42 + .../ui_library/menus/main_menu/main_menu.gd.uid | 1 + .../ui_library/menus/main_menu/main_menu.tscn | 85 + .../ui_library/menus/options/flags/de.png | Bin 0 -> 2245 bytes .../ui_library/menus/options/flags/de.png.import | 40 + .../ui_library/menus/options/flags/en.png | Bin 0 -> 8760 bytes .../ui_library/menus/options/flags/en.png.import | 40 + .../ui_library/menus/options/flags/es.png | Bin 0 -> 5210 bytes .../ui_library/menus/options/flags/es.png.import | 40 + .../ui_library/menus/options/flags/fr.png | Bin 0 -> 1840 bytes .../ui_library/menus/options/flags/fr.png.import | 40 + .../ui_library/menus/options/options.gd | 190 ++ .../ui_library/menus/options/options.gd.uid | 1 + .../ui_library/menus/options/options.tscn | 136 ++ .../ui_library/menus/pause_menu/pause_menu.gd | 75 + .../ui_library/menus/pause_menu/pause_menu.gd.uid | 1 + .../ui_library/menus/pause_menu/pause_menu.tscn | 94 + addons/escoria-core/ui_library/theme.tres | 6 + .../ui_library/tools/hover_stack/hover_stack.gd | 14 + .../tools/hover_stack/hover_stack.gd.uid | 1 + .../ui_library/tools/hover_stack/hover_stack.tscn | 23 + .../ui_library/tools/room_select/room_select.gd | 73 + .../tools/room_select/room_select.gd.uid | 1 + .../ui_library/tools/room_select/room_select.tscn | 25 + addons/escoria-dialog-simple/chooser/simple.gd | 99 ++ addons/escoria-dialog-simple/chooser/simple.gd.uid | 1 + addons/escoria-dialog-simple/chooser/simple.tscn | 52 + addons/escoria-dialog-simple/esc_dialog_simple.gd | 211 +++ .../escoria-dialog-simple/esc_dialog_simple.gd.uid | 1 + .../esc_dialog_simple_settings.gd | 24 + .../esc_dialog_simple_settings.gd.uid | 1 + .../esc_dialog_simple_state_machine.gd | 34 + .../esc_dialog_simple_state_machine.gd.uid | 1 + addons/escoria-dialog-simple/plugin.cfg | 7 + addons/escoria-dialog-simple/plugin.gd | 145 ++ addons/escoria-dialog-simple/plugin.gd.uid | 1 + .../escoria-dialog-simple/states/dialog_choices.gd | 44 + .../states/dialog_choices.gd.uid | 1 + .../escoria-dialog-simple/states/dialog_finish.gd | 19 + .../states/dialog_finish.gd.uid | 1 + addons/escoria-dialog-simple/states/dialog_idle.gd | 5 + .../states/dialog_idle.gd.uid | 1 + .../states/dialog_interrupt.gd | 24 + .../states/dialog_interrupt.gd.uid | 1 + addons/escoria-dialog-simple/states/dialog_say.gd | 172 ++ .../escoria-dialog-simple/states/dialog_say.gd.uid | 1 + .../states/dialog_say_fast.gd | 29 + .../states/dialog_say_fast.gd.uid | 1 + .../states/dialog_say_finish.gd | 29 + .../states/dialog_say_finish.gd.uid | 1 + .../escoria-dialog-simple/states/dialog_visible.gd | 34 + .../states/dialog_visible.gd.uid | 1 + addons/escoria-dialog-simple/types/avatar.gd | 239 +++ addons/escoria-dialog-simple/types/avatar.gd.uid | 1 + addons/escoria-dialog-simple/types/avatar.tscn | 53 + addons/escoria-dialog-simple/types/floating.gd | 287 +++ addons/escoria-dialog-simple/types/floating.gd.uid | 1 + addons/escoria-dialog-simple/types/floating.tscn | 12 + .../fonts/caslonantique.tres | 7 + .../fonts/caslonantique.ttf | Bin 0 -> 147756 bytes .../fonts/caslonantique.ttf.import | 36 + addons/escoria-ui-keyboard-9verbs/game.gd | 404 +++++ addons/escoria-ui-keyboard-9verbs/game.gd.uid | 1 + addons/escoria-ui-keyboard-9verbs/game.tscn | 133 ++ addons/escoria-ui-keyboard-9verbs/input_map.gd | 60 + addons/escoria-ui-keyboard-9verbs/input_map.gd.uid | 1 + .../inventory/inventory_ui.tscn | 31 + addons/escoria-ui-keyboard-9verbs/plugin.cfg | 7 + addons/escoria-ui-keyboard-9verbs/plugin.gd | 24 + addons/escoria-ui-keyboard-9verbs/plugin.gd.uid | 1 + addons/escoria-ui-keyboard-9verbs/theme.tres | 6 + .../tooltip/action_target_tooltip.tscn | 14 + .../tooltip/tooltip_action_target.gd | 34 + .../tooltip/tooltip_action_target.gd.uid | 1 + addons/escoria-ui-keyboard-9verbs/verbs_menu.gd | 32 + .../escoria-ui-keyboard-9verbs/verbs_menu.gd.uid | 1 + addons/escoria-ui-keyboard-9verbs/verbs_menu.tscn | 125 ++ .../cursors/cursor_dialog.png | Bin 0 -> 6078 bytes .../cursors/cursor_dialog.png.import | 40 + .../cursors/cursor_examine.png | Bin 0 -> 13569 bytes .../cursors/cursor_examine.png.import | 40 + .../escoria-ui-simplemouse/cursors/cursor_foot.png | Bin 0 -> 14886 bytes .../cursors/cursor_foot.png.import | 40 + .../cursors/cursor_glasses.png | Bin 0 -> 52307 bytes .../cursors/cursor_glasses.png.import | 40 + .../escoria-ui-simplemouse/cursors/cursor_gun.png | Bin 0 -> 44739 bytes .../cursors/cursor_gun.png.import | 40 + .../escoria-ui-simplemouse/cursors/cursor_hand.png | Bin 0 -> 14640 bytes .../cursors/cursor_hand.png.import | 40 + .../escoria-ui-simplemouse/cursors/cursor_heal.png | Bin 0 -> 41874 bytes .../cursors/cursor_heal.png.import | 40 + .../escoria-ui-simplemouse/cursors/cursor_lock.png | Bin 0 -> 50583 bytes .../cursors/cursor_lock.png.import | 40 + .../escoria-ui-simplemouse/cursors/cursor_pen.png | Bin 0 -> 14074 bytes .../cursors/cursor_pen.png.import | 40 + .../cursors/cursor_punch.png | Bin 0 -> 54012 bytes .../cursors/cursor_punch.png.import | 40 + .../cursors/cursor_shield.png | Bin 0 -> 44890 bytes .../cursors/cursor_shield.png.import | 40 + .../escoria-ui-simplemouse/cursors/cursor_tool.png | Bin 0 -> 14021 bytes .../cursors/cursor_tool.png.import | 40 + .../escoria-ui-simplemouse/cursors/cursor_wait.png | Bin 0 -> 13895 bytes .../cursors/cursor_wait.png.import | 40 + .../escoria-ui-simplemouse/cursors/new/arrow_e.png | Bin 0 -> 287 bytes .../cursors/new/arrow_e.png.import | 40 + .../escoria-ui-simplemouse/cursors/new/arrow_n.png | Bin 0 -> 264 bytes .../cursors/new/arrow_n.png.import | 40 + .../cursors/new/arrow_ne.png | Bin 0 -> 332 bytes .../cursors/new/arrow_ne.png.import | 40 + .../cursors/new/arrow_nw.png | Bin 0 -> 341 bytes .../cursors/new/arrow_nw.png.import | 40 + .../escoria-ui-simplemouse/cursors/new/arrow_s.png | Bin 0 -> 272 bytes .../cursors/new/arrow_s.png.import | 40 + .../cursors/new/arrow_se.png | Bin 0 -> 341 bytes .../cursors/new/arrow_se.png.import | 40 + .../cursors/new/arrow_sw.png | Bin 0 -> 340 bytes .../cursors/new/arrow_sw.png.import | 40 + .../escoria-ui-simplemouse/cursors/new/arrow_w.png | Bin 0 -> 290 bytes .../cursors/new/arrow_w.png.import | 40 + .../cursors/new/hand_open.png | Bin 0 -> 507 bytes .../cursors/new/hand_open.png.import | 40 + .../cursors/new/line_cross.png | Bin 0 -> 215 bytes .../cursors/new/line_cross.png.import | 40 + .../escoria-ui-simplemouse/cursors/new/look_b.png | Bin 0 -> 503 bytes .../cursors/new/look_b.png.import | 40 + .../cursors/new/message_dots_round.png | Bin 0 -> 478 bytes .../cursors/new/message_dots_round.png.import | 40 + .../cursors/new/target_a.png | Bin 0 -> 294 bytes .../cursors/new/target_a.png.import | 40 + .../cursors/new/tool_wrench.png | Bin 0 -> 494 bytes .../cursors/new/tool_wrench.png.import | 40 + addons/escoria-ui-simplemouse/cursors/new/zoom.png | Bin 0 -> 532 bytes .../cursors/new/zoom.png.import | 40 + .../cursors/originals/cursor_examine.png | Bin 0 -> 50892 bytes .../cursors/originals/cursor_examine.png.import | 40 + .../cursors/originals/cursor_foot.png | Bin 0 -> 52578 bytes .../cursors/originals/cursor_foot.png.import | 40 + .../cursors/originals/cursor_glasses.png | Bin 0 -> 52307 bytes .../cursors/originals/cursor_glasses.png.import | 40 + .../cursors/originals/cursor_gun.png | Bin 0 -> 44739 bytes .../cursors/originals/cursor_gun.png.import | 40 + .../cursors/originals/cursor_hand.png | Bin 0 -> 53165 bytes .../cursors/originals/cursor_hand.png.import | 40 + .../cursors/originals/cursor_heal.png | Bin 0 -> 41874 bytes .../cursors/originals/cursor_heal.png.import | 40 + .../cursors/originals/cursor_lock.png | Bin 0 -> 50583 bytes .../cursors/originals/cursor_lock.png.import | 40 + .../cursors/originals/cursor_pen.png | Bin 0 -> 51555 bytes .../cursors/originals/cursor_pen.png.import | 40 + .../cursors/originals/cursor_punch.png | Bin 0 -> 54012 bytes .../cursors/originals/cursor_punch.png.import | 40 + .../cursors/originals/cursor_shield.png | Bin 0 -> 44890 bytes .../cursors/originals/cursor_shield.png.import | 40 + .../cursors/originals/cursor_tool.png | Bin 0 -> 47099 bytes .../cursors/originals/cursor_tool.png.import | 40 + .../cursors/originals/cursor_wait.png | Bin 0 -> 45986 bytes .../cursors/originals/cursor_wait.png.import | 40 + .../cursors/originals/pointers4.svg | 1824 ++++++++++++++++++++ .../cursors/originals/pointers4.svg.import | 43 + .../fonts/caslonantique.tres | 7 + .../escoria-ui-simplemouse/fonts/caslonantique.ttf | Bin 0 -> 147756 bytes .../fonts/caslonantique.ttf.import | 36 + addons/escoria-ui-simplemouse/game.gd | 485 ++++++ addons/escoria-ui-simplemouse/game.gd.uid | 1 + addons/escoria-ui-simplemouse/game.tscn | 90 + addons/escoria-ui-simplemouse/images/frame.png | Bin 0 -> 285943 bytes .../escoria-ui-simplemouse/images/frame.png.import | 40 + .../escoria-ui-simplemouse/images/inventory_bg.png | Bin 0 -> 59720 bytes .../images/inventory_bg.png.import | 40 + .../images/inventory_icon.png | Bin 0 -> 68743 bytes .../images/inventory_icon.png.import | 40 + .../images/inventory_panel.png | Bin 0 -> 2308 bytes .../images/inventory_panel.png.import | 40 + .../images/originals/inventory_panel.svg | 53 + .../images/originals/inventory_panel.svg.import | 43 + .../inventory/inventory_ui.gd | 88 + .../inventory/inventory_ui.gd.uid | 1 + .../inventory/inventory_ui.tscn | 90 + addons/escoria-ui-simplemouse/plugin.cfg | 7 + addons/escoria-ui-simplemouse/plugin.gd | 24 + addons/escoria-ui-simplemouse/plugin.gd.uid | 1 + addons/escoria-ui-simplemouse/theme.tres | 6 + .../tooltip/target_tooltip.tscn | 11 + .../tooltip/tooltip_target.gd | 28 + .../tooltip/tooltip_target.gd.uid | 1 + addons/escoria-ui-simplemouse/verbs_mouseicons.gd | 54 + .../escoria-ui-simplemouse/verbs_mouseicons.gd.uid | 1 + .../escoria-ui-simplemouse/verbs_mouseicons.tscn | 88 + addons/escoria-wizard/BackgroundItem.gd | 139 ++ addons/escoria-wizard/BackgroundItem.gd.uid | 1 + addons/escoria-wizard/CharacterCreator.gd | 1739 +++++++++++++++++++ addons/escoria-wizard/CharacterCreator.gd.uid | 1 + addons/escoria-wizard/CharacterCreator.tscn | 1179 +++++++++++++ addons/escoria-wizard/CharacterCreator.tscn.bak | 1544 +++++++++++++++++ addons/escoria-wizard/ItemCreator.tscn | 313 ++++ addons/escoria-wizard/RoomCreator.gd | 325 ++++ addons/escoria-wizard/RoomCreator.gd.uid | 1 + addons/escoria-wizard/RoomCreator.tscn | 278 +++ addons/escoria-wizard/background.pdn | Bin 0 -> 73218 bytes addons/escoria-wizard/draw_frame_rects.gd | 44 + addons/escoria-wizard/draw_frame_rects.gd.uid | 1 + addons/escoria-wizard/escoria_wizard.gd | 43 + addons/escoria-wizard/escoria_wizard.gd.uid | 1 + addons/escoria-wizard/escoria_wizard.tscn | 83 + addons/escoria-wizard/graphics/background.png | Bin 0 -> 3360 bytes .../escoria-wizard/graphics/background.png.import | 40 + .../escoria-wizard/graphics/background_preview.png | Bin 0 -> 12006 bytes .../graphics/background_preview.png.import | 40 + addons/escoria-wizard/graphics/character.png | Bin 0 -> 3936 bytes .../escoria-wizard/graphics/character.png.import | 40 + .../graphics/darkgreenarrow_down.png | Bin 0 -> 523 bytes .../graphics/darkgreenarrow_down.png.import | 40 + .../graphics/darkgreenarrow_downleft.png | Bin 0 -> 726 bytes .../graphics/darkgreenarrow_downleft.png.import | 40 + .../graphics/darkgreenarrow_downright.png | Bin 0 -> 798 bytes .../graphics/darkgreenarrow_downright.png.import | 40 + .../graphics/darkgreenarrow_left.png | Bin 0 -> 559 bytes .../graphics/darkgreenarrow_left.png.import | 40 + .../graphics/darkgreenarrow_right.png | Bin 0 -> 546 bytes .../graphics/darkgreenarrow_right.png.import | 40 + .../escoria-wizard/graphics/darkgreenarrow_up.png | Bin 0 -> 536 bytes .../graphics/darkgreenarrow_up.png.import | 40 + .../graphics/darkgreenarrow_upleft.png | Bin 0 -> 824 bytes .../graphics/darkgreenarrow_upleft.png.import | 40 + .../graphics/darkgreenarrow_upright.png | Bin 0 -> 738 bytes .../graphics/darkgreenarrow_upright.png.import | 40 + addons/escoria-wizard/graphics/darkgreyarrow_d.png | Bin 0 -> 534 bytes .../graphics/darkgreyarrow_d.png.import | 40 + .../escoria-wizard/graphics/darkgreyarrow_dl.png | Bin 0 -> 748 bytes .../graphics/darkgreyarrow_dl.png.import | 40 + .../escoria-wizard/graphics/darkgreyarrow_dr.png | Bin 0 -> 816 bytes .../graphics/darkgreyarrow_dr.png.import | 40 + addons/escoria-wizard/graphics/darkgreyarrow_l.png | Bin 0 -> 612 bytes .../graphics/darkgreyarrow_l.png.import | 40 + addons/escoria-wizard/graphics/darkgreyarrow_r.png | Bin 0 -> 599 bytes .../graphics/darkgreyarrow_r.png.import | 40 + addons/escoria-wizard/graphics/darkgreyarrow_u.png | Bin 0 -> 564 bytes .../graphics/darkgreyarrow_u.png.import | 40 + .../escoria-wizard/graphics/darkgreyarrow_ul.png | Bin 0 -> 897 bytes .../graphics/darkgreyarrow_ul.png.import | 40 + .../escoria-wizard/graphics/darkgreyarrow_ur.png | Bin 0 -> 779 bytes .../graphics/darkgreyarrow_ur.png.import | 40 + addons/escoria-wizard/graphics/greenarrow_down.png | Bin 0 -> 466 bytes .../graphics/greenarrow_down.png.import | 40 + .../graphics/greenarrow_downleft.png | Bin 0 -> 745 bytes .../graphics/greenarrow_downleft.png.import | 40 + .../graphics/greenarrow_downright.png | Bin 0 -> 812 bytes .../graphics/greenarrow_downright.png.import | 40 + addons/escoria-wizard/graphics/greenarrow_left.png | Bin 0 -> 426 bytes .../graphics/greenarrow_left.png.import | 40 + .../escoria-wizard/graphics/greenarrow_right.png | Bin 0 -> 438 bytes .../graphics/greenarrow_right.png.import | 40 + addons/escoria-wizard/graphics/greenarrow_up.png | Bin 0 -> 491 bytes .../graphics/greenarrow_up.png.import | 40 + .../escoria-wizard/graphics/greenarrow_upleft.png | Bin 0 -> 843 bytes .../graphics/greenarrow_upleft.png.import | 40 + .../escoria-wizard/graphics/greenarrow_upright.png | Bin 0 -> 740 bytes .../graphics/greenarrow_upright.png.import | 40 + addons/escoria-wizard/graphics/greyarrow_d.png | Bin 0 -> 531 bytes .../escoria-wizard/graphics/greyarrow_d.png.import | 40 + addons/escoria-wizard/graphics/greyarrow_dl.png | Bin 0 -> 766 bytes .../graphics/greyarrow_dl.png.import | 40 + addons/escoria-wizard/graphics/greyarrow_dr.png | Bin 0 -> 854 bytes .../graphics/greyarrow_dr.png.import | 40 + addons/escoria-wizard/graphics/greyarrow_l.png | Bin 0 -> 589 bytes .../escoria-wizard/graphics/greyarrow_l.png.import | 40 + addons/escoria-wizard/graphics/greyarrow_r.png | Bin 0 -> 584 bytes .../escoria-wizard/graphics/greyarrow_r.png.import | 40 + addons/escoria-wizard/graphics/greyarrow_u.png | Bin 0 -> 560 bytes .../escoria-wizard/graphics/greyarrow_u.png.import | 40 + addons/escoria-wizard/graphics/greyarrow_ul.png | Bin 0 -> 888 bytes .../graphics/greyarrow_ul.png.import | 40 + addons/escoria-wizard/graphics/greyarrow_ur.png | Bin 0 -> 809 bytes .../graphics/greyarrow_ur.png.import | 40 + .../graphics/help/help_background.png | Bin 0 -> 69164 bytes .../graphics/help/help_background.png.import | 40 + addons/escoria-wizard/graphics/help/left1.png | Bin 0 -> 1299 bytes .../escoria-wizard/graphics/help/left1.png.import | 40 + addons/escoria-wizard/graphics/help/left2.png | Bin 0 -> 429 bytes .../escoria-wizard/graphics/help/left2.png.import | 40 + addons/escoria-wizard/graphics/help/left3.png | Bin 0 -> 428 bytes .../escoria-wizard/graphics/help/left3.png.import | 40 + addons/escoria-wizard/graphics/help/left4.png | Bin 0 -> 573 bytes .../escoria-wizard/graphics/help/left4.png.import | 40 + addons/escoria-wizard/graphics/help/left5.png | Bin 0 -> 647 bytes .../escoria-wizard/graphics/help/left5.png.import | 40 + addons/escoria-wizard/graphics/help/leftall.png | Bin 0 -> 167 bytes .../graphics/help/leftall.png.import | 40 + addons/escoria-wizard/graphics/help/middle1.png | Bin 0 -> 1302 bytes .../graphics/help/middle1.png.import | 40 + addons/escoria-wizard/graphics/help/middle2.png | Bin 0 -> 407 bytes .../graphics/help/middle2.png.import | 40 + addons/escoria-wizard/graphics/help/middle3.png | Bin 0 -> 420 bytes .../graphics/help/middle3.png.import | 40 + addons/escoria-wizard/graphics/help/middleall.png | Bin 0 -> 171 bytes .../graphics/help/middleall.png.import | 40 + addons/escoria-wizard/graphics/help/right1.png | Bin 0 -> 322 bytes .../escoria-wizard/graphics/help/right1.png.import | 40 + addons/escoria-wizard/graphics/help/right2.png | Bin 0 -> 453 bytes .../escoria-wizard/graphics/help/right2.png.import | 40 + addons/escoria-wizard/graphics/help/right3.png | Bin 0 -> 364 bytes .../escoria-wizard/graphics/help/right3.png.import | 40 + addons/escoria-wizard/graphics/help/right4.png | Bin 0 -> 360 bytes .../escoria-wizard/graphics/help/right4.png.import | 40 + addons/escoria-wizard/graphics/help/right5.png | Bin 0 -> 384 bytes .../escoria-wizard/graphics/help/right5.png.import | 40 + addons/escoria-wizard/graphics/help/rightall.png | Bin 0 -> 162 bytes .../graphics/help/rightall.png.import | 40 + addons/escoria-wizard/graphics/icon.png | Bin 0 -> 2904 bytes addons/escoria-wizard/graphics/icon.png.import | 40 + addons/escoria-wizard/graphics/icon16x16.png | Bin 0 -> 241 bytes .../escoria-wizard/graphics/icon16x16.png.import | 40 + addons/escoria-wizard/graphics/inventory.png | Bin 0 -> 1161 bytes .../escoria-wizard/graphics/inventory.png.import | 40 + .../escoria-wizard/graphics/inventory_preview.png | Bin 0 -> 41995 bytes .../graphics/inventory_preview.png.import | 40 + addons/escoria-wizard/graphics/mark-animtest.png | Bin 0 -> 4720 bytes .../graphics/mark-animtest.png.import | 40 + addons/escoria-wizard/graphics/no_animation.png | Bin 0 -> 7920 bytes .../graphics/no_animation.png.import | 40 + addons/escoria-wizard/graphics/no_spritesheet.png | Bin 0 -> 10735 bytes .../graphics/no_spritesheet.png.import | 40 + addons/escoria-wizard/graphics/object_preview.png | Bin 0 -> 42899 bytes .../graphics/object_preview.png.import | 40 + addons/escoria-wizard/graphics/robot/idle_all.png | Bin 0 -> 21111 bytes .../graphics/robot/idle_all.png.import | 40 + addons/escoria-wizard/graphics/robot/open_all.png | Bin 0 -> 351187 bytes .../graphics/robot/open_all.png.import | 40 + addons/escoria-wizard/graphics/robot/talk_down.png | Bin 0 -> 129376 bytes .../graphics/robot/talk_down.png.import | 40 + .../graphics/robot/talk_downleft.png | Bin 0 -> 180733 bytes .../graphics/robot/talk_downleft.png.import | 40 + .../graphics/robot/talk_downright.png | Bin 0 -> 172763 bytes .../graphics/robot/talk_downright.png.import | 40 + .../escoria-wizard/graphics/robot/talk_right.png | Bin 0 -> 114653 bytes .../graphics/robot/talk_right.png.import | 40 + addons/escoria-wizard/graphics/robot/talk_up.png | Bin 0 -> 140713 bytes .../graphics/robot/talk_up.png.import | 40 + .../escoria-wizard/graphics/robot/talk_upright.png | Bin 0 -> 159846 bytes .../graphics/robot/talk_upright.png.import | 40 + addons/escoria-wizard/graphics/robot/walk_down.png | Bin 0 -> 36496 bytes .../graphics/robot/walk_down.png.import | 40 + .../graphics/robot/walk_downleft.png | Bin 0 -> 67899 bytes .../graphics/robot/walk_downleft.png.import | 40 + .../graphics/robot/walk_downright.png | Bin 0 -> 68562 bytes .../graphics/robot/walk_downright.png.import | 40 + .../escoria-wizard/graphics/robot/walk_right.png | Bin 0 -> 41141 bytes .../graphics/robot/walk_right.png.import | 40 + addons/escoria-wizard/graphics/robot/walk_up.png | Bin 0 -> 29360 bytes .../graphics/robot/walk_up.png.import | 40 + .../escoria-wizard/graphics/robot/walk_upright.png | Bin 0 -> 60858 bytes .../graphics/robot/walk_upright.png.import | 40 + addons/escoria-wizard/graphics/room.png | Bin 0 -> 2171 bytes addons/escoria-wizard/graphics/room.png.import | 40 + addons/escoria-wizard/graphics/roomitem.png | Bin 0 -> 1857 bytes addons/escoria-wizard/graphics/roomitem.png.import | 40 + addons/escoria-wizard/graphics/wand.png | Bin 0 -> 4030 bytes addons/escoria-wizard/graphics/wand.png.import | 40 + addons/escoria-wizard/help_window.gd | 54 + addons/escoria-wizard/help_window.gd.uid | 1 + addons/escoria-wizard/help_window.tscn | 556 ++++++ addons/escoria-wizard/item_creator.gd | 318 ++++ addons/escoria-wizard/item_creator.gd.uid | 1 + addons/escoria-wizard/plugin.cfg | 7 + addons/escoria-wizard/plugin.gd | 59 + addons/escoria-wizard/plugin.gd.uid | 1 + addons/escoria-wizard/room_script_template.esc | 39 + build_escoria_docs.sh | 76 + default_env.tres | 7 + escoria-wizard.conf | Bin 0 -> 34 bytes export_presets.cfg | 637 +++++++ game.esc | 5 + .../Assets.xcassets/AppIcon.appiconset/100.png | Bin 0 -> 8370 bytes .../AppIcon.appiconset/100.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/1024.png | Bin 0 -> 176294 bytes .../AppIcon.appiconset/1024.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/114.png | Bin 0 -> 9530 bytes .../AppIcon.appiconset/114.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/120.png | Bin 0 -> 10077 bytes .../AppIcon.appiconset/120.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/128.png | Bin 0 -> 10777 bytes .../AppIcon.appiconset/128.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/144.png | Bin 0 -> 12408 bytes .../AppIcon.appiconset/144.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/152.png | Bin 0 -> 13299 bytes .../AppIcon.appiconset/152.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/16.png | Bin 0 -> 812 bytes .../AppIcon.appiconset/16.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/167.png | Bin 0 -> 14827 bytes .../AppIcon.appiconset/167.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/172.png | Bin 0 -> 15316 bytes .../AppIcon.appiconset/172.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/180.png | Bin 0 -> 16144 bytes .../AppIcon.appiconset/180.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/196.png | Bin 0 -> 17903 bytes .../AppIcon.appiconset/196.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/20.png | Bin 0 -> 1065 bytes .../AppIcon.appiconset/20.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/216.png | Bin 0 -> 20111 bytes .../AppIcon.appiconset/216.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/256.png | Bin 0 -> 24393 bytes .../AppIcon.appiconset/256.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/29.png | Bin 0 -> 1693 bytes .../AppIcon.appiconset/29.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/32.png | Bin 0 -> 1959 bytes .../AppIcon.appiconset/32.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/40.png | Bin 0 -> 2622 bytes .../AppIcon.appiconset/40.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/48.png | Bin 0 -> 3400 bytes .../AppIcon.appiconset/48.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/50.png | Bin 0 -> 3606 bytes .../AppIcon.appiconset/50.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/512.png | Bin 0 -> 62997 bytes .../AppIcon.appiconset/512.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/55.png | Bin 0 -> 4116 bytes .../AppIcon.appiconset/55.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/57.png | Bin 0 -> 4393 bytes .../AppIcon.appiconset/57.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/58.png | Bin 0 -> 4415 bytes .../AppIcon.appiconset/58.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/60.png | Bin 0 -> 4544 bytes .../AppIcon.appiconset/60.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/64.png | Bin 0 -> 4945 bytes .../AppIcon.appiconset/64.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/72.png | Bin 0 -> 5685 bytes .../AppIcon.appiconset/72.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/76.png | Bin 0 -> 6129 bytes .../AppIcon.appiconset/76.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/80.png | Bin 0 -> 6496 bytes .../AppIcon.appiconset/80.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/87.png | Bin 0 -> 7125 bytes .../AppIcon.appiconset/87.png.import | 40 + .../Assets.xcassets/AppIcon.appiconset/88.png | Bin 0 -> 7232 bytes .../AppIcon.appiconset/88.png.import | 40 + .../AppIcon.appiconset/Contents.json | 1 + game/appicons/android/mipmap-hdpi/ic_launcher.png | Bin 0 -> 5483 bytes .../android/mipmap-hdpi/ic_launcher.png.import | 40 + game/appicons/android/mipmap-mdpi/ic_launcher.png | Bin 0 -> 3275 bytes .../android/mipmap-mdpi/ic_launcher.png.import | 40 + game/appicons/android/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 7682 bytes .../android/mipmap-xhdpi/ic_launcher.png.import | 40 + .../appicons/android/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 12158 bytes .../android/mipmap-xxhdpi/ic_launcher.png.import | 40 + .../android/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 16987 bytes .../android/mipmap-xxxhdpi/ic_launcher.png.import | 40 + game/appicons/appstore.png | Bin 0 -> 176294 bytes game/appicons/appstore.png.import | 40 + game/appicons/playstore.png | Bin 0 -> 62299 bytes game/appicons/playstore.png.import | 40 + game/characters/weener/png/weener.png | Bin 0 -> 36510 bytes game/characters/weener/png/weener.png.import | 40 + game/characters/weener/png/weener.png~ | Bin 0 -> 36510 bytes game/characters/weener/weener.tscn | 325 ++++ game/fonts/caslonantique.tres | 7 + game/fonts/caslonantique.ttf | Bin 0 -> 147756 bytes game/fonts/caslonantique.ttf.import | 36 + game/items/escitems/button.tscn | 96 ++ game/items/inventory/pen.esc | 3 + game/items/inventory/pen.png | Bin 0 -> 310 bytes game/items/inventory/pen.png.import | 40 + game/items/inventory/pen.tscn | 23 + game/items/textures/bottle.png | Bin 0 -> 373 bytes game/items/textures/bottle.png.import | 40 + game/rooms/intro/esc/intro.esc | 13 + game/rooms/intro/escoria-logo-comet-anim.png | Bin 0 -> 85426 bytes .../rooms/intro/escoria-logo-comet-anim.png.import | 40 + game/rooms/intro/escoria-logo-comet.png | Bin 0 -> 28142 bytes game/rooms/intro/escoria-logo-comet.png.import | 40 + game/rooms/intro/escoria_background.png | Bin 0 -> 71094 bytes game/rooms/intro/escoria_background.png.import | 40 + game/rooms/intro/escoria_background_1080p.png | Bin 0 -> 75553 bytes .../intro/escoria_background_1080p.png.import | 40 + game/rooms/intro/intro_background_space.png | Bin 0 -> 944543 bytes game/rooms/intro/intro_background_space.png.import | 40 + game/rooms/intro/intro_background_space_1080p.png | Bin 0 -> 1714359 bytes .../intro/intro_background_space_1080p.png.import | 40 + game/rooms/intro/intro_room.tscn | 306 ++++ game/rooms/no_escoria/no_escoria.tscn | 13 + game/rooms/no_escoria/player.gd | 32 + game/rooms/no_escoria/player.gd.uid | 1 + game/rooms/test/phone.esc | 8 + game/rooms/test/room_test.esc | 4 + game/rooms/test/test.tscn | 113 ++ game/rooms/test/test_bg.png | Bin 0 -> 2500226 bytes game/rooms/test/test_bg.png.import | 40 + game/rooms/test_kitchen/kitchen.png | Bin 0 -> 2493262 bytes game/rooms/test_kitchen/kitchen.png.import | 40 + game/rooms/test_kitchen/room_kitchen.esc | 4 + game/rooms/test_kitchen/test_kitchen.tscn | 53 + game/shaders/outline.gdshader | 64 + game/shaders/outline.gdshader.uid | 1 + game/shaders/shadermaterial_outline_green5px.tres | 11 + game/translations/game.csv.import | 19 + game/translations/main_menu.csv.import | 19 + icon.png | Bin 0 -> 2898 bytes icon.png.import | 40 + icon.svg | 1 - icon.svg.import | 43 - mus/intro.wav | Bin 6342104 -> 0 bytes mus/intro.wav.import | 24 - project.godot | 114 +- room/kitchen/kitchen.png | Bin 2493262 -> 0 bytes room/kitchen/kitchen.png.import | 40 - room/kitchen/kitchenbottomlayer.png | Bin 2767048 -> 0 bytes room/kitchen/kitchenbottomlayer.png.import | 40 - room/kitchen/kitchentoplayer.png | Bin 1571856 -> 0 bytes room/kitchen/kitchentoplayer.png.import | 40 - room/start/room.jpg | Bin 197584 -> 0 bytes room/start/room.jpg.import | 40 - room/start/testprop.png | Bin 4982 -> 0 bytes room/start/testprop.png.import | 40 - scenes/char/weener.gd | 110 -- scenes/char/weener.gd.uid | 1 - scenes/char/weener.tscn | 109 -- scenes/item/door.gd | 14 - scenes/item/door.gd.uid | 1 - scenes/item/door.tscn | 14 - scenes/item/inventory.gd | 12 - scenes/item/inventory.gd.uid | 1 - scenes/item/inventory.tscn | 3 - scenes/item/inventory_item.gd | 1 - scenes/item/inventory_item.gd.uid | 1 - scenes/item/item.gd | 42 - scenes/item/item.gd.uid | 1 - scenes/item/item.tscn | 19 - scenes/room/kitchen.tscn | 32 - scenes/room/room.gd | 7 - scenes/room/room.gd.uid | 1 - scenes/room/room.tscn | 19 - scenes/room/spawnpoint.gd | 1 - scenes/room/spawnpoint.gd.uid | 1 - scenes/room/spawnpoint.tscn | 6 - scenes/room/start.tscn | 78 - scenes/ui/menu.gd | 42 - scenes/ui/menu.gd.uid | 1 - scenes/ui/menu.tscn | 76 - scenes/ui/options.gd | 7 - scenes/ui/options.gd.uid | 1 - scenes/ui/options.tscn | 54 - scenes/ui/title.tscn | 72 - scenes/ui/title_button.tscn | 8 - sfx/telephone.wav | Bin 281900 -> 0 bytes sfx/telephone.wav.import | 24 - spr/weener/crouch.png | Bin 28039 -> 0 bytes spr/weener/crouch.png.import | 40 - spr/weener/grab.png | Bin 46706 -> 0 bytes spr/weener/grab.png.import | 40 - spr/weener/run1.png | Bin 60115 -> 0 bytes spr/weener/run1.png.import | 40 - spr/weener/run2.png | Bin 58235 -> 0 bytes spr/weener/run2.png.import | 40 - spr/weener/stand.png | Bin 59490 -> 0 bytes spr/weener/stand.png.import | 40 - spr/weener/walk.png | Bin 65558 -> 0 bytes spr/weener/walk.png.import | 40 - .../__pycache__/reformat_docstrings.cpython-39.pyc | Bin 0 -> 23998 bytes tools/docstring_formatter.py | 908 ++++++++++ tools/reformat_docstrings.py | 1132 ++++++++++++ ui/Baskerville.ttf | Bin 54976 -> 0 bytes ui/Baskerville.ttf.import | 36 - ui/cursor.png | Bin 4155 -> 0 bytes ui/cursor.png.import | 40 - ui/cursor_shadow.png | Bin 4553 -> 0 bytes ui/cursor_shadow.png.import | 40 - ui/frame.png | Bin 60290 -> 0 bytes ui/frame.png.import | 40 - ui/settings.png | Bin 10528 -> 0 bytes ui/settings.png.import | 40 - ui/settings_active.png | Bin 17465 -> 0 bytes ui/settings_active.png.import | 40 - ui/testmenu.png | Bin 73748 -> 0 bytes ui/testmenu.png.import | 40 - ui/textbox.png | Bin 4040 -> 0 bytes ui/textbox.png.import | 40 - ui/textbox_arrow.png | Bin 1098 -> 0 bytes ui/textbox_arrow.png.import | 40 - ui/textbox_container.png | Bin 3309 -> 0 bytes ui/textbox_container.png.import | 40 - ui/title.png | Bin 352593 -> 0 bytes ui/title.png.import | 40 - 995 files changed, 57117 insertions(+), 1725 deletions(-) create mode 100644 action_defaults.esc create mode 100644 addons/escoria-core/_test/deprecated/test_esc_compiler.gd create mode 100644 addons/escoria-core/_test/deprecated/test_esc_compiler.gd.uid create mode 100644 addons/escoria-core/_test/deprecated/test_esc_compiler.tscn create mode 100644 addons/escoria-core/_test/test_migrations.gd create mode 100644 addons/escoria-core/_test/test_migrations.gd.uid create mode 100644 addons/escoria-core/_test/test_migrations.tscn create mode 100644 addons/escoria-core/_test/testversions/1.0.1.gd create mode 100644 addons/escoria-core/_test/testversions/1.0.1.gd.uid create mode 100644 addons/escoria-core/_test/testversions/1.1.0.gd create mode 100644 addons/escoria-core/_test/testversions/1.1.0.gd.uid create mode 100644 addons/escoria-core/buses/default_bus_layout.tres create mode 100644 addons/escoria-core/design/esc_background.svg create mode 100644 addons/escoria-core/design/esc_background.svg.import create mode 100644 addons/escoria-core/design/esc_exit.svg create mode 100644 addons/escoria-core/design/esc_exit.svg.import create mode 100644 addons/escoria-core/design/esc_item.svg create mode 100644 addons/escoria-core/design/esc_item.svg.import create mode 100644 addons/escoria-core/design/esc_location.svg create mode 100644 addons/escoria-core/design/esc_location.svg.import create mode 100644 addons/escoria-core/design/esc_player.svg create mode 100644 addons/escoria-core/design/esc_player.svg.import create mode 100644 addons/escoria-core/design/esc_room.svg create mode 100644 addons/escoria-core/design/esc_room.svg.import create mode 100644 addons/escoria-core/design/esc_terrain.svg create mode 100644 addons/escoria-core/design/esc_terrain.svg.import create mode 100644 addons/escoria-core/design/escoria-logo-small.png create mode 100644 addons/escoria-core/design/escoria-logo-small.png.import create mode 100644 addons/escoria-core/design/escoria-logo.png create mode 100644 addons/escoria-core/design/escoria-logo.png.import create mode 100644 addons/escoria-core/design/escoria-logo.svg create mode 100644 addons/escoria-core/design/escoria-logo.svg.import create mode 100644 addons/escoria-core/design/icons.sketch create mode 100644 addons/escoria-core/game/assets/images/no_image.png create mode 100644 addons/escoria-core/game/assets/images/no_image.png.import create mode 100644 addons/escoria-core/game/assets/images/no_image.xcf create mode 100644 addons/escoria-core/game/assets/images/white.png create mode 100644 addons/escoria-core/game/assets/images/white.png.import create mode 100644 addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd create mode 100644 addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/README.md create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/accept_input.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/accept_input.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/anim.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/anim.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/block_say.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/block_say.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_push.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_push.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_push_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_push_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_limits.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_limits.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_target.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_target.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_target_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_target_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_shift.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_shift.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_shift_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/camera_shift_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/change_scene.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/change_scene.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/change_scene_godot.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/change_scene_godot.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/custom.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/custom.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/enable_terrain.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/enable_terrain.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/end_block_say.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/end_block_say.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/hide_menu.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/hide_menu.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/inventory_add.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/inventory_add.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/inventory_remove.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/inventory_remove.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/play_snd.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/play_snd.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/print_internal.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/print_internal.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/queue_event.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/queue_event.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/queue_resource.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/queue_resource.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/repeat.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/repeat.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/save_game.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/save_game.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/say.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/say.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/sched_event.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/sched_event.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_active.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_active.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_active_if_exists.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_active_if_exists.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_angle.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_angle.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_direction.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_direction.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_global.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_global.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_globals.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_globals.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_gui_visible.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_gui_visible.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_interactive.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_interactive.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_item_custom_data.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_item_custom_data.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_speed.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_speed.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_state.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/set_state.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/slide.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/slide.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/slide_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/slide_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/spawn.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/spawn.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/stop.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/stop.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/stop_snd.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/stop_snd.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/teleport.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/teleport.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/teleport_pos.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/teleport_pos.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/transition.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/transition.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/turn_to.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/turn_to.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/wait.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/wait.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/walk.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/walk.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/walk_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/walk_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos_block.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos_block.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_token_type.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/compiler/esc_token_type.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/design-principles.md create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_command_registry.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_command_registry.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_compiler.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_compiler.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_inventory_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_inventory_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_room_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/esc_room_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_base_command.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_base_command.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_camera_base_command.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_camera_base_command.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_command.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_command.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_command_argument_descriptor.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_command_argument_descriptor.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_dialog.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_dialog.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_dialog_option.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_dialog_option.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_event.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_event.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_execution.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_execution.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_object.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_object.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_room_container.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_room_container.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_room_objects.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_room_objects.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_room_objects_key.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_room_objects_key.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_room_terrains.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_room_terrains.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_scheduled_event.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_scheduled_event.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_script.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_script.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd create mode 100644 addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_animation_player.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_animation_player.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_background.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_background.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_dialog_location.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_dialog_location.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_exit.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_exit.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_game.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_game.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_interaction_location.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_interaction_location.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_inventory_item.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_inventory_item.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_item.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_item.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_location.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_location.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_player.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_player.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_resource_cache.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_resource_cache.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_room.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_room.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_terrain.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_terrain.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_tooltip.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_tooltip.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/esc_walk_context.gd create mode 100644 addons/escoria-core/game/core-scripts/esc_walk_context.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/migrations/esc_migration.gd create mode 100644 addons/escoria-core/game/core-scripts/migrations/esc_migration.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/plugins/escoria_plugin.gd create mode 100644 addons/escoria-core/game/core-scripts/plugins/escoria_plugin.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/resources/esc_animationname.gd create mode 100644 addons/escoria-core/game/core-scripts/resources/esc_animationname.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd create mode 100644 addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd create mode 100644 addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/resources/esc_resource_descriptor.gd create mode 100644 addons/escoria-core/game/core-scripts/resources/esc_resource_descriptor.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/save_data/esc_save_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/save_data/esc_save_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/save_data/esc_savegame.gd create mode 100644 addons/escoria-core/game/core-scripts/save_data/esc_savegame.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/save_data/esc_savesettings.gd create mode 100644 addons/escoria-core/game/core-scripts/save_data/esc_savesettings.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/save_data/esc_settings_manager.gd create mode 100644 addons/escoria-core/game/core-scripts/save_data/esc_settings_manager.gd.uid create mode 100644 addons/escoria-core/game/core-scripts/utils/esc_utils.gd create mode 100644 addons/escoria-core/game/core-scripts/utils/esc_utils.gd.uid create mode 100644 addons/escoria-core/game/esc_autoload.gd create mode 100644 addons/escoria-core/game/esc_autoload.gd.uid create mode 100644 addons/escoria-core/game/esc_dependency_injector.gd create mode 100644 addons/escoria-core/game/esc_dependency_injector.gd.uid create mode 100644 addons/escoria-core/game/esc_inputs_manager.gd create mode 100644 addons/escoria-core/game/esc_inputs_manager.gd.uid create mode 100644 addons/escoria-core/game/esc_project_settings_manager.gd create mode 100644 addons/escoria-core/game/esc_project_settings_manager.gd.uid create mode 100644 addons/escoria-core/game/escoria.gd create mode 100644 addons/escoria-core/game/escoria.gd.uid create mode 100644 addons/escoria-core/game/escoria.tscn create mode 100644 addons/escoria-core/game/main.gd create mode 100644 addons/escoria-core/game/main.gd.uid create mode 100644 addons/escoria-core/game/main.tscn create mode 100644 addons/escoria-core/game/main_scene.gd create mode 100644 addons/escoria-core/game/main_scene.gd.uid create mode 100644 addons/escoria-core/game/main_scene.tscn create mode 100644 addons/escoria-core/game/scenes/camera_player/camera.tscn create mode 100644 addons/escoria-core/game/scenes/camera_player/esc_camera.gd create mode 100644 addons/escoria-core/game/scenes/camera_player/esc_camera.gd.uid create mode 100644 addons/escoria-core/game/scenes/camera_player/esc_camera_limits.gd create mode 100644 addons/escoria-core/game/scenes/camera_player/esc_camera_limits.gd.uid create mode 100644 addons/escoria-core/game/scenes/dialogs/esc_dialog_manager.gd create mode 100644 addons/escoria-core/game/scenes/dialogs/esc_dialog_manager.gd.uid create mode 100644 addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd create mode 100644 addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd.uid create mode 100644 addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd create mode 100644 addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd.uid create mode 100644 addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd create mode 100644 addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd.uid create mode 100644 addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.tscn create mode 100644 addons/escoria-core/game/scenes/inventory/inventory_ui.gd create mode 100644 addons/escoria-core/game/scenes/inventory/inventory_ui.gd.uid create mode 100644 addons/escoria-core/game/scenes/sound/esc_ambient_player.gd create mode 100644 addons/escoria-core/game/scenes/sound/esc_ambient_player.gd.uid create mode 100644 addons/escoria-core/game/scenes/sound/esc_ambient_player.tscn create mode 100644 addons/escoria-core/game/scenes/sound/esc_music_player.gd create mode 100644 addons/escoria-core/game/scenes/sound/esc_music_player.gd.uid create mode 100644 addons/escoria-core/game/scenes/sound/esc_music_player.tscn create mode 100644 addons/escoria-core/game/scenes/sound/esc_sound_player.gd create mode 100644 addons/escoria-core/game/scenes/sound/esc_sound_player.gd.uid create mode 100644 addons/escoria-core/game/scenes/sound/esc_sound_player.tscn create mode 100644 addons/escoria-core/game/scenes/sound/esc_speech_player.gd create mode 100644 addons/escoria-core/game/scenes/sound/esc_speech_player.gd.uid create mode 100644 addons/escoria-core/game/scenes/sound/esc_speech_player.tscn create mode 100644 addons/escoria-core/game/scenes/transitions/esc_transition_player.gd create mode 100644 addons/escoria-core/game/scenes/transitions/esc_transition_player.gd.uid create mode 100644 addons/escoria-core/game/scenes/transitions/masks/curtain.png create mode 100644 addons/escoria-core/game/scenes/transitions/masks/curtain.png.import create mode 100644 addons/escoria-core/game/scenes/transitions/masks/from_center.png create mode 100644 addons/escoria-core/game/scenes/transitions/masks/from_center.png.import create mode 100644 addons/escoria-core/game/scenes/transitions/masks/shards.png create mode 100644 addons/escoria-core/game/scenes/transitions/masks/shards.png.import create mode 100644 addons/escoria-core/game/scenes/transitions/shaders/curtain.material create mode 100644 addons/escoria-core/game/scenes/transitions/shaders/fade_black.material create mode 100644 addons/escoria-core/game/scenes/transitions/shaders/fade_white.material create mode 100644 addons/escoria-core/game/scenes/transitions/shaders/from_center.material create mode 100644 addons/escoria-core/game/scenes/transitions/shaders/shards.material create mode 100644 addons/escoria-core/game/scenes/transitions/transition.tscn create mode 100644 addons/escoria-core/game/tween3.gd create mode 100644 addons/escoria-core/game/tween3.gd.uid create mode 100644 addons/escoria-core/patterns/state_machine/state.gd create mode 100644 addons/escoria-core/patterns/state_machine/state.gd.uid create mode 100644 addons/escoria-core/patterns/state_machine/state_machine.gd create mode 100644 addons/escoria-core/patterns/state_machine/state_machine.gd.uid create mode 100644 addons/escoria-core/plugin.cfg create mode 100644 addons/escoria-core/plugin.gd create mode 100644 addons/escoria-core/plugin.gd.uid create mode 100644 addons/escoria-core/testing/NotoSans-Regular.ttf create mode 100644 addons/escoria-core/testing/NotoSans-Regular.ttf.import create mode 100644 addons/escoria-core/testing/README create mode 100644 addons/escoria-core/testing/player_angles_finder.gd create mode 100644 addons/escoria-core/testing/player_angles_finder.gd.uid create mode 100644 addons/escoria-core/testing/player_angles_finder.tscn create mode 100644 addons/escoria-core/testing/rtl_screen_offset_testing.gd create mode 100644 addons/escoria-core/testing/rtl_screen_offset_testing.gd.uid create mode 100644 addons/escoria-core/testing/rtl_screen_offset_testing.tscn create mode 100644 addons/escoria-core/tools/logging/esc_log_level.gd create mode 100644 addons/escoria-core/tools/logging/esc_log_level.gd.uid create mode 100644 addons/escoria-core/tools/logging/esc_logger.gd create mode 100644 addons/escoria-core/tools/logging/esc_logger.gd.uid create mode 100644 addons/escoria-core/tools/logging/esc_safe_logging.gd create mode 100644 addons/escoria-core/tools/logging/esc_safe_logging.gd.uid create mode 100644 addons/escoria-core/tools/script_analysis/analyzers/esc_exit_scene_script_analyzer.gd create mode 100644 addons/escoria-core/tools/script_analysis/analyzers/esc_exit_scene_script_analyzer.gd.uid create mode 100644 addons/escoria-core/tools/script_analysis/analyzers/esc_script_analyzer.gd create mode 100644 addons/escoria-core/tools/script_analysis/analyzers/esc_script_analyzer.gd.uid create mode 100644 addons/escoria-core/tools/script_analysis/esc_ashes_analyzer.gd create mode 100644 addons/escoria-core/tools/script_analysis/esc_ashes_analyzer.gd.uid create mode 100644 addons/escoria-core/tools/script_analysis/esc_static_analyzers.gd create mode 100644 addons/escoria-core/tools/script_analysis/esc_static_analyzers.gd.uid create mode 100644 addons/escoria-core/ui_library/fonts/caslonantique.tres create mode 100644 addons/escoria-core/ui_library/fonts/caslonantique.ttf create mode 100644 addons/escoria-core/ui_library/fonts/caslonantique.ttf.import create mode 100644 addons/escoria-core/ui_library/inventory/esc_inventory_button.gd create mode 100644 addons/escoria-core/ui_library/inventory/esc_inventory_button.gd.uid create mode 100644 addons/escoria-core/ui_library/inventory/esc_inventory_container.gd create mode 100644 addons/escoria-core/ui_library/inventory/esc_inventory_container.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd create mode 100644 addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/load_save/load/load_game.gd create mode 100644 addons/escoria-core/ui_library/menus/load_save/load/load_game.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn create mode 100644 addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd create mode 100644 addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/save_game.gd create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/save_game.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn create mode 100644 addons/escoria-core/ui_library/menus/main_menu/main_menu.gd create mode 100644 addons/escoria-core/ui_library/menus/main_menu/main_menu.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn create mode 100644 addons/escoria-core/ui_library/menus/options/flags/de.png create mode 100644 addons/escoria-core/ui_library/menus/options/flags/de.png.import create mode 100644 addons/escoria-core/ui_library/menus/options/flags/en.png create mode 100644 addons/escoria-core/ui_library/menus/options/flags/en.png.import create mode 100644 addons/escoria-core/ui_library/menus/options/flags/es.png create mode 100644 addons/escoria-core/ui_library/menus/options/flags/es.png.import create mode 100644 addons/escoria-core/ui_library/menus/options/flags/fr.png create mode 100644 addons/escoria-core/ui_library/menus/options/flags/fr.png.import create mode 100644 addons/escoria-core/ui_library/menus/options/options.gd create mode 100644 addons/escoria-core/ui_library/menus/options/options.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/options/options.tscn create mode 100644 addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd create mode 100644 addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd.uid create mode 100644 addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn create mode 100644 addons/escoria-core/ui_library/theme.tres create mode 100644 addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd create mode 100644 addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd.uid create mode 100644 addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn create mode 100644 addons/escoria-core/ui_library/tools/room_select/room_select.gd create mode 100644 addons/escoria-core/ui_library/tools/room_select/room_select.gd.uid create mode 100644 addons/escoria-core/ui_library/tools/room_select/room_select.tscn create mode 100644 addons/escoria-dialog-simple/chooser/simple.gd create mode 100644 addons/escoria-dialog-simple/chooser/simple.gd.uid create mode 100644 addons/escoria-dialog-simple/chooser/simple.tscn create mode 100644 addons/escoria-dialog-simple/esc_dialog_simple.gd create mode 100644 addons/escoria-dialog-simple/esc_dialog_simple.gd.uid create mode 100644 addons/escoria-dialog-simple/esc_dialog_simple_settings.gd create mode 100644 addons/escoria-dialog-simple/esc_dialog_simple_settings.gd.uid create mode 100644 addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd create mode 100644 addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd.uid create mode 100644 addons/escoria-dialog-simple/plugin.cfg create mode 100644 addons/escoria-dialog-simple/plugin.gd create mode 100644 addons/escoria-dialog-simple/plugin.gd.uid create mode 100644 addons/escoria-dialog-simple/states/dialog_choices.gd create mode 100644 addons/escoria-dialog-simple/states/dialog_choices.gd.uid create mode 100644 addons/escoria-dialog-simple/states/dialog_finish.gd create mode 100644 addons/escoria-dialog-simple/states/dialog_finish.gd.uid create mode 100644 addons/escoria-dialog-simple/states/dialog_idle.gd create mode 100644 addons/escoria-dialog-simple/states/dialog_idle.gd.uid create mode 100644 addons/escoria-dialog-simple/states/dialog_interrupt.gd create mode 100644 addons/escoria-dialog-simple/states/dialog_interrupt.gd.uid create mode 100644 addons/escoria-dialog-simple/states/dialog_say.gd create mode 100644 addons/escoria-dialog-simple/states/dialog_say.gd.uid create mode 100644 addons/escoria-dialog-simple/states/dialog_say_fast.gd create mode 100644 addons/escoria-dialog-simple/states/dialog_say_fast.gd.uid create mode 100644 addons/escoria-dialog-simple/states/dialog_say_finish.gd create mode 100644 addons/escoria-dialog-simple/states/dialog_say_finish.gd.uid create mode 100644 addons/escoria-dialog-simple/states/dialog_visible.gd create mode 100644 addons/escoria-dialog-simple/states/dialog_visible.gd.uid create mode 100644 addons/escoria-dialog-simple/types/avatar.gd create mode 100644 addons/escoria-dialog-simple/types/avatar.gd.uid create mode 100644 addons/escoria-dialog-simple/types/avatar.tscn create mode 100644 addons/escoria-dialog-simple/types/floating.gd create mode 100644 addons/escoria-dialog-simple/types/floating.gd.uid create mode 100644 addons/escoria-dialog-simple/types/floating.tscn create mode 100644 addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.tres create mode 100644 addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf create mode 100644 addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf.import create mode 100644 addons/escoria-ui-keyboard-9verbs/game.gd create mode 100644 addons/escoria-ui-keyboard-9verbs/game.gd.uid create mode 100644 addons/escoria-ui-keyboard-9verbs/game.tscn create mode 100644 addons/escoria-ui-keyboard-9verbs/input_map.gd create mode 100644 addons/escoria-ui-keyboard-9verbs/input_map.gd.uid create mode 100644 addons/escoria-ui-keyboard-9verbs/inventory/inventory_ui.tscn create mode 100644 addons/escoria-ui-keyboard-9verbs/plugin.cfg create mode 100644 addons/escoria-ui-keyboard-9verbs/plugin.gd create mode 100644 addons/escoria-ui-keyboard-9verbs/plugin.gd.uid create mode 100644 addons/escoria-ui-keyboard-9verbs/theme.tres create mode 100644 addons/escoria-ui-keyboard-9verbs/tooltip/action_target_tooltip.tscn create mode 100644 addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd create mode 100644 addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd.uid create mode 100644 addons/escoria-ui-keyboard-9verbs/verbs_menu.gd create mode 100644 addons/escoria-ui-keyboard-9verbs/verbs_menu.gd.uid create mode 100644 addons/escoria-ui-keyboard-9verbs/verbs_menu.tscn create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_dialog.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_dialog.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_examine.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_examine.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_foot.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_foot.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_glasses.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_glasses.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_gun.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_gun.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_hand.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_hand.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_heal.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_heal.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_lock.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_lock.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_pen.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_pen.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_punch.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_punch.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_shield.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_shield.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_tool.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_tool.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_wait.png create mode 100644 addons/escoria-ui-simplemouse/cursors/cursor_wait.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_e.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_e.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_n.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_n.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_s.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_s.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_se.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_se.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_w.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/arrow_w.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/hand_open.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/hand_open.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/line_cross.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/line_cross.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/look_b.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/look_b.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/target_a.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/target_a.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/new/zoom.png create mode 100644 addons/escoria-ui-simplemouse/cursors/new/zoom.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png.import create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg create mode 100644 addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg.import create mode 100644 addons/escoria-ui-simplemouse/fonts/caslonantique.tres create mode 100644 addons/escoria-ui-simplemouse/fonts/caslonantique.ttf create mode 100644 addons/escoria-ui-simplemouse/fonts/caslonantique.ttf.import create mode 100644 addons/escoria-ui-simplemouse/game.gd create mode 100644 addons/escoria-ui-simplemouse/game.gd.uid create mode 100644 addons/escoria-ui-simplemouse/game.tscn create mode 100644 addons/escoria-ui-simplemouse/images/frame.png create mode 100644 addons/escoria-ui-simplemouse/images/frame.png.import create mode 100644 addons/escoria-ui-simplemouse/images/inventory_bg.png create mode 100644 addons/escoria-ui-simplemouse/images/inventory_bg.png.import create mode 100644 addons/escoria-ui-simplemouse/images/inventory_icon.png create mode 100644 addons/escoria-ui-simplemouse/images/inventory_icon.png.import create mode 100644 addons/escoria-ui-simplemouse/images/inventory_panel.png create mode 100644 addons/escoria-ui-simplemouse/images/inventory_panel.png.import create mode 100644 addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg create mode 100644 addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg.import create mode 100644 addons/escoria-ui-simplemouse/inventory/inventory_ui.gd create mode 100644 addons/escoria-ui-simplemouse/inventory/inventory_ui.gd.uid create mode 100644 addons/escoria-ui-simplemouse/inventory/inventory_ui.tscn create mode 100644 addons/escoria-ui-simplemouse/plugin.cfg create mode 100644 addons/escoria-ui-simplemouse/plugin.gd create mode 100644 addons/escoria-ui-simplemouse/plugin.gd.uid create mode 100644 addons/escoria-ui-simplemouse/theme.tres create mode 100644 addons/escoria-ui-simplemouse/tooltip/target_tooltip.tscn create mode 100644 addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd create mode 100644 addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd.uid create mode 100644 addons/escoria-ui-simplemouse/verbs_mouseicons.gd create mode 100644 addons/escoria-ui-simplemouse/verbs_mouseicons.gd.uid create mode 100644 addons/escoria-ui-simplemouse/verbs_mouseicons.tscn create mode 100644 addons/escoria-wizard/BackgroundItem.gd create mode 100644 addons/escoria-wizard/BackgroundItem.gd.uid create mode 100644 addons/escoria-wizard/CharacterCreator.gd create mode 100644 addons/escoria-wizard/CharacterCreator.gd.uid create mode 100644 addons/escoria-wizard/CharacterCreator.tscn create mode 100644 addons/escoria-wizard/CharacterCreator.tscn.bak create mode 100644 addons/escoria-wizard/ItemCreator.tscn create mode 100644 addons/escoria-wizard/RoomCreator.gd create mode 100644 addons/escoria-wizard/RoomCreator.gd.uid create mode 100644 addons/escoria-wizard/RoomCreator.tscn create mode 100644 addons/escoria-wizard/background.pdn create mode 100644 addons/escoria-wizard/draw_frame_rects.gd create mode 100644 addons/escoria-wizard/draw_frame_rects.gd.uid create mode 100644 addons/escoria-wizard/escoria_wizard.gd create mode 100644 addons/escoria-wizard/escoria_wizard.gd.uid create mode 100644 addons/escoria-wizard/escoria_wizard.tscn create mode 100644 addons/escoria-wizard/graphics/background.png create mode 100644 addons/escoria-wizard/graphics/background.png.import create mode 100644 addons/escoria-wizard/graphics/background_preview.png create mode 100644 addons/escoria-wizard/graphics/background_preview.png.import create mode 100644 addons/escoria-wizard/graphics/character.png create mode 100644 addons/escoria-wizard/graphics/character.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_down.png create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_down.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_downleft.png create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_downleft.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_downright.png create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_downright.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_left.png create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_left.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_right.png create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_right.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_up.png create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_up.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_upleft.png create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_upleft.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_upright.png create mode 100644 addons/escoria-wizard/graphics/darkgreenarrow_upright.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_d.png create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_d.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_dl.png create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_dl.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_dr.png create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_dr.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_l.png create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_l.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_r.png create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_r.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_u.png create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_u.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_ul.png create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_ul.png.import create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_ur.png create mode 100644 addons/escoria-wizard/graphics/darkgreyarrow_ur.png.import create mode 100644 addons/escoria-wizard/graphics/greenarrow_down.png create mode 100644 addons/escoria-wizard/graphics/greenarrow_down.png.import create mode 100644 addons/escoria-wizard/graphics/greenarrow_downleft.png create mode 100644 addons/escoria-wizard/graphics/greenarrow_downleft.png.import create mode 100644 addons/escoria-wizard/graphics/greenarrow_downright.png create mode 100644 addons/escoria-wizard/graphics/greenarrow_downright.png.import create mode 100644 addons/escoria-wizard/graphics/greenarrow_left.png create mode 100644 addons/escoria-wizard/graphics/greenarrow_left.png.import create mode 100644 addons/escoria-wizard/graphics/greenarrow_right.png create mode 100644 addons/escoria-wizard/graphics/greenarrow_right.png.import create mode 100644 addons/escoria-wizard/graphics/greenarrow_up.png create mode 100644 addons/escoria-wizard/graphics/greenarrow_up.png.import create mode 100644 addons/escoria-wizard/graphics/greenarrow_upleft.png create mode 100644 addons/escoria-wizard/graphics/greenarrow_upleft.png.import create mode 100644 addons/escoria-wizard/graphics/greenarrow_upright.png create mode 100644 addons/escoria-wizard/graphics/greenarrow_upright.png.import create mode 100644 addons/escoria-wizard/graphics/greyarrow_d.png create mode 100644 addons/escoria-wizard/graphics/greyarrow_d.png.import create mode 100644 addons/escoria-wizard/graphics/greyarrow_dl.png create mode 100644 addons/escoria-wizard/graphics/greyarrow_dl.png.import create mode 100644 addons/escoria-wizard/graphics/greyarrow_dr.png create mode 100644 addons/escoria-wizard/graphics/greyarrow_dr.png.import create mode 100644 addons/escoria-wizard/graphics/greyarrow_l.png create mode 100644 addons/escoria-wizard/graphics/greyarrow_l.png.import create mode 100644 addons/escoria-wizard/graphics/greyarrow_r.png create mode 100644 addons/escoria-wizard/graphics/greyarrow_r.png.import create mode 100644 addons/escoria-wizard/graphics/greyarrow_u.png create mode 100644 addons/escoria-wizard/graphics/greyarrow_u.png.import create mode 100644 addons/escoria-wizard/graphics/greyarrow_ul.png create mode 100644 addons/escoria-wizard/graphics/greyarrow_ul.png.import create mode 100644 addons/escoria-wizard/graphics/greyarrow_ur.png create mode 100644 addons/escoria-wizard/graphics/greyarrow_ur.png.import create mode 100644 addons/escoria-wizard/graphics/help/help_background.png create mode 100644 addons/escoria-wizard/graphics/help/help_background.png.import create mode 100644 addons/escoria-wizard/graphics/help/left1.png create mode 100644 addons/escoria-wizard/graphics/help/left1.png.import create mode 100644 addons/escoria-wizard/graphics/help/left2.png create mode 100644 addons/escoria-wizard/graphics/help/left2.png.import create mode 100644 addons/escoria-wizard/graphics/help/left3.png create mode 100644 addons/escoria-wizard/graphics/help/left3.png.import create mode 100644 addons/escoria-wizard/graphics/help/left4.png create mode 100644 addons/escoria-wizard/graphics/help/left4.png.import create mode 100644 addons/escoria-wizard/graphics/help/left5.png create mode 100644 addons/escoria-wizard/graphics/help/left5.png.import create mode 100644 addons/escoria-wizard/graphics/help/leftall.png create mode 100644 addons/escoria-wizard/graphics/help/leftall.png.import create mode 100644 addons/escoria-wizard/graphics/help/middle1.png create mode 100644 addons/escoria-wizard/graphics/help/middle1.png.import create mode 100644 addons/escoria-wizard/graphics/help/middle2.png create mode 100644 addons/escoria-wizard/graphics/help/middle2.png.import create mode 100644 addons/escoria-wizard/graphics/help/middle3.png create mode 100644 addons/escoria-wizard/graphics/help/middle3.png.import create mode 100644 addons/escoria-wizard/graphics/help/middleall.png create mode 100644 addons/escoria-wizard/graphics/help/middleall.png.import create mode 100644 addons/escoria-wizard/graphics/help/right1.png create mode 100644 addons/escoria-wizard/graphics/help/right1.png.import create mode 100644 addons/escoria-wizard/graphics/help/right2.png create mode 100644 addons/escoria-wizard/graphics/help/right2.png.import create mode 100644 addons/escoria-wizard/graphics/help/right3.png create mode 100644 addons/escoria-wizard/graphics/help/right3.png.import create mode 100644 addons/escoria-wizard/graphics/help/right4.png create mode 100644 addons/escoria-wizard/graphics/help/right4.png.import create mode 100644 addons/escoria-wizard/graphics/help/right5.png create mode 100644 addons/escoria-wizard/graphics/help/right5.png.import create mode 100644 addons/escoria-wizard/graphics/help/rightall.png create mode 100644 addons/escoria-wizard/graphics/help/rightall.png.import create mode 100644 addons/escoria-wizard/graphics/icon.png create mode 100644 addons/escoria-wizard/graphics/icon.png.import create mode 100644 addons/escoria-wizard/graphics/icon16x16.png create mode 100644 addons/escoria-wizard/graphics/icon16x16.png.import create mode 100644 addons/escoria-wizard/graphics/inventory.png create mode 100644 addons/escoria-wizard/graphics/inventory.png.import create mode 100644 addons/escoria-wizard/graphics/inventory_preview.png create mode 100644 addons/escoria-wizard/graphics/inventory_preview.png.import create mode 100644 addons/escoria-wizard/graphics/mark-animtest.png create mode 100644 addons/escoria-wizard/graphics/mark-animtest.png.import create mode 100644 addons/escoria-wizard/graphics/no_animation.png create mode 100644 addons/escoria-wizard/graphics/no_animation.png.import create mode 100644 addons/escoria-wizard/graphics/no_spritesheet.png create mode 100644 addons/escoria-wizard/graphics/no_spritesheet.png.import create mode 100644 addons/escoria-wizard/graphics/object_preview.png create mode 100644 addons/escoria-wizard/graphics/object_preview.png.import create mode 100644 addons/escoria-wizard/graphics/robot/idle_all.png create mode 100644 addons/escoria-wizard/graphics/robot/idle_all.png.import create mode 100644 addons/escoria-wizard/graphics/robot/open_all.png create mode 100644 addons/escoria-wizard/graphics/robot/open_all.png.import create mode 100644 addons/escoria-wizard/graphics/robot/talk_down.png create mode 100644 addons/escoria-wizard/graphics/robot/talk_down.png.import create mode 100644 addons/escoria-wizard/graphics/robot/talk_downleft.png create mode 100644 addons/escoria-wizard/graphics/robot/talk_downleft.png.import create mode 100644 addons/escoria-wizard/graphics/robot/talk_downright.png create mode 100644 addons/escoria-wizard/graphics/robot/talk_downright.png.import create mode 100644 addons/escoria-wizard/graphics/robot/talk_right.png create mode 100644 addons/escoria-wizard/graphics/robot/talk_right.png.import create mode 100644 addons/escoria-wizard/graphics/robot/talk_up.png create mode 100644 addons/escoria-wizard/graphics/robot/talk_up.png.import create mode 100644 addons/escoria-wizard/graphics/robot/talk_upright.png create mode 100644 addons/escoria-wizard/graphics/robot/talk_upright.png.import create mode 100644 addons/escoria-wizard/graphics/robot/walk_down.png create mode 100644 addons/escoria-wizard/graphics/robot/walk_down.png.import create mode 100644 addons/escoria-wizard/graphics/robot/walk_downleft.png create mode 100644 addons/escoria-wizard/graphics/robot/walk_downleft.png.import create mode 100644 addons/escoria-wizard/graphics/robot/walk_downright.png create mode 100644 addons/escoria-wizard/graphics/robot/walk_downright.png.import create mode 100644 addons/escoria-wizard/graphics/robot/walk_right.png create mode 100644 addons/escoria-wizard/graphics/robot/walk_right.png.import create mode 100644 addons/escoria-wizard/graphics/robot/walk_up.png create mode 100644 addons/escoria-wizard/graphics/robot/walk_up.png.import create mode 100644 addons/escoria-wizard/graphics/robot/walk_upright.png create mode 100644 addons/escoria-wizard/graphics/robot/walk_upright.png.import create mode 100644 addons/escoria-wizard/graphics/room.png create mode 100644 addons/escoria-wizard/graphics/room.png.import create mode 100644 addons/escoria-wizard/graphics/roomitem.png create mode 100644 addons/escoria-wizard/graphics/roomitem.png.import create mode 100644 addons/escoria-wizard/graphics/wand.png create mode 100644 addons/escoria-wizard/graphics/wand.png.import create mode 100644 addons/escoria-wizard/help_window.gd create mode 100644 addons/escoria-wizard/help_window.gd.uid create mode 100644 addons/escoria-wizard/help_window.tscn create mode 100644 addons/escoria-wizard/item_creator.gd create mode 100644 addons/escoria-wizard/item_creator.gd.uid create mode 100644 addons/escoria-wizard/plugin.cfg create mode 100644 addons/escoria-wizard/plugin.gd create mode 100644 addons/escoria-wizard/plugin.gd.uid create mode 100644 addons/escoria-wizard/room_script_template.esc create mode 100644 build_escoria_docs.sh create mode 100644 default_env.tres create mode 100644 escoria-wizard.conf create mode 100644 export_presets.cfg create mode 100644 game.esc create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/100.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/100.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/114.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/114.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/120.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/120.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/128.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/128.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/144.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/144.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/152.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/152.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/16.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/16.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/167.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/167.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/172.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/172.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/180.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/180.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/196.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/196.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/20.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/20.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/216.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/216.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/256.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/256.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/29.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/29.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/32.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/32.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/40.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/40.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/48.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/48.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/50.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/50.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/512.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/512.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/55.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/55.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/57.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/57.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/58.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/58.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/60.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/60.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/64.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/64.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/72.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/72.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/76.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/76.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/80.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/80.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/87.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/87.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/88.png create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/88.png.import create mode 100644 game/appicons/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 game/appicons/android/mipmap-hdpi/ic_launcher.png create mode 100644 game/appicons/android/mipmap-hdpi/ic_launcher.png.import create mode 100644 game/appicons/android/mipmap-mdpi/ic_launcher.png create mode 100644 game/appicons/android/mipmap-mdpi/ic_launcher.png.import create mode 100644 game/appicons/android/mipmap-xhdpi/ic_launcher.png create mode 100644 game/appicons/android/mipmap-xhdpi/ic_launcher.png.import create mode 100644 game/appicons/android/mipmap-xxhdpi/ic_launcher.png create mode 100644 game/appicons/android/mipmap-xxhdpi/ic_launcher.png.import create mode 100644 game/appicons/android/mipmap-xxxhdpi/ic_launcher.png create mode 100644 game/appicons/android/mipmap-xxxhdpi/ic_launcher.png.import create mode 100644 game/appicons/appstore.png create mode 100644 game/appicons/appstore.png.import create mode 100644 game/appicons/playstore.png create mode 100644 game/appicons/playstore.png.import create mode 100644 game/characters/weener/png/weener.png create mode 100644 game/characters/weener/png/weener.png.import create mode 100644 game/characters/weener/png/weener.png~ create mode 100644 game/characters/weener/weener.tscn create mode 100644 game/fonts/caslonantique.tres create mode 100644 game/fonts/caslonantique.ttf create mode 100644 game/fonts/caslonantique.ttf.import create mode 100644 game/items/escitems/button.tscn create mode 100644 game/items/inventory/pen.esc create mode 100644 game/items/inventory/pen.png create mode 100644 game/items/inventory/pen.png.import create mode 100644 game/items/inventory/pen.tscn create mode 100644 game/items/textures/bottle.png create mode 100644 game/items/textures/bottle.png.import create mode 100644 game/rooms/intro/esc/intro.esc create mode 100644 game/rooms/intro/escoria-logo-comet-anim.png create mode 100644 game/rooms/intro/escoria-logo-comet-anim.png.import create mode 100644 game/rooms/intro/escoria-logo-comet.png create mode 100644 game/rooms/intro/escoria-logo-comet.png.import create mode 100644 game/rooms/intro/escoria_background.png create mode 100644 game/rooms/intro/escoria_background.png.import create mode 100644 game/rooms/intro/escoria_background_1080p.png create mode 100644 game/rooms/intro/escoria_background_1080p.png.import create mode 100644 game/rooms/intro/intro_background_space.png create mode 100644 game/rooms/intro/intro_background_space.png.import create mode 100644 game/rooms/intro/intro_background_space_1080p.png create mode 100644 game/rooms/intro/intro_background_space_1080p.png.import create mode 100644 game/rooms/intro/intro_room.tscn create mode 100644 game/rooms/no_escoria/no_escoria.tscn create mode 100644 game/rooms/no_escoria/player.gd create mode 100644 game/rooms/no_escoria/player.gd.uid create mode 100644 game/rooms/test/phone.esc create mode 100644 game/rooms/test/room_test.esc create mode 100644 game/rooms/test/test.tscn create mode 100644 game/rooms/test/test_bg.png create mode 100644 game/rooms/test/test_bg.png.import create mode 100644 game/rooms/test_kitchen/kitchen.png create mode 100644 game/rooms/test_kitchen/kitchen.png.import create mode 100644 game/rooms/test_kitchen/room_kitchen.esc create mode 100644 game/rooms/test_kitchen/test_kitchen.tscn create mode 100644 game/shaders/outline.gdshader create mode 100644 game/shaders/outline.gdshader.uid create mode 100644 game/shaders/shadermaterial_outline_green5px.tres create mode 100644 game/translations/game.csv.import create mode 100644 game/translations/main_menu.csv.import create mode 100644 icon.png create mode 100644 icon.png.import delete mode 100644 icon.svg delete mode 100644 icon.svg.import delete mode 100644 mus/intro.wav delete mode 100644 mus/intro.wav.import delete mode 100644 room/kitchen/kitchen.png delete mode 100644 room/kitchen/kitchen.png.import delete mode 100644 room/kitchen/kitchenbottomlayer.png delete mode 100644 room/kitchen/kitchenbottomlayer.png.import delete mode 100644 room/kitchen/kitchentoplayer.png delete mode 100644 room/kitchen/kitchentoplayer.png.import delete mode 100644 room/start/room.jpg delete mode 100644 room/start/room.jpg.import delete mode 100644 room/start/testprop.png delete mode 100644 room/start/testprop.png.import delete mode 100644 scenes/char/weener.gd delete mode 100644 scenes/char/weener.gd.uid delete mode 100644 scenes/char/weener.tscn delete mode 100644 scenes/item/door.gd delete mode 100644 scenes/item/door.gd.uid delete mode 100644 scenes/item/door.tscn delete mode 100644 scenes/item/inventory.gd delete mode 100644 scenes/item/inventory.gd.uid delete mode 100644 scenes/item/inventory.tscn delete mode 100644 scenes/item/inventory_item.gd delete mode 100644 scenes/item/inventory_item.gd.uid delete mode 100644 scenes/item/item.gd delete mode 100644 scenes/item/item.gd.uid delete mode 100644 scenes/item/item.tscn delete mode 100644 scenes/room/kitchen.tscn delete mode 100644 scenes/room/room.gd delete mode 100644 scenes/room/room.gd.uid delete mode 100644 scenes/room/room.tscn delete mode 100644 scenes/room/spawnpoint.gd delete mode 100644 scenes/room/spawnpoint.gd.uid delete mode 100644 scenes/room/spawnpoint.tscn delete mode 100644 scenes/room/start.tscn delete mode 100644 scenes/ui/menu.gd delete mode 100644 scenes/ui/menu.gd.uid delete mode 100644 scenes/ui/menu.tscn delete mode 100644 scenes/ui/options.gd delete mode 100644 scenes/ui/options.gd.uid delete mode 100644 scenes/ui/options.tscn delete mode 100644 scenes/ui/title.tscn delete mode 100644 scenes/ui/title_button.tscn delete mode 100644 sfx/telephone.wav delete mode 100644 sfx/telephone.wav.import delete mode 100644 spr/weener/crouch.png delete mode 100644 spr/weener/crouch.png.import delete mode 100644 spr/weener/grab.png delete mode 100644 spr/weener/grab.png.import delete mode 100644 spr/weener/run1.png delete mode 100644 spr/weener/run1.png.import delete mode 100644 spr/weener/run2.png delete mode 100644 spr/weener/run2.png.import delete mode 100644 spr/weener/stand.png delete mode 100644 spr/weener/stand.png.import delete mode 100644 spr/weener/walk.png delete mode 100644 spr/weener/walk.png.import create mode 100644 tools/__pycache__/reformat_docstrings.cpython-39.pyc create mode 100644 tools/docstring_formatter.py create mode 100644 tools/reformat_docstrings.py delete mode 100644 ui/Baskerville.ttf delete mode 100644 ui/Baskerville.ttf.import delete mode 100644 ui/cursor.png delete mode 100644 ui/cursor.png.import delete mode 100644 ui/cursor_shadow.png delete mode 100644 ui/cursor_shadow.png.import delete mode 100644 ui/frame.png delete mode 100644 ui/frame.png.import delete mode 100644 ui/settings.png delete mode 100644 ui/settings.png.import delete mode 100644 ui/settings_active.png delete mode 100644 ui/settings_active.png.import delete mode 100644 ui/testmenu.png delete mode 100644 ui/testmenu.png.import delete mode 100644 ui/textbox.png delete mode 100644 ui/textbox.png.import delete mode 100644 ui/textbox_arrow.png delete mode 100644 ui/textbox_arrow.png.import delete mode 100644 ui/textbox_container.png delete mode 100644 ui/textbox_container.png.import delete mode 100644 ui/title.png delete mode 100644 ui/title.png.import diff --git a/action_defaults.esc b/action_defaults.esc new file mode 100644 index 0000000..83d0a2b --- /dev/null +++ b/action_defaults.esc @@ -0,0 +1,5 @@ +:use | TK + say($current_player, "I can't use this!") + +:pickup | TK + say($current_player, "I can't take this!") diff --git a/addons/escoria-core/_test/deprecated/test_esc_compiler.gd b/addons/escoria-core/_test/deprecated/test_esc_compiler.gd new file mode 100644 index 0000000..28bf439 --- /dev/null +++ b/addons/escoria-core/_test/deprecated/test_esc_compiler.gd @@ -0,0 +1,300 @@ +extends Control + + +func _test_basic() -> bool: + var esc = """ +:test +# first group +> + say player "Test" + # Second group + > [test] + say player "Test2 BLANK" + say player "Test3" [test2] + # Third group + > + + say player "Test4" +# Fourth group +> + say player "Test5" + say player "Test 6" + say player TEST:"Test 7" + """ + var script = escoria.esc_compiler.compile(esc) + + var subject = script + assert(subject is ESCScript) + + subject = script.events.keys() + assert(subject.size() == 1) + assert(subject[0] == "test") + + subject = script.events["test"].statements + assert(subject.size() == 2) + + subject = script.events["test"].statements[0] + assert(subject is ESCGroup) + assert(subject.statements.size() == 4) + + subject = script.events["test"].statements[0].statements[0] + assert(subject is ESCCommand) + assert(subject.name == "say") + assert(subject.parameters.size() == 2) + assert(subject.parameters[0] == "player") + assert(subject.parameters[1] == '"Test"') + + subject = script.events["test"].statements[0].statements[1] + assert(subject is ESCGroup) + assert(subject.conditions.size() == 1) + assert(subject.conditions[0] is ESCCondition) + assert(subject.conditions[0].flag == "test") + + subject = script.events["test"].statements[0].statements[1].statements[0] + assert(subject is ESCCommand) + assert(subject.name == "say") + assert(subject.parameters.size() == 2) + assert(subject.parameters[0] == "player") + assert(subject.parameters[1] == '"Test2 BLANK"') + + subject = script.events["test"].statements[0].statements[2] + assert(subject is ESCCommand) + assert(subject.name == "say") + assert(subject.parameters.size() == 2) + assert(subject.parameters[0] == "player") + assert(subject.parameters[1] == '"Test3"') + assert(subject.conditions.size() == 1) + assert(subject.conditions[0].flag == "test2") + + subject = script.events["test"].statements[0].statements[3] + assert(subject is ESCGroup) + assert(subject.statements.size() == 1) + + subject = script.events["test"].statements[1] + assert(subject is ESCGroup) + assert(subject.statements.size() == 3) + + subject = script.events["test"].statements[1].statements[1] + assert(subject is ESCCommand) + assert(subject.name == "say") + assert(subject.parameters[1] == '"Test 6"') + + subject = script.events["test"].statements[1].statements[2] + assert(subject is ESCCommand) + assert(subject.name == "say") + assert(subject.parameters[1] == "TEST:\"Test 7\"") + + return true + + +func _test_conditions() -> bool: + var esc = """ +:test +say player "Test" [flag] +say player "Test" [flag1,flag2] +say player "Test" [!flag] +say player "Test" [i/flag] +say player "Test" [i/flag,flag] +say player "Test" [i/flag,flag,!flag2] +say player "Test" [eq flag 3] +say player "Test" [eq flag 3,gt flag 5] +say player "Test" [!eq flag 3] + """ + var script = escoria.esc_compiler.compile(esc) + + var subject = script.events["test"].statements[0] + assert(subject is ESCCommand) + assert(subject.conditions.size() == 1) + + subject = script.events["test"].statements[0].conditions[0] + assert(subject.flag == "flag") + assert(not subject.negated) + assert(not subject.inventory) + assert(subject.comparison == ESCCondition.COMPARISON_NONE) + + subject = script.events["test"].statements[1].conditions + assert(subject.size() == 2) + assert(subject[0].flag == "flag1") + assert(subject[1].flag == "flag2") + + subject = script.events["test"].statements[2].conditions + assert(subject.size() == 1) + assert(subject[0].flag == "flag") + assert(subject[0].negated) + + subject = script.events["test"].statements[3].conditions + assert(subject.size() == 1) + assert(subject[0].flag == "flag") + assert(subject[0].inventory) + + subject = script.events["test"].statements[4].conditions + assert(subject.size() == 2) + assert(subject[0].flag == "flag") + assert(subject[0].inventory) + assert(subject[1].flag == "flag") + assert(not subject[1].inventory) + + subject = script.events["test"].statements[5].conditions + assert(subject.size() == 3) + assert(subject[0].flag == "flag") + assert(subject[0].inventory) + assert(subject[1].flag == "flag") + assert(not subject[1].inventory) + assert(subject[2].flag == "flag2") + assert(not subject[2].inventory) + assert(subject[2].negated) + + subject = script.events["test"].statements[6].conditions + assert(subject.size() == 1) + assert(subject[0].flag == "flag") + assert(subject[0].comparison == ESCCondition.COMPARISON_EQ) + assert(subject[0].comparison_value == 3) + + subject = script.events["test"].statements[7].conditions + assert(subject.size() == 2) + assert(subject[0].flag == "flag") + assert(subject[0].comparison == ESCCondition.COMPARISON_EQ) + assert(subject[0].comparison_value == 3) + assert(subject[1].flag == "flag") + assert(subject[1].comparison == ESCCondition.COMPARISON_GT) + assert(subject[1].comparison_value == 5) + + subject = script.events["test"].statements[8].conditions + assert(subject.size() == 1) + assert(subject[0].flag == "flag") + assert(subject[0].comparison == ESCCondition.COMPARISON_EQ) + assert(subject[0].comparison_value == 3) + assert(subject[0].negated) + + return true + + +func _test_event_flags() -> bool: + var esc = """ +:test | TK +:test2 | TK NO_TT +:test3 | TK NO_TT NO_UI + """ + var script = escoria.esc_compiler.compile(esc) + + var subject = script.events + assert(subject.keys().size() == 3) + assert("test" in subject.keys()) + assert("test2" in subject.keys()) + assert("test3" in subject.keys()) + + subject = script.events["test"] + assert(subject.name == "test") + assert(subject.flags & ESCEvent.FLAGS.TK != 0) + assert(subject.flags & ESCEvent.FLAGS.NO_TT == 0) + + subject = script.events["test2"] + assert(subject.name == "test2") + assert(subject.flags & ESCEvent.FLAGS.TK != 0) + assert(subject.flags & ESCEvent.FLAGS.NO_TT != 0) + + subject = script.events["test3"] + assert(subject.name == "test3") + assert(subject.flags & ESCEvent.FLAGS.TK != 0) + assert(subject.flags & ESCEvent.FLAGS.NO_TT != 0) + assert(subject.flags & ESCEvent.FLAGS.NO_UI != 0) + + return true + + +func _test_dialog() -> bool: + var esc = """ +:test +? + - "Option 1" + say player "test" + say player "testb" + say player "testb?" + - "Option 2" [flag] + say player "test2" + ? + - "Suboption 1" + say player "test21" + - "Suboption 2" + say player "test22" + ! + - "Option 3" + > + say player "test3" + - TEST:"Option 4" + say player "test4" +! + """ + var script = escoria.esc_compiler.compile(esc) + + var subject = script.events["test"].statements + assert(subject.size() == 1) + + assert(subject[0] is ESCDialog) + assert(subject[0].options.size() == 4) + + subject = script.events["test"].statements[0].options[0] + assert(subject is ESCDialogOption) + assert(subject.option == "Option 1") + + subject = script.events["test"].statements[0].options[0].statements + assert(subject.size() == 3) + assert(subject[0] is ESCCommand) + assert(subject[0].name == "say") + assert(subject[0].parameters.size() == 2) + assert(subject[1] is ESCCommand) + assert(subject[1].name == "say") + assert(subject[1].parameters.size() == 2) + assert(subject[1].parameters[1] == '"testb"') + assert(subject[2] is ESCCommand) + assert(subject[2].name == "say") + assert(subject[2].parameters.size() == 2) + assert(subject[2].parameters[1] == '"testb?"') + + subject = script.events["test"].statements[0].options[1] + assert(subject is ESCDialogOption) + assert(subject.option == "Option 2") + assert(subject.conditions.size() == 1) + assert(subject.conditions[0].flag == "flag") + + subject = script.events["test"].statements[0].options[1].statements + assert(subject.size() == 2) + assert(subject[0] is ESCCommand) + assert(subject[0].name == "say") + assert(subject[0].parameters.size() == 2) + + assert(subject[1] is ESCDialog) + assert(subject[1].options.size() == 2) + + subject = script.events["test"].statements[0].options[2] + assert(subject is ESCDialogOption) + assert(subject.option == "Option 3") + + subject = script.events["test"].statements[0].options[2].statements + assert(subject.size() == 1) + assert(subject[0] is ESCGroup) + assert(subject[0].statements.size() == 1) + assert(subject[0].statements[0] is ESCCommand) + assert(subject[0].statements[0].parameters.size() == 2) + + subject = script.events["test"].statements[0].options[3] + assert(subject is ESCDialogOption) + assert(subject.option == "TEST") + + return true + + +func _on_BasicFunctionality_pressed(): + $VBoxContainer/VBoxContainer/BasicFunctionality.button_pressed = self._test_basic() + + +func _on_Conditions_pressed(): + $VBoxContainer/VBoxContainer/Conditions.button_pressed = self._test_conditions() + + +func _on_EventFlags_pressed(): + $VBoxContainer/VBoxContainer/EventFlags.button_pressed = self._test_event_flags() + + +func _on_Dialog_pressed(): + $VBoxContainer/VBoxContainer/Dialog.button_pressed = self._test_dialog() diff --git a/addons/escoria-core/_test/deprecated/test_esc_compiler.gd.uid b/addons/escoria-core/_test/deprecated/test_esc_compiler.gd.uid new file mode 100644 index 0000000..407abb1 --- /dev/null +++ b/addons/escoria-core/_test/deprecated/test_esc_compiler.gd.uid @@ -0,0 +1 @@ +uid://cjhr78sjyfnb3 diff --git a/addons/escoria-core/_test/deprecated/test_esc_compiler.tscn b/addons/escoria-core/_test/deprecated/test_esc_compiler.tscn new file mode 100644 index 0000000..9d7a934 --- /dev/null +++ b/addons/escoria-core/_test/deprecated/test_esc_compiler.tscn @@ -0,0 +1,43 @@ +[gd_scene load_steps=2 format=3 uid="uid://btv33yamt7164"] + +[ext_resource type="Script" uid="uid://cjhr78sjyfnb3" path="res://addons/escoria-core/_test/deprecated/test_esc_compiler.gd" id="1"] + +[node name="Testsuite" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("1") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = 1.0 +offset_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="BasicFunctionality" type="CheckButton" parent="VBoxContainer/VBoxContainer"] +layout_mode = 2 +text = "Basic Functionality" + +[node name="Conditions" type="CheckButton" parent="VBoxContainer/VBoxContainer"] +layout_mode = 2 +text = "Check conditions" + +[node name="EventFlags" type="CheckButton" parent="VBoxContainer/VBoxContainer"] +layout_mode = 2 +text = "Check event flags" + +[node name="Dialog" type="CheckButton" parent="VBoxContainer/VBoxContainer"] +layout_mode = 2 +text = "Check dialogs" + +[connection signal="pressed" from="VBoxContainer/VBoxContainer/BasicFunctionality" to="." method="_on_BasicFunctionality_pressed"] +[connection signal="pressed" from="VBoxContainer/VBoxContainer/Conditions" to="." method="_on_Conditions_pressed"] +[connection signal="pressed" from="VBoxContainer/VBoxContainer/EventFlags" to="." method="_on_EventFlags_pressed"] +[connection signal="pressed" from="VBoxContainer/VBoxContainer/Dialog" to="." method="_on_Dialog_pressed"] diff --git a/addons/escoria-core/_test/test_migrations.gd b/addons/escoria-core/_test/test_migrations.gd new file mode 100644 index 0000000..19ecb45 --- /dev/null +++ b/addons/escoria-core/_test/test_migrations.gd @@ -0,0 +1,20 @@ +extends Control + + + +func _on_CheckESCMigrationManager_pressed() -> bool: + var savegame: ESCSaveGame = ESCSaveGame.new() + + savegame.globals["test"] = "testa" + + var migration_manager: ESCMigrationManager = ESCMigrationManager.new() + savegame = migration_manager.migrate( + savegame, + "1.0.0", + "2.0.0", + "res://addons/escoria-core/_test/testversions" + ) + + assert(savegame.globals["test"] == "testc") + + return true diff --git a/addons/escoria-core/_test/test_migrations.gd.uid b/addons/escoria-core/_test/test_migrations.gd.uid new file mode 100644 index 0000000..79fdadc --- /dev/null +++ b/addons/escoria-core/_test/test_migrations.gd.uid @@ -0,0 +1 @@ +uid://b7x4hqeh2dtgi diff --git a/addons/escoria-core/_test/test_migrations.tscn b/addons/escoria-core/_test/test_migrations.tscn new file mode 100644 index 0000000..252dd79 --- /dev/null +++ b/addons/escoria-core/_test/test_migrations.tscn @@ -0,0 +1,25 @@ +[gd_scene load_steps=2 format=3 uid="uid://ck1ew8vbwietx"] + +[ext_resource type="Script" uid="uid://b7x4hqeh2dtgi" path="res://addons/escoria-core/_test/test_migrations.gd" id="1"] + +[node name="Control" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("1") +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="CheckESCMigrationManager" type="CheckButton" parent="VBoxContainer"] +offset_right = 1280.0 +offset_bottom = 40.0 +text = "Check ESCMigrationManager" + +[connection signal="pressed" from="VBoxContainer/CheckESCMigrationManager" to="." method="_on_CheckESCMigrationManager_pressed"] diff --git a/addons/escoria-core/_test/testversions/1.0.1.gd b/addons/escoria-core/_test/testversions/1.0.1.gd new file mode 100644 index 0000000..a1cf8b8 --- /dev/null +++ b/addons/escoria-core/_test/testversions/1.0.1.gd @@ -0,0 +1,5 @@ +extends ESCMigration + +func migrate(): + self._savegame.globals["test"] = "testb" + diff --git a/addons/escoria-core/_test/testversions/1.0.1.gd.uid b/addons/escoria-core/_test/testversions/1.0.1.gd.uid new file mode 100644 index 0000000..ffbdb4c --- /dev/null +++ b/addons/escoria-core/_test/testversions/1.0.1.gd.uid @@ -0,0 +1 @@ +uid://0fnh1m2wcuad diff --git a/addons/escoria-core/_test/testversions/1.1.0.gd b/addons/escoria-core/_test/testversions/1.1.0.gd new file mode 100644 index 0000000..7840490 --- /dev/null +++ b/addons/escoria-core/_test/testversions/1.1.0.gd @@ -0,0 +1,5 @@ +extends ESCMigration + +func migrate(): + self._savegame.globals["test"] = "testc" + diff --git a/addons/escoria-core/_test/testversions/1.1.0.gd.uid b/addons/escoria-core/_test/testversions/1.1.0.gd.uid new file mode 100644 index 0000000..b8b91b5 --- /dev/null +++ b/addons/escoria-core/_test/testversions/1.1.0.gd.uid @@ -0,0 +1 @@ +uid://gdobd31uc5ot diff --git a/addons/escoria-core/buses/default_bus_layout.tres b/addons/escoria-core/buses/default_bus_layout.tres new file mode 100644 index 0000000..9072693 --- /dev/null +++ b/addons/escoria-core/buses/default_bus_layout.tres @@ -0,0 +1,27 @@ +[gd_resource type="AudioBusLayout" format=3 uid="uid://d3fjjoihd5xw"] + +[resource] +bus/1/name = &"SFX" +bus/1/solo = false +bus/1/mute = false +bus/1/bypass_fx = false +bus/1/volume_db = 0.0 +bus/1/send = &"Master" +bus/2/name = &"Music" +bus/2/solo = false +bus/2/mute = false +bus/2/bypass_fx = false +bus/2/volume_db = 0.0 +bus/2/send = &"Master" +bus/3/name = &"Speech" +bus/3/solo = false +bus/3/mute = false +bus/3/bypass_fx = false +bus/3/volume_db = 0.0 +bus/3/send = &"Master" +bus/4/name = &"Ambient" +bus/4/solo = false +bus/4/mute = false +bus/4/bypass_fx = false +bus/4/volume_db = 0.0 +bus/4/send = &"Master" diff --git a/addons/escoria-core/design/esc_background.svg b/addons/escoria-core/design/esc_background.svg new file mode 100644 index 0000000..01762fa --- /dev/null +++ b/addons/escoria-core/design/esc_background.svg @@ -0,0 +1,17 @@ + + + esc_background + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/escoria-core/design/esc_background.svg.import b/addons/escoria-core/design/esc_background.svg.import new file mode 100644 index 0000000..18602e5 --- /dev/null +++ b/addons/escoria-core/design/esc_background.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c4fxnujo0iogd" +path="res://.godot/imported/esc_background.svg-2ffa7f5fdc64f4da571930b62ec7851e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/esc_background.svg" +dest_files=["res://.godot/imported/esc_background.svg-2ffa7f5fdc64f4da571930b62ec7851e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-core/design/esc_exit.svg b/addons/escoria-core/design/esc_exit.svg new file mode 100644 index 0000000..5b1fcf1 --- /dev/null +++ b/addons/escoria-core/design/esc_exit.svg @@ -0,0 +1,24 @@ + + + esc_exit + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/escoria-core/design/esc_exit.svg.import b/addons/escoria-core/design/esc_exit.svg.import new file mode 100644 index 0000000..21dceae --- /dev/null +++ b/addons/escoria-core/design/esc_exit.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c87ess740pk33" +path="res://.godot/imported/esc_exit.svg-8cecc48c52b1eb27ccb920ef948cfe96.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/esc_exit.svg" +dest_files=["res://.godot/imported/esc_exit.svg-8cecc48c52b1eb27ccb920ef948cfe96.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-core/design/esc_item.svg b/addons/escoria-core/design/esc_item.svg new file mode 100644 index 0000000..86e28f0 --- /dev/null +++ b/addons/escoria-core/design/esc_item.svg @@ -0,0 +1,14 @@ + + + esc_item + + + + + + + + + + + \ No newline at end of file diff --git a/addons/escoria-core/design/esc_item.svg.import b/addons/escoria-core/design/esc_item.svg.import new file mode 100644 index 0000000..63831e6 --- /dev/null +++ b/addons/escoria-core/design/esc_item.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bq23l6fdxbpya" +path="res://.godot/imported/esc_item.svg-4dcb2b4781e1c17880f7b0454370fa40.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/esc_item.svg" +dest_files=["res://.godot/imported/esc_item.svg-4dcb2b4781e1c17880f7b0454370fa40.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-core/design/esc_location.svg b/addons/escoria-core/design/esc_location.svg new file mode 100644 index 0000000..56d3c5b --- /dev/null +++ b/addons/escoria-core/design/esc_location.svg @@ -0,0 +1,15 @@ + + + esc_location + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/escoria-core/design/esc_location.svg.import b/addons/escoria-core/design/esc_location.svg.import new file mode 100644 index 0000000..ce27db8 --- /dev/null +++ b/addons/escoria-core/design/esc_location.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bjuxtjfahgtom" +path="res://.godot/imported/esc_location.svg-353c07f231a6ae5f2e9985427679cc91.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/esc_location.svg" +dest_files=["res://.godot/imported/esc_location.svg-353c07f231a6ae5f2e9985427679cc91.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-core/design/esc_player.svg b/addons/escoria-core/design/esc_player.svg new file mode 100644 index 0000000..9a58d5e --- /dev/null +++ b/addons/escoria-core/design/esc_player.svg @@ -0,0 +1,14 @@ + + + esc_player + + + + + + + + + + + \ No newline at end of file diff --git a/addons/escoria-core/design/esc_player.svg.import b/addons/escoria-core/design/esc_player.svg.import new file mode 100644 index 0000000..91fc8fa --- /dev/null +++ b/addons/escoria-core/design/esc_player.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bxaw8jangm6mg" +path="res://.godot/imported/esc_player.svg-2efe29526d0cb2ca1666545e6882f93a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/esc_player.svg" +dest_files=["res://.godot/imported/esc_player.svg-2efe29526d0cb2ca1666545e6882f93a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-core/design/esc_room.svg b/addons/escoria-core/design/esc_room.svg new file mode 100644 index 0000000..917cbb6 --- /dev/null +++ b/addons/escoria-core/design/esc_room.svg @@ -0,0 +1,18 @@ + + + esc_room + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/escoria-core/design/esc_room.svg.import b/addons/escoria-core/design/esc_room.svg.import new file mode 100644 index 0000000..70e87c8 --- /dev/null +++ b/addons/escoria-core/design/esc_room.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://djcyqaocwdujw" +path="res://.godot/imported/esc_room.svg-12d86d5e956c3f7778eb4e3ead13c5d1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/esc_room.svg" +dest_files=["res://.godot/imported/esc_room.svg-12d86d5e956c3f7778eb4e3ead13c5d1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-core/design/esc_terrain.svg b/addons/escoria-core/design/esc_terrain.svg new file mode 100644 index 0000000..51be6a0 --- /dev/null +++ b/addons/escoria-core/design/esc_terrain.svg @@ -0,0 +1,14 @@ + + + esc_terrain + + + + + + + + + + + \ No newline at end of file diff --git a/addons/escoria-core/design/esc_terrain.svg.import b/addons/escoria-core/design/esc_terrain.svg.import new file mode 100644 index 0000000..e1bc722 --- /dev/null +++ b/addons/escoria-core/design/esc_terrain.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b4cqtg504det1" +path="res://.godot/imported/esc_terrain.svg-2af245bbff5e7e049fb0bea22767b0c2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/esc_terrain.svg" +dest_files=["res://.godot/imported/esc_terrain.svg-2af245bbff5e7e049fb0bea22767b0c2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-core/design/escoria-logo-small.png b/addons/escoria-core/design/escoria-logo-small.png new file mode 100644 index 0000000..aae00d1 Binary files /dev/null and b/addons/escoria-core/design/escoria-logo-small.png differ diff --git a/addons/escoria-core/design/escoria-logo-small.png.import b/addons/escoria-core/design/escoria-logo-small.png.import new file mode 100644 index 0000000..01d8899 --- /dev/null +++ b/addons/escoria-core/design/escoria-logo-small.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bsk881j7uv7hr" +path="res://.godot/imported/escoria-logo-small.png-1060da8168786f7366de01066d7187df.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/escoria-logo-small.png" +dest_files=["res://.godot/imported/escoria-logo-small.png-1060da8168786f7366de01066d7187df.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/design/escoria-logo.png b/addons/escoria-core/design/escoria-logo.png new file mode 100644 index 0000000..2a5a52d Binary files /dev/null and b/addons/escoria-core/design/escoria-logo.png differ diff --git a/addons/escoria-core/design/escoria-logo.png.import b/addons/escoria-core/design/escoria-logo.png.import new file mode 100644 index 0000000..1fea0f6 --- /dev/null +++ b/addons/escoria-core/design/escoria-logo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bf0uryqlmic0p" +path="res://.godot/imported/escoria-logo.png-dee8515de46018c5fb29454f5a45f136.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/escoria-logo.png" +dest_files=["res://.godot/imported/escoria-logo.png-dee8515de46018c5fb29454f5a45f136.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/design/escoria-logo.svg b/addons/escoria-core/design/escoria-logo.svg new file mode 100644 index 0000000..115d9ab --- /dev/null +++ b/addons/escoria-core/design/escoria-logo.svg @@ -0,0 +1,594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + ESC + RIA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/escoria-core/design/escoria-logo.svg.import b/addons/escoria-core/design/escoria-logo.svg.import new file mode 100644 index 0000000..2c3017c --- /dev/null +++ b/addons/escoria-core/design/escoria-logo.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://vaud5ixosj5" +path="res://.godot/imported/escoria-logo.svg-5120fc29f2fd2ce34e28256d215beb8c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/design/escoria-logo.svg" +dest_files=["res://.godot/imported/escoria-logo.svg-5120fc29f2fd2ce34e28256d215beb8c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-core/design/icons.sketch b/addons/escoria-core/design/icons.sketch new file mode 100644 index 0000000..348e920 Binary files /dev/null and b/addons/escoria-core/design/icons.sketch differ diff --git a/addons/escoria-core/game/assets/images/no_image.png b/addons/escoria-core/game/assets/images/no_image.png new file mode 100644 index 0000000..7c06dbb Binary files /dev/null and b/addons/escoria-core/game/assets/images/no_image.png differ diff --git a/addons/escoria-core/game/assets/images/no_image.png.import b/addons/escoria-core/game/assets/images/no_image.png.import new file mode 100644 index 0000000..0482fa8 --- /dev/null +++ b/addons/escoria-core/game/assets/images/no_image.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://l6fa7t5xs5fr" +path="res://.godot/imported/no_image.png-7e4632ad2d21010b279ddaa4725bacb7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/game/assets/images/no_image.png" +dest_files=["res://.godot/imported/no_image.png-7e4632ad2d21010b279ddaa4725bacb7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/game/assets/images/no_image.xcf b/addons/escoria-core/game/assets/images/no_image.xcf new file mode 100644 index 0000000..2a7ef08 Binary files /dev/null and b/addons/escoria-core/game/assets/images/no_image.xcf differ diff --git a/addons/escoria-core/game/assets/images/white.png b/addons/escoria-core/game/assets/images/white.png new file mode 100644 index 0000000..573faa3 Binary files /dev/null and b/addons/escoria-core/game/assets/images/white.png differ diff --git a/addons/escoria-core/game/assets/images/white.png.import b/addons/escoria-core/game/assets/images/white.png.import new file mode 100644 index 0000000..837c458 --- /dev/null +++ b/addons/escoria-core/game/assets/images/white.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b3u1y0se2wx7m" +path="res://.godot/imported/white.png-f3cec12c1569e9ac3b3eebff4a7bc5d8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/game/assets/images/white.png" +dest_files=["res://.godot/imported/white.png-f3cec12c1569e9ac3b3eebff4a7bc5d8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd b/addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd new file mode 100644 index 0000000..27ed7a5 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd @@ -0,0 +1,627 @@ +## Node that performs the moving (walk, teleport, terrain scaling...) actions on +## its parent node. +extends Node +class_name ESCMovable + + +## Tasks carried out by this walkable node[br] +## NONE - The node is inactive[br] +## WALK - The node walks the parent somewhere[br] +## SLIDE - The node slides the parent somewhere +# TODO Currently SLIDE is no different from WALK. Consider removing. +enum MovableTask { + NONE, + WALK, + SLIDE +} + + +## Character path through the scene as calculated by the Pathfinder +var walk_path: Array = [] + +## Current active walk path entry +var path_ofs: int + +## The destination where the character should be moving to +var walk_destination: Vector2 + +## The walk context currently carried out by this movable node +var walk_context: ESCWalkContext = null + +## Whether the character was moved at all +var moved: bool + +## Player Direction used to reflect the movement to the new position +var last_dir: int + +## Last angle calculated +var last_angle: float + +## The last scaling applied to the parent +var last_scale: Vector2 + +## Whether the current direction animation is flipped +var is_mirrored: bool + +## Shortcut variable that references the node's parent +@onready var parent = get_parent() + + +## Currenly running task +@onready var task = MovableTask.NONE + + +## Ready function. Adds the signal "arrived" to the parent node, which is emitted when the destination position was reached.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready() -> void: + if not parent.has_user_signal("arrived"): + parent.add_user_signal("arrived") + + +## Main processing loop[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |delta|`float`|Time that has passed since the last call to this function|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _process(delta: float) -> void: + if Engine.is_editor_hint(): + return + + if task == MovableTask.WALK or task == MovableTask.SLIDE: + var old_pos = parent.get_position() + var new_pos = _calculate_movement(delta) + if new_pos == null: + return + + if task == MovableTask.WALK: + # Get the angle of the object to face the position to reach. + var angle: float = (old_pos.angle_to_point(new_pos)) + _perform_walk_orientation(angle) + + update_terrain() + else: + moved = false + set_process(false) + + +## Calculates the next position of the object.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |delta|`float`|the time elapsed from last frame|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the new position as `Vector2`, or `null` if the movement calculation cannot proceed. +func _calculate_movement(delta: float): + # Initialize the current pos and previous pos variables + var pos: Vector2 = parent.get_position() + var old_pos: Vector2 = pos + + # Get next waypoint from the walkpath array. + var next: Vector2 + if walk_path.size() > 1: + next = walk_path[path_ofs + 1] + else: + next = walk_path[path_ofs] + + # Movement speed calculation + var a: float = pow(sin(last_angle), 2) + var movement_speed: float = parent.speed * delta * last_scale.x * (a * last_scale.x + 1 - a) * \ + parent.terrain.player_speed_multiplier + if walk_context.fast: + movement_speed *= parent.terrain.player_doubleclick_speed_multiplier + + # Calculate the direction vector from current position and next waypoint + var dir: Vector2 = (next - pos).normalized() + + # If we're close to the next waypoint (ie. distance < necessary movement + # speed to get to this waypoint, we consider the waypoint reached + # and pass to the next one. + # Else, calculate the new position. + var new_pos: Vector2 + if pos.distance_to(next) < movement_speed: + new_pos = next + path_ofs += 1 + else: + new_pos = pos + dir * movement_speed * parent.v_speed_damp + + # If current waypoint id is >= the number of waypoints, were're at the + # end of the walk: stop walking. + if path_ofs >= walk_path.size() - 1: + walk_stop(walk_destination) + return + + # Update current position variable + pos = new_pos + parent.set_position(pos) + return pos + +## Calculates the orientation of the object while walking, to play the right animation according to this orientation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |angle|`float`|the angle X axis and object's facing direction.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _perform_walk_orientation(angle: float): + last_angle = angle + last_dir = _get_dir_deg(ESCUtils.get_deg_from_rad(angle), + parent.animations) + + var animation_player: ESCAnimationPlayer = \ + parent.get_animation_player() + + var current_animation = animation_player.get_animation() + + var animation_to_play = \ + parent.animations.directions[last_dir].animation + if current_animation != animation_to_play and \ + animation_player.has_animation(animation_to_play): + animation_player.play(animation_to_play) + elif current_animation != animation_to_play and \ + not animation_player.has_animation(animation_to_play): + current_animation = animation_to_play + escoria.logger.warn( + self, + "Character %s has no animation %s\nBypassing the missing animation and movement command." + % [parent.global_id, animation_to_play] + ) + + is_mirrored = parent.animations.directions[last_dir].mirrored + + +## Teleports this item to the target position.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target|`Node`|ESCLocation or ESCItem to teleport to|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func teleport(target: Node) -> void: + if target.has_method("get_interact_position"): + parent.global_position = target.get_interact_position() + escoria.logger.info( + self, + "Object %s is teleported to position %s." + % [target.name, parent.global_position] + ) + elif "position" in target: + escoria.logger.info( + self, + "Object %s teleported to position %s." + % [parent.global_id, str(target.global_position)] + ) + parent.global_position = target.global_position + else: + escoria.logger.error( + self, + "Target %s could not be teleported. Please configure the interact position parameter or create a child ESCLocation node." % target + ) + + +## Teleports this item to the target position.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target|`Vector2`|Vector2 target position to teleport to|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func teleport_to(target: Vector2) -> void: + escoria.logger.info( + self, + "Object %s teleported to position %s." + % [parent.global_id, str(target)] + ) + parent.global_position = target + + +## Walk to a given position[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pos|`Vector2`|Position to walk to|yes|[br] +## |p_walk_context|`ESCWalkContext`|Walk context to use|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func walk_to(pos: Vector2, p_walk_context: ESCWalkContext = null) -> void: + if not parent.terrain: + walk_stop(parent.get_global_position()) + return + + if task == MovableTask.WALK: + if walk_context.target_object == p_walk_context.target_object \ + or walk_context.target_position \ + == p_walk_context.target_position: + walk_context.fast = p_walk_context.fast + + walk_context = p_walk_context + + if task == MovableTask.NONE: + task = MovableTask.WALK + + walk_path = parent.terrain.get_simple_path(parent.get_position(), pos, true) + + if walk_path.size() == 0: + task = MovableTask.NONE + walk_stop(parent.get_global_position()) + set_process(false) + return + moved = true + walk_destination = walk_path[walk_path.size()-1] + path_ofs = 0 + task = MovableTask.WALK + set_process(true) + + +## We have finished walking. Set the idle pose and complete.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pos|`Vector2`|Final target position|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func walk_stop(pos: Vector2) -> void: + parent.global_position = pos + walk_path = [] + + task = MovableTask.NONE + moved = false + set_process(false) + + # If we're heading to an object and reached its interaction position, + # orient towards the defined interaction direction set on the object + # (if any), can be ESCItem or ESCLocation + if walk_context.target_object and \ + walk_context.target_object.node.player_orients_on_arrival: + + var orientation = _get_dir_deg(walk_context.target_object.node.interaction_angle - 90, + parent.animations) + if orientation >= parent.animations.dir_angles.size() or orientation < 0: + escoria.logger.warn(self, "Orientation is invalid for item '%s' (received value: %s degrees)\nPath to item: '%s'.\nDefaulting to 0 degrees." + % [ + walk_context.target_object.global_id, + orientation, + walk_context.target_object.node.get_path() + ]) + orientation = 0 + + last_dir = orientation + parent.get_animation_player().play( + parent.animations.idles[orientation].animation + ) + is_mirrored = parent.animations.idles[orientation].mirrored + else: + parent.get_animation_player().play( + parent.animations.idles[last_dir].animation + ) + is_mirrored = parent.animations.idles[last_dir].mirrored + + update_terrain() + + if walk_context.target_object: + escoria.logger.debug( + self, + "%s arrived at %s." % [ + parent.global_id, + walk_context.target_object.global_id + ] + ) + else: + escoria.logger.debug( + self, + "%s arrived at %s." % [ + parent.global_id, + walk_context.target_position + ] + ) + parent.arrived.emit(walk_context) + + +## Update the sprite scale and lighting.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |on_event_finished_name|`Variant`|Used if this function is called from an ESC event|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func update_terrain(on_event_finished_name = null) -> void: + if !parent.terrain or parent.terrain == null \ + or !is_instance_valid(parent.terrain): + return + if on_event_finished_name != null \ + and on_event_finished_name != ESCEventManager.EVENT_SETUP: + return + if parent.get("is_exit"): + return + if parent.get("dont_apply_terrain_scaling"): + return + if not parent.is_inside_tree(): + return + + var pos = parent.global_position + if pos.y <= RenderingServer.CANVAS_ITEM_Z_MAX: + parent.z_index = pos.y + else: + parent.z_index = RenderingServer.CANVAS_ITEM_Z_MAX + + var factor = parent.terrain.get_terrain(pos) + var scal = parent.terrain.get_scale_range(factor) + if scal != parent.get_scale(): + last_scale = scal + parent.scale = last_scale + + var color = parent.terrain.get_light(pos) + parent.modulate = color + + var sprite: Node = parent.get_sprite() + + # Do not flip the entire character, because that would conflict + # with shadows that expect to be siblings of $texture + # + # - Current sprite scale is >0, meaning it's currently heading to right + # - but calculated is_mirrored is <0, meaning it's going to head to left + # Or, on the contrary: + # - current sprite scale is <0, meaning it's currently heading to left + # - but calculated is_mirrored is >0, meaning it's going to head to right + # We're operating a 180° turn (from right to left, or from left to right) + # So we just inverse the sprite scale. + if is_mirrored and sprite.scale.x > 0 \ + or not is_mirrored and sprite.scale.x < 0: + sprite.scale.x *= -1 + parent.collision.scale.x *= -1 + + +## Get the player direction index based on degrees.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |deg|`int`|Degrees|yes|[br] +## |animations|`ESCAnimationResource`|Player animations script|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the direction index. (`int`) +func _get_dir_deg(deg: int, animations: ESCAnimationResource) -> int: + # We turn the angle by -90° because angle_to_point gives the angle + # against X axis, not Y + deg = wrapi(deg + 90, 0, 360) + + var dir = -1 + var i = 0 + + for direction_angle in animations.dir_angles: + if _is_angle_in_interval(deg, direction_angle): + dir = i + break + else: + i += 1 + continue + + # It's an error to have the animations misconfigured + if dir == -1: + escoria.logger.error( + self, + "No animation has been configured for angle %s." % str(deg) + ) + + return dir + + +## True if given angle is inside the interval given by a starting_angle and the size.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |angle|`float`|Angle to test|yes|[br] +## |direction_angle|`ESCDirectionAngle`|ESCDirectionAngle resource, containing the starting angle, and the size of interval eg: angle_start=90, angle_size=40 corresponds to angle between 90° and 130°|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns true if given angle is inside the interval given by a starting_angle and the size. (`bool`) +func _is_angle_in_interval( + angle: float, + direction_angle: ESCDirectionAngle +) -> bool: + var start_angle = direction_angle.angle_start + var end_angle = direction_angle.angle_start + direction_angle.angle_size + + if end_angle > 360 and angle < start_angle: + angle += 360 + + return (start_angle <= angle and angle <= end_angle) + + +## Sets the character's facing angle and plays the corresponding idle animation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |deg|`int`|Absolute angle in degrees (0-360) the character should face.|yes|[br] +## |wait|`float`|Optional delay in seconds before the script continues after turning.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_angle(deg: int, wait: float = 0.0) -> void: + if deg < 0 or deg > 360: + escoria.logger.error( + self, + "Invalid degree to turn to : %s. Valid angles are between 0 and 360." % str(deg) + ) + moved = true + var target_dir = _get_dir_deg(deg, parent.animations) + set_direction(target_dir, wait) + + +## Sets character's direction (instead of angle, see set_angle() and plays according animation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target_dir|`int`|int direction to set the character|yes|[br] +## |wait|`float`|float Wait this amount of seconds until continuing with turning around|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_direction(target_dir: int, wait: float = 0.0) -> void: + if target_dir < 0 or target_dir >= parent.animations.dir_angles.size(): + escoria.logger.warn( + self, + "Invalid direction to turn to : %s. Valid directions are between 0 and %s. " + % [str(target_dir), parent.animations.dir_angles.size() - 1] + + "Resetting target direction to 0 (up)" + ) + target_dir = 0 + + var current_dir = last_dir + var way_to_turn = get_shortest_way_to_dir(current_dir, target_dir) + + var dir = current_dir + while dir != target_dir: + dir += way_to_turn + if dir >= parent.animations.dir_angles.size(): + dir = 0 + if dir < 0: + dir = parent.animations.dir_angles.size() - 1 + + parent.get_animation_player().play( + parent.animations.idles[dir].animation + ) + if wait > 0.0: + await get_tree().create_timer(wait).timeout + is_mirrored = parent.animations.idles[dir].mirrored + + last_dir = dir + + # The character may have a state animation from before, which would be + # resumed, so we immediately force the correct idle animation + if parent.get_animation_player().get_animation() != \ + parent.animations.idles[last_dir].animation: + parent.get_animation_player().play( + parent.animations.idles[last_dir].animation + ) + update_terrain() + + +## Rotates the character so it faces the supplied node and plays the matching animation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`Node`|Target node whose position the character should face.|yes|[br] +## |wait|`float`|Optional delay in seconds before the script continues after turning.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func turn_to(item: Node, wait: float = 0.0) -> void: + set_angle( + wrapi( + rad_to_deg(parent.get_position().angle_to_point(item.get_position())), + 0, + 360 + ), + wait + ) + + +## The angle that corresponds to the current direction of the object.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the angle that corresponds to the current direction of the object. (`int`) +func _get_angle() -> int: + return parent.animations.dir_angles[last_dir].angle_start + + +## Return the shortest way to turn from a direction to another. Returned way is either: -1 (shortest way is to turn anti-clockwise) 0 (already at the right direction) 1 (clockwise).[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |current_dir|`int`|integer corresponding to the starting direction as defined in the attached ESCAnimationResource.directions.|yes|[br] +## |target_dir|`int`|integer corresponding to the target direction as defined in the attached ESCAnimationResource.directions.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func get_shortest_way_to_dir(current_dir: int, target_dir: int) -> int: + if current_dir < 0 or current_dir > parent.animations.dir_angles.size() - 1: + escoria.logger.error( + self, + "Invalid direction (current_dir) %s" % str(current_dir) + ) + + if target_dir < 0 or target_dir > parent.animations.dir_angles.size() - 1: + escoria.logger.error( + self, + "Invalid direction (target_dir) %s " % str(target_dir) + ) + + if current_dir == target_dir: + return 0 + + var internal = false + if max(current_dir, target_dir) - min(current_dir, target_dir) \ + < parent.animations.dir_angles.size() / 2: + internal = true + else: + internal = false + + if internal and current_dir < target_dir or \ + (not internal and current_dir > target_dir): + return 1 + else: + return -1 diff --git a/addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd.uid b/addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd.uid new file mode 100644 index 0000000..c927d3d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/behaviors/esc_movable.gd.uid @@ -0,0 +1 @@ +uid://yqgtbourjlud diff --git a/addons/escoria-core/game/core-scripts/esc/README.md b/addons/escoria-core/game/core-scripts/esc/README.md new file mode 100644 index 0000000..b4e3d61 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/README.md @@ -0,0 +1,253 @@ +# Welcome to ASHES! + +ASHES (**A**dventure **S**cripting **H**elping **ES**coria) is the language used to make your adventure game come to life. + +If you're familiar with languages like Python or GDScript, you should feel right at home using it. If you aren't, don't worry because both Python and GDScript are very beginner-friendly languages! + +If you're already familiar with ESCscript from earlier versions of Escoria, much of it is the same in terms of how files and events are laid out, as are the commands available. + +An evolving [Design Principles](design-principles.md) document is also available. + +# Script Files + +ASHES is placed into separate files with the extension `.esc`. These files can then be referenced from specific objects in Escoria, like rooms and inventory items. They're also used to setup and start your game. + +These files contain the events that a specific Escoria object can respond to or that are necessary for Escoria to run. + +# The Basics +As mentioned above, ASHES borrows heavily from Pythonic languages. If you're new to such languages, it might be a good idea to look into tutorials and/or documentation on Python and/or GDScript. ASHES can almost be considered a "subset" of such languages. + +The biggest differences include the following: +- `.esc` files contain one or more "events", existing at the highest level of scope (i.e. has no indentation at all). Events are denoted using `:event` where "event" is the name of the event you wish to implement. Refer to https://docs.escoria-framework.org/en/devel/scripting/z_esc_reference.html#events for more information on events. +- Currently there is no support for user-defined functions. Note that these are different from "events". +- There are some special variable conventions meant to make life a bit easier. +- Global variable scoping can require some care. + +# Data Types +ASHES currently recognizes the following data types: +- Integers +- Floats/reals +- Booleans +- Strings + +The keyword `nil` is used in place of "null" and/or where empty values are desired. + +## Strings +Strings can be concatenated using the `+` operator. Use this to construct dynamic strings that will display the value of a given variable, e.g. +``` +var first_string = "amazing" +var second_string = "This is an " + first_string + " feature!" +``` +Quotation marks in a string can be escaped in order to display them as actual output. + +# Precedence +Precedence is respected and interpreted in an order similar to Python and GDScript. + +# Syntax +ASHES supports the following language features: + +## Booleans and Boolean Operators +`true` and `false` are used for Boolean values. + +`not`, `and` and `or` are available as Boolean operators. `not` can also be represented with an exclamation mark, i.e. `!`. + +ASHES evaluates "truthiness" similar to how GDScript does. In our case, if a value can be cast to a Boolean, it is and then evaluated. + +Boolean evaluations should also "short circuit". + +## Arithmetic Operators +|Operation | Symbol | +|-----------------------|--------| +|Addition | + | +|Subtraction | - | +|Multiplication | * | +|Division | / | + +## Comparison Operators +|Operation | Symbol | +|---------------------------|-----------| +|Greater than | > | +|Greater than or equal | >= | +|Less than | < | +|Less than or equal | <= | +|Equality | == | + + +## Variables + +### Local +Variables can be declared using the `var` keyword, e.g. `var this_variable`. No types are needed as part of the declaration. + +You can also define the value of variables at the time of declaration, e.g. `var this_variable = 1`. + +Variables respect scoping rules similar to those of Python and GDScript. A variable is considered to be "local" to the block in which it's declared. + +### Global +Global variables can be declared for use across events and even scripts. The `global` keyword exists for this purpose and can be used in place of `var`, e.g. `global important_variable`. Again, no types are needed, and you can also define the value during declaration just as with local variables. + +**A Word of Caution**: Because Escoria only executes scripts as they are encountered, the order of execution follows the structure of the scene tree in Godot. That is, an ASHES script attached to a room will be executed before that of a child node. As such, the use of global variables should be closely watched, and it is worth noting that a global variable declaration's initializer (if one exists) will be run every time it is encountered. However, the variable will NOT be "redeclared" and so multiple executions of a line containing a global variable declaration (with or without an initializer) should be safe. + +## Comments +Text may be commented by using one (or more) hashtags/pound signs/number signs (i.e. a `#` character). Any text appearing to the right of the `#` sign for the remainder of that particular line will be treated as a comment. + +Block comments are not currently supported. + +## Branching +ASHES can branch using `if`, `elif`, and `else`. For example: +``` +if some_variable or other_variable: + print("Branch 1") +elif another_variable: + print("Branch 2") +else: + print("Branch 3") +``` + +## Looping +`while` loops are currently the only form of looping supported. For example: + +``` +while counter_variable < 10: + print(counter_variable) +``` +Loops can be broken out of by using the `break` keyword. Like similar languages, `break` will only exit the innermost loop containing the `break`. + +# Language Features +ASHES was created with making scripting adventure games easy. Here is a list of features built in to the language: + +## Events +Events remain largely the same as described earlier on this page. + +As a reminder: +- If you wish to cease execution of the current event, use the `stop` keyword at any point within the event. +- `pass` can be used in the same way as it is used in GDScript. +- One or more flags can be set (optional) beside the event name. Each one must be preceded by a `|` character. E.g. `:look | NO_UI | NO_TT` + +## Global IDs +In Escoria, every important entity is referenced by a global ID assigned by the developer (if you aren't sure where these are found, you can check the inspector panel of an appropriate Escoria node and look for the `Global ID` field). + +As you have likely seen, these are strings, and you treat them as strings in ASHES wherever you need to use a global ID, e.g. `if this_object == "object_1_id":`. + +To avoid having to always enclose a global ID in quotation marks, you can simply prefix the ID with a `$` character, e.g. `if this_object == $object_1_id`, and Escoria will know you're trying to refer to a global ID. + +## Built-in Variables +The following list of variables are always available in script: + +|Variable | Purpose | +|-----------------------|-------------------------------------------------------------------------------| +|`CURRENT_PLAYER` | References the current player scene being used, irrespective of its global ID.| +|`ESC_LAST_SCENE` | Holds the global ID of the last visited room. | +|`ESC_CURRENT_SCENE` | Holds the global ID of the current room. | +|`FORCE_LAST_SCENE_NULL`| Internal use only. | +|`ANIMATION_RESOURCES` | Internal use only. | + +## Inventory +If you want to check whether a particular object is currently in your inventory, you can make use of the `in inventory` feature. + +For example: `if $gold_brick in inventory:` + +## State Checking +You can check whether a particular `ESCObject` (or derivative) is active by using the `is active` feature, e.g. +`if $room_monster is active:` + +Similarly, you can for a particular state of an `ESCObject` (or derivative) by using the `is` keyword, e.g. `if $room_monster is "running_away":` + +# Commands +Built-in commands come from the `commands` directory in `escoria-core`. This makes it easy to create custom commands and be able to instantly use them in your scripts! + +Commands are called just as functions normally are, e.g. `say($player, "I'm saying this line!")` + +In addition, the `print` command exists to help you debug and otherwise send output to Godot's output window, e.g. `print("The value of this variable is: " + some_variable)` + +# Dialogs +A block of dialog begins with `?!` and is scoped the same as any other script; that is, with appropriate indentation. When a corresponding outdent is encountered, the block of dialog is considered finished. + +Blocks can be nested. You can use the `break` keyword to "bust out" of the current level of dialog and Escoria will move up to the previous level, if one exists. If the dialog is already at the top-most level, the `break` will conclude the dialog. + +If you need to break out of more than one level, you can specify the number of levels to break out of, e.g. `break 2` will move up two levels of dialog, and, if there aren't that many levels of dialog above the current one, the outermost dialog will be concluded. + +Use `done` if you want to conclude the entire dialog from any level of nesting. In other words, if you're 5 levels deep, instead of using `break 5` to dialog, you can simply use `done`. + +Each dialog choice is prefixed with a `-`, for example: +``` +- "Why are you here?" +``` + +Dialog choices can be made to be shown (or hidden) by placing a predicate in square brackets to the immediate right of the dialog choice. Say, for example, you wish to present the dialog choice from the previous point only if the character hasn't already asked this three times: +``` +- "Why are you here?" [num_times_asked < 3] +``` + +Script can be placed beneath a dialog choice so that if the corresponding dialog choice is chosen, that script will be executed. The script must be indented and treated like a new block of script. + +## Examples +Here is a dialog that takes place between the player and a worker where the player has only one dialog choice: +``` + # Variables declared above here and not shown for brevity + ?! + - "What is your name?" + say($current_player, "Who are you?") + + if !name_known: + say($worker, "I'm the worker") + else: + say($worker, "You already asked me that") + + name_known = true + + say($current_player, "This conversation is done!") +``` + +In this example, the only available dialog choice asks, "What is your name?" Note the indentation of the script that appears after the `?!` marker + +On selection of the dialog choice in the game, the player will be made to ask, "Who are you?" Depending on the value of `name_known`, the worker will reply with an appropriate answer, after which the value of `name_known` is updated, so you can imagine the worker giving two different answers depending on whether the player chooses this dialog choice for the first time, or any time subsequent. + +Once the dialog is finished, the player will remark, "This conversation is done!". + +Here is a slightly more complicated example, showing how dialogs can be nested: + +``` + # Variables declared above here and not shown for brevity + ?! + - "Can I ask you about Loom?" [!loom_conversation_done] + say("current_player", "What do you know about Loom?") + say("worker", "What do you want to know about Loom?") + ?! + - "Could it be created in Escoria?" + say("player", "Could Loom be created in Escoria?") + say("worker", "Yes!") + - "Is it an old game?" + say("current_player", "Is the game as old as you are?") + say("worker", "You'd better be kidding...") + ?! + - "I am." + say("current_player", "I am. Sorry.") + break + - "Not really." + say("current_player", "Not really.") + say("current_player", "Let's talk about something else.") + var break_level = 2 + break break_level + - "Is it a fun game?" + say("player", "Is Loom a fun game?") + say("worker", "Yes!") + fun_game_asked = true + - "I don't want to talk at all anymore." + turn_to("worker", "worker_face_down") + done + # This will take you back to the outer set of questions + - "I know enough about Loom." + loom_conversation_done = true + break +``` +(Notice that, in this example, instead of using the `$` convention for global IDs, we use an actual string to show that both methods work.) + +In this example, the player is engaged in a dialog with a worker, this time potentially inquiring about Loom, depending on the value of `loom_conversation_done`. + +We can see that if "Can I ask you about Loom?" is chosen, another level of dialog containing five new options will be displayed, each with its own associated script to execute. + +We can also see that if "Is it an old game?" is chosen, it will open up another level of dialog with two new options ("I am." and "Not really."). + +If we do end up selecting, "Not really.", the dialog will use the `break` keyword to move up two levels of dialog, i.e. the top-most level. Similarly, if "I know enough about Loom." is selected, it will move up one level of dialog, which happens to be the top-most level. + +If "I don't want to talk at all anymore." is chosen, `done` will conclude the entire dialog. diff --git a/addons/escoria-core/game/core-scripts/esc/commands/accept_input.gd b/addons/escoria-core/game/core-scripts/esc/commands/accept_input.gd new file mode 100644 index 0000000..f29f9c7 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/accept_input.gd @@ -0,0 +1,97 @@ +## `accept_input([type: String])` +## +## Sets how much input the game is to accept. This allows for cut scenes in which dialogue can be skipped (if [type] is set to SKIP), and ones where it can't (if [type] is set to NONE).[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |type|`String`|Type of inputs to accept (ALL) `ALL`: Accept all types of user input `SKIP`: Accept skipping dialogues but nothing else `NONE`: Deny all inputs (including opening menus) Warning The type of user input accepted will persist even after the current event has ended. Remember to reset the input type at the end of cut-scenes!|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name AcceptInputCommand + + +## The list of supported input types +const SUPPORTED_INPUT_TYPES = ["ALL", "NONE", "SKIP"] + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING], + ["ALL"] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not arguments[0] in SUPPORTED_INPUT_TYPES: + raise_error(self, "Invalid parameter. %s is not a valid value. " + + "Should be one of: %s" + % [ + arguments[0], + str(SUPPORTED_INPUT_TYPES) + ]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var mode = escoria.inputs_manager.INPUT_ALL + match command_params[0]: + "NONE": + mode = escoria.inputs_manager.INPUT_NONE + "SKIP": + mode = escoria.inputs_manager.INPUT_SKIP + + escoria.inputs_manager.input_mode = mode + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/accept_input.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/accept_input.gd.uid new file mode 100644 index 0000000..a526b0a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/accept_input.gd.uid @@ -0,0 +1 @@ +uid://cv20awuhkso8f diff --git a/addons/escoria-core/game/core-scripts/esc/commands/anim.gd b/addons/escoria-core/game/core-scripts/esc/commands/anim.gd new file mode 100644 index 0000000..6525654 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/anim.gd @@ -0,0 +1,94 @@ +## `anim(object: String, name: String[, reverse: Boolean])` +## +## Executes the animation specified in "name" on "object" without blocking. The next command in the event will be executed immediately after the animation is started.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object with the animation|yes|[br] +## |name|`String`|Name of the animation to start on the object.|yes|[br] +## |reverse|`Boolean`|Plays the animation in reverse when true|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name AnimCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_BOOL], + [null, null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var obj = escoria.object_manager.get_object(command_params[0]) + var anim_id = command_params[1] + var reverse = command_params[2] + var animator: ESCAnimationPlayer = \ + (obj.node as ESCItem).get_animation_player() + if reverse: + animator.play_backwards(anim_id) + else: + animator.play(anim_id) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/anim.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/anim.gd.uid new file mode 100644 index 0000000..6b8757a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/anim.gd.uid @@ -0,0 +1 @@ +uid://dfo38eqajwpfx diff --git a/addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd new file mode 100644 index 0000000..b64b166 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd @@ -0,0 +1,99 @@ +## `anim_block(object: String, name: String[, reverse: Boolean])` +## +## Executes the animation specified in "name" on "object" while blocking other events from starting. The next command in the event will be executed when the animation is finished playing.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object with the animation|yes|[br] +## |name|`String`|Name of the animation to play before continuing.|yes|[br] +## |reverse|`Boolean`|Plays the animation in reverse when true|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name AnimBlockCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_BOOL], + [null, null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var obj = escoria.object_manager.get_object(command_params[0]) + var anim_id = command_params[1] + var reverse = command_params[2] + var animator: ESCAnimationPlayer = \ + (obj.node as ESCItem).get_animation_player() + if reverse: + animator.play_backwards(anim_id) + else: + animator.play(anim_id) + if animator.get_length(anim_id) < 1.0: + return ESCExecution.RC_OK + var animation_finished = await animator.animation_finished + while animation_finished != anim_id: + animation_finished = await animator.animation_finished + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd.uid new file mode 100644 index 0000000..97be3dd --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd.uid @@ -0,0 +1 @@ +uid://brvcy1uj6vnpm diff --git a/addons/escoria-core/game/core-scripts/esc/commands/block_say.gd b/addons/escoria-core/game/core-scripts/esc/commands/block_say.gd new file mode 100644 index 0000000..d2b7dd2 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/block_say.gd @@ -0,0 +1,84 @@ +## `block_say()` +## +## `say` commands called subsequent to using the `block_say` command will reuse the dialog box type of the previous `say` command if both dialog box types between the two `say` commands match. Different dialog box types can be used across multiple `say` commands, with the latest one used being preserved for reuse by the next `say` command should the dialog box type specified by both `say` commands match. This reuse will continue until a call to `end_block_say` is made. Using `block_say` more than once prior to calling `end_block_say` is idempotent and has the following behaviour: - If no `say` command has yet been encountered since the first use of `block_say`, the result of using this command will be as described above. - If a `say` command has been encountered since the previous use of `block_say`, the dialog box used with that `say` command will continue to be reused for subsequent `say` commands should the dialog box type requested match. Note that the dialog box used with the next `say` command may be different than the one currently being reused. Example: `block say` `say player "Picture's looking good."` `say player "And so am I."` `end_block_say` This example will reuse the same dialog box type since they are the same between both `say` calls.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name BlockSayCommand + + +## Constructor[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init() -> void: + pass + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new(0) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.dialog_player.enable_preserve_dialog_box() + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/block_say.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/block_say.gd.uid new file mode 100644 index 0000000..a82a94c --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/block_say.gd.uid @@ -0,0 +1 @@ +uid://bc4qmptcudfai diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_push.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_push.gd new file mode 100644 index 0000000..37f99dd --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_push.gd @@ -0,0 +1,144 @@ +## `camera_push(target: String[, time: Number[, type: String]])` +## +## Pushes (moves) the camera so it points at a specific `target`. If the camera +## was following a target (like the player) previously, it will no longer follow +## this target.[br] +##[br] +## Make sure the target is reachable if camera limits have been configured.[br] +##[br] +## **Parameters**[br] +##[br] +## - *target*: Global ID of the `ESCItem` to push the camera to. `ESCItem`s have +## a "camera_node" property that can be set to point to a node (usually an +## `ESCLocation` node). If the "camera_node" property is empty, `camera_push` +## will point the camera at the `ESCItem`s location. If however, the `ESCItem` +## has its "camera_node" property set, the command will instead point the +## camera at the node referenced by the `ESCItem`s "camera_node" property.[br] +## - *time*: Number of seconds the transition should take (default: `1`)[br] +## - *type*: Transition type to use (default: `QUAD`)[br] +##[br] +## Supported transitions include the names of the values used +## in the "TransitionType" enum of the "Tween" type (without the "TRANS_" prefix):[br] +##[br] +## See https://docs.godotengine.org/en/stable/classes/class_tween.html?highlight=tween#enumerations[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraPushCommand + +## The list of supported transitions as per the link mentioned above +const SUPPORTED_TRANSITIONS = ["LINEAR","SINE","QUINT","QUART","QUAD" ,"EXPO","ELASTIC","CUBIC", + "CIRC","BOUNCE","BACK"] + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING, [TYPE_FLOAT, TYPE_INT], TYPE_STRING], + [null, 1, "QUAD"] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + + var target_pos = _get_target_pos(arguments[0]) + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + + if not camera.check_point_is_inside_viewport_limits(target_pos): + generate_viewport_warning(target_pos, camera) + return false + + if not arguments[2] in SUPPORTED_TRANSITIONS: + raise_error(self, ("Invalid transition type. Transition type {t_type} " + + "is not one of the accepted types: {allowed_types}").format( + { + "t_type": arguments[2], + "allowed_types": SUPPORTED_TRANSITIONS + } + )) + + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .push( + escoria.object_manager.get_object(command_params[0]).node, + command_params[1], + ClassDB.class_get_integer_constant("Tween", "TRANS_%s" % command_params[2]) + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) + + +## Gets the appropriate target position from the `ESCItem`, as used by the camera.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target_global_id|`String`|The `global_id` of the `ESCItem` to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Vector2` value. (`Vector2`) +func _get_target_pos(target_global_id: String) -> Vector2: + var target = escoria.object_manager.get_object(target_global_id).node as ESCItem + return target.get_camera_node().global_position diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_push.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_push.gd.uid new file mode 100644 index 0000000..774a8bc --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_push.gd.uid @@ -0,0 +1 @@ +uid://200iys3070ss diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_push_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_push_block.gd new file mode 100644 index 0000000..289cfc7 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_push_block.gd @@ -0,0 +1,159 @@ +## `camera_push_block(target: String[, time: Number[, type: String]])` +## +## Pushes (moves) the camera so it points at a specific `target`. If the camera +## was following a target (like the player) previously, it will no longer follow +## this target. Blocks until the command completes.[br] +##[br] +## Make sure the target is reachable if camera limits have been configured.[br] +##[br] +## **Parameters**[br] +##[br] +## - *target*: Global ID of the `ESCItem` to push the camera to. `ESCItem`s have +## a "camera_node" property that can be set to point to a node (usually an +## `ESCLocation` node). If the "camera_node" property is empty, `camera_push_block` +## will point the camera at the `ESCItem`s location. If however, the `ESCItem` +## has its "camera_node" property set, the command will instead point the +## camera at the node referenced by the `ESCItem`s "camera_node" property.[br] +## - *time*: Number of seconds the transition should take (default: `1`)[br] +## - *type*: Transition type to use (default: `QUAD`)[br] +##[br] +## Supported transitions include the names of the values used +## in the "TransitionType" enum of the "Tween" type (without the "TRANS_" prefix).[br] +##[br] +## See https://docs.godotengine.org/en/stable/classes/class_tween.html?highlight=tween#enumerations[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraPushBlockCommand + + +## The list of supported transitions as per the link mentioned above +const SUPPORTED_TRANSITIONS = ["LINEAR","SINE","QUINT","QUART","QUAD" ,"EXPO","ELASTIC","CUBIC", + "CIRC","BOUNCE","BACK"] + + +## Tween for blocking +var _camera_tween: Tween3 + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING, [TYPE_FLOAT, TYPE_INT], TYPE_STRING], + [null, 1, "QUAD"] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + + var target_pos = _get_target_pos(arguments[0]) + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + + if not camera.check_point_is_inside_viewport_limits(target_pos): + generate_viewport_warning(target_pos, camera) + return false + + if not arguments[2] in SUPPORTED_TRANSITIONS: + raise_error(self, ("Invalid transition type. Transition type {t_type} " + + "is not one of the accepted types: {allowed_types}").format( + { + "t_type": arguments[2], + "allowed_types": SUPPORTED_TRANSITIONS + } + )) + + return false + + _camera_tween = camera.get_tween() + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .push( + escoria.object_manager.get_object(command_params[0]).node, + command_params[1], + ClassDB.class_get_integer_constant("Tween", "TRANS_%s" % command_params[2]) + ) + + if command_params[1] > 0.0: + await _camera_tween.finished + escoria.logger.debug( + self, + "camera_push_block tween complete." + ) + + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) + + +## Gets the appropriate target position from the `ESCItem`, as used by the camera. ######## Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target_global_id|`String`|The `global_id` of the `ESCItem` to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Vector2` value. (`Vector2`) +func _get_target_pos(target_global_id: String) -> Vector2: + var target = escoria.object_manager.get_object(target_global_id).node as ESCItem + return target.get_camera_node().global_position diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_push_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_push_block.gd.uid new file mode 100644 index 0000000..c6eb4ae --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_push_block.gd.uid @@ -0,0 +1 @@ +uid://25hbb2uppyut diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_limits.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_limits.gd new file mode 100644 index 0000000..79dc992 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_limits.gd @@ -0,0 +1,98 @@ +## `camera_set_limits(camlimits_id: Integer)` +## +## Limits the current camera's movement to a limit defined in the `ESCRoom`'s +## definition. A limit is defined as an upper-left (x, y) coordinate, a width +## and a height that the camera must stay within. Multiple limits can be +## defined for a room, allowing for new areas to be seen once they have +## been 'unlocked'.[br] +##[br] +## **Parameters**[br] +##[br] +## - *camlimits_id*: Index of the camera limit defined in the `camera limits` +## list of the current `ESCRoom`[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraSetLimitsCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_INT], + [null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if escoria.main.current_scene.camera_limits.size() < arguments[0]: + raise_error(self, "Invalid limits id. Camera limit id (%d) is larger than the number of limits defined in this scene (%d)." + % [ + arguments[0], + escoria.main.current_scene.camera_limits.size() + ] + ) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + camera.clamp_to_viewport_limits() + escoria.main.set_camera_limits(command_params[0]) + + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_limits.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_limits.gd.uid new file mode 100644 index 0000000..caf1947 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_limits.gd.uid @@ -0,0 +1 @@ +uid://bd30dsr4dkwnb diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos.gd new file mode 100644 index 0000000..5403bcf --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos.gd @@ -0,0 +1,94 @@ +## `camera_set_pos(time: Number, x: Integer, y: Integer)` +## +## Moves the camera to the given absolute position over a time period.[br] +##[br] +## **Parameters**[br] +##[br] +## - *time*: Number of seconds the transition should take[br] +## - *x*: Target X coordinate[br] +## - *y*: Target Y coordinate[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraSetPosCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 3, + [[TYPE_FLOAT, TYPE_INT], TYPE_INT, TYPE_INT], + [null, null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + var new_pos: Vector2 = Vector2(arguments[1], arguments[2]) + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + + if not camera.check_point_is_inside_viewport_limits(new_pos): + generate_viewport_warning(new_pos, camera) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .set_target( + Vector2(command_params[1], command_params[2]), + command_params[0] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos.gd.uid new file mode 100644 index 0000000..c847d1f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos.gd.uid @@ -0,0 +1 @@ +uid://cl0s0gbnyt0bi diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos_block.gd new file mode 100644 index 0000000..2fe7dc0 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos_block.gd @@ -0,0 +1,110 @@ +## `camera_set_pos_block(time: Number, x: Integer, y: Integer)` +## +## Moves the camera to the given absolute position over a time period. Blocks +## until the command completes.[br] +##[br] +## Make sure the coordinates are reachable if camera limits have been configured.[br] +##[br] +## **Parameters**[br] +##[br] +## - *time*: Number of seconds the transition should take[br] +## - *x*: Target X coordinate[br] +## - *y*: Target Y coordinate[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraSetPosBlockCommand + + +# Tween for blocking +var _camera_tween: Tween3 + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 3, + [[TYPE_FLOAT, TYPE_INT], TYPE_INT, TYPE_INT], + [null, null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + var new_pos: Vector2 = Vector2(arguments[1], arguments[2]) + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + + if not camera.check_point_is_inside_viewport_limits(new_pos): + generate_viewport_warning(new_pos, camera) + return false + + _camera_tween = camera.get_tween() + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .set_target( + Vector2(command_params[1], command_params[2]), + command_params[0] + ) + + if command_params[0] > 0.0: + await _camera_tween.finished + escoria.logger.debug( + self, + "camera_set_pos_block tween complete." + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos_block.gd.uid new file mode 100644 index 0000000..ae00ebc --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_pos_block.gd.uid @@ -0,0 +1 @@ +uid://nekyymneqvj7 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target.gd new file mode 100644 index 0000000..8d5e3e0 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target.gd @@ -0,0 +1,92 @@ +## `camera_set_target(time: Number, object: String)` +## +## Configures the camera to follow the supplied Escoria object, easing into the new target over the requested duration.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |time|`Number`|Duration in seconds for the camera to transition to the target.|yes|[br] +## |object|`String`|Global ID of the object the camera should follow.|yes|[br] +## [br] +## For more details see: https://docs.escoria-framework.org/camera[br] +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraSetTargetCommand + + +## Provides the argument descriptor that defines the command signature.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor used to validate command arguments. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [[TYPE_FLOAT, TYPE_INT], TYPE_STRING], + [null, null] + ) + + +## Validates the command arguments and ensures the target object exists.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|Command arguments to check against the descriptor.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns `true` when the arguments are valid; otherwise `false`. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[1]): + raise_invalid_object_error(self, arguments[1]) + return false + + return true + + +## Applies the camera target using the provided arguments.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|Execution parameters `[time, object_id]` for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the command execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .set_target( + escoria.object_manager.get_object(command_params[1]).node, + command_params[0] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target.gd.uid new file mode 100644 index 0000000..1ca3ad2 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target.gd.uid @@ -0,0 +1 @@ +uid://bqhqgp0f4pnsb diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target_block.gd new file mode 100644 index 0000000..e071dec --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target_block.gd @@ -0,0 +1,110 @@ +## `camera_set_target_block(time: Number, object: String)` +## +## Configures the camera to follow the specified target `object` (ESCItem) as it moves +## around the current room. The transition to focus on the `object` will happen +## over a time period. Blocks until the command completes.[br] +##[br] +## The camera will move as close as it can if camera limits have been configured +## and the `object` is at coordinates that are not reachable.[br] +##[br] +## **Parameters**[br] +##[br] +## - *time*: Number of seconds the transition should take to move the camera +## to follow `object`[br] +## - *object*: Global ID of the target object[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraSetTargetBlockCommand + + +# Tween for blocking +var _camera_tween: Tween3 + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [[TYPE_FLOAT, TYPE_INT], TYPE_STRING], + [null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[1]): + raise_invalid_object_error(self, arguments[1]) + return false + + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + _camera_tween = camera.get_tween() + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .set_target( + escoria.object_manager.get_object(command_params[1]).node, + command_params[0] + ) + + if command_params[0] > 0.0: + await _camera_tween.finished + escoria.logger.debug( + self, + "camera_set_target_block tween complete." + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target_block.gd.uid new file mode 100644 index 0000000..72c29ae --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_target_block.gd.uid @@ -0,0 +1 @@ +uid://bmh3th6bers4o diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom.gd new file mode 100644 index 0000000..515aa44 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom.gd @@ -0,0 +1,73 @@ +## `camera_set_zoom(magnitude: Number[, time: Number])` +## +## Zooms the camera in/out to the desired `magnitude`. Values larger than '1' zoom +## the camera out while smaller values zoom in. These values are relative to the +## default zoom value of '1', not the current value. As such, while using a value +## of '0.5' would double the size of the graphics, running the same command again +## would result in no change. The zoom will happen over the given time period.[br] +##[br] +## **Parameters**[br] +##[br] +## - *magnitude*: Magnitude of zoom[br] +## - *time*: Number of seconds the transition should take, with a value of `0` +## meaning the zoom should happen instantly (default: `0`)[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraSetZoomCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [[TYPE_FLOAT, TYPE_INT], [TYPE_FLOAT, TYPE_INT]], + [null, 0.0] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .set_camera_zoom( + command_params[0], + command_params[1] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom.gd.uid new file mode 100644 index 0000000..d67e45a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom.gd.uid @@ -0,0 +1 @@ +uid://bf4or5jhbu2vk diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_block.gd new file mode 100644 index 0000000..07ea05f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_block.gd @@ -0,0 +1,106 @@ +## `camera_set_zoom_block(magnitude: Number[, time: Number])` +## +## Zooms the camera in/out to the desired `magnitude`. Values larger than '1' zoom +## the camera out while smaller values zoom in. These values are relative to the +## default zoom value of '1', not the current value. As such, while using a value +## of '0.5' would double the size of the graphics, running the same command again +## would result in no change. The zoom will happen over the given time period. +## Blocks until the command completes.[br] +##[br] +## Zoom operations might not be as smooth as desired if the requested zoom +## level results in an edge of the camera meeting any defined camera limits.[br] +##[br] +## **Parameters**[br] +##[br] +## - *magnitude*: Magnitude of zoom[br] +## - *time*: Number of seconds the transition should take, with a value of `0` +## meaning the zoom should happen instantly (default: `0`)[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraSetZoomBlockCommand + + +var _camera_tween: Tween3 + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [[TYPE_FLOAT, TYPE_INT], [TYPE_FLOAT, TYPE_INT]], + [null, 0.0] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + _camera_tween = camera.get_tween() + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + + camera.set_camera_zoom(command_params[0], command_params[1]) + + if command_params[1] > 0.0: + await _camera_tween.finished + escoria.logger.debug( + self, + "camera_set_zoom_block tween complete." + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_block.gd.uid new file mode 100644 index 0000000..61ef943 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_block.gd.uid @@ -0,0 +1 @@ +uid://co5ea82krtqct diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height.gd new file mode 100644 index 0000000..5ffa7c0 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height.gd @@ -0,0 +1,90 @@ +## `camera_set_zoom_height(pixels: Integer[, time: Number])` +## +## Zooms the camera in/out so it occupies the given height in pixels.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pixels|`Integer`|Target height in pixels|yes|[br] +## |time|`Number`|Number of seconds the transition should take, with a value of `0` meaning the zoom should happen instantly (default: `0`) For more details see: https://docs.escoria-framework.org/camera|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name CameraSetZoomHeightCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_INT, [TYPE_INT, TYPE_FLOAT]], + [null, 0.0] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if arguments[0] < 0: + raise_error(self, "Invalid height. Can't zoom to a negative height (%d)." % arguments[0]) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .set_camera_zoom( + command_params[0] / escoria.game_size.y, + command_params[1] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height.gd.uid new file mode 100644 index 0000000..5744632 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height.gd.uid @@ -0,0 +1 @@ +uid://618iqhw5r426 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height_block.gd new file mode 100644 index 0000000..b526d9c --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height_block.gd @@ -0,0 +1,104 @@ +## `camera_set_zoom_height_block(pixels: Integer[, time: Number])` +## +## Zooms the camera in/out so it occupies the given height in pixels. Blocks until the command completes.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pixels|`Integer`|Target height in pixels (integer values only)|yes|[br] +## |time|`Number`|Number of seconds the transition should take, with a value of `0` meaning the zoom should happen instantly (default: `0`) For more details see: https://docs.escoria-framework.org/camera|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name CameraSetZoomHeightBlockCommand + + +## Tween for blocking +var _camera_tween: Tween3 + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_INT, [TYPE_INT, TYPE_FLOAT]], + [null, 0.0] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if arguments[0] <= 0: + raise_error(self, "Invalid height. Can't zoom to a negative height (%d)." % arguments[0]) + return false + + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + _camera_tween = camera.get_tween() + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .set_camera_zoom( + command_params[0] / escoria.game_size.y, + command_params[1] + ) + + if command_params[1] > 0.0: + await _camera_tween.finished + escoria.logger.debug( + self, + "camera_set_zoom_height_block tween complete." + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height_block.gd.uid new file mode 100644 index 0000000..d127093 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_set_zoom_height_block.gd.uid @@ -0,0 +1 @@ +uid://ur4otce4l8pl diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_shift.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_shift.gd new file mode 100644 index 0000000..fb95bb0 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_shift.gd @@ -0,0 +1,127 @@ +## `camera_shift(x: Number, y: Number[, time: Number[, type: String]])` +## +## Shifts the camera by the given horizontal and vertical amounts relative to the +## current location.[br] +##[br] +## **Parameters**[br] +##[br] +## - *x*: Shift by x pixels along the x-axis[br] +## - *y*: Shift by y pixels along the y-axis[br] +## - *time*: Number of seconds the transition should take, with a value of `0` +## meaning the zoom should happen instantly (default: `1`)[br] +## - *type*: Transition type to use (default: `QUAD`)[br] +##[br] +## Supported transitions include the names of the values used +## in the "TransitionType" enum of the "Tween" type (without the "TRANS_" prefix):[br] +##[br] +## https://docs.godotengine.org/en/stable/classes/class_tween.html?highlight=tween#enumerations[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraShiftCommand + +## The list of supported transitions as per the link mentioned above +const SUPPORTED_TRANSITIONS = ["LINEAR","SINE","QUINT","QUART","QUAD" ,"EXPO","ELASTIC","CUBIC", + "CIRC","BOUNCE","BACK"] + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [ + [TYPE_INT, TYPE_FLOAT], + [TYPE_INT, TYPE_FLOAT], + [TYPE_INT, TYPE_FLOAT], + TYPE_STRING + ], + [null, null, 1, "QUAD"] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .shift( + Vector2( + command_params[0], + command_params[1] + ), + command_params[2], + ClassDB.class_get_integer_constant("Tween", "TRANS_%s" % command_params[3]) + ) + return ESCExecution.RC_OK + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not arguments[3] in SUPPORTED_TRANSITIONS: + raise_error(self, ("Invalid transition type. " + + "Transition type {t_type} is not one of the accepted types: {allowed_types}").format( + { + "t_type": arguments[3], + "allowed_types": SUPPORTED_TRANSITIONS + } + ) + ) + + return false + + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + var shift_by: Vector2 = Vector2(arguments[0], arguments[1]) + var new_pos: Vector2 = Vector2(camera.position.x + shift_by.x, camera.position.y + shift_by.y) + + if not camera.check_point_is_inside_viewport_limits(new_pos): + generate_viewport_warning(new_pos, camera) + return false + + return true + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_shift.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_shift.gd.uid new file mode 100644 index 0000000..5a333b3 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_shift.gd.uid @@ -0,0 +1 @@ +uid://dop4up3740fw diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_shift_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/camera_shift_block.gd new file mode 100644 index 0000000..a06a8db --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_shift_block.gd @@ -0,0 +1,145 @@ +## `camera_shift_block(x: Number, y: Number[, time: Number[, type: String]])` +## +## Shifts the camera by the given horizontal and vertical amounts relative to the +## current location. Blocks until the command completes.[br] +##[br] +## Make sure the destination coordinates are reachable if +## camera limits have been configured.[br] +##[br] +## **Parameters**[br] +##[br] +## - *x*: Shift by x pixels along the x-axis[br] +## - *y*: Shift by y pixels along the y-axis[br] +## - *time*: Number of seconds the transition should take, with a value of `0` +## meaning the transition should happen instantly (default: `1`)[br] +## - *type*: Transition type to use (default: `QUAD`)[br] +##[br] +## Supported transitions include the names of the values used +## in the "TransitionType" enum of the "Tween" type (without the "TRANS_" prefix).[br] +##[br] +## See https://docs.godotengine.org/en/stable/classes/class_tween.html?highlight=tween#enumerations[br] +##[br] +## For more details see: https://docs.escoria-framework.org/camera +## +## @ASHES +## @COMMAND +extends ESCCameraBaseCommand +class_name CameraShiftBlockCommand + + +# The list of supported transitions as per the link mentioned above +const SUPPORTED_TRANSITIONS = ["LINEAR","SINE","QUINT","QUART","QUAD" ,"EXPO","ELASTIC","CUBIC", + "CIRC","BOUNCE","BACK"] + + +# Tween for blocking +var _camera_tween: Tween3 + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [ + [TYPE_INT, TYPE_FLOAT], + [TYPE_INT, TYPE_FLOAT], + [TYPE_INT, TYPE_FLOAT], + TYPE_STRING + ], + [null, null, 1, "QUAD"] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera)\ + .shift( + Vector2( + command_params[0], + command_params[1] + ), + command_params[2], + ClassDB.class_get_integer_constant("Tween", "TRANS_%s" % command_params[3]) + ) + + if command_params[2] > 0.0: + await _camera_tween.finished + escoria.logger.debug( + self, + "camera_shift_block tween complete." + ) + return ESCExecution.RC_OK + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not arguments[3] in SUPPORTED_TRANSITIONS: + raise_error(self, ("Invalid transition type. " + + "Transition type {t_type} is not one of the accepted types: {allowed_types}").format( + { + "t_type": arguments[3], + "allowed_types": SUPPORTED_TRANSITIONS + } + ) + ) + + return false + + var camera: ESCCamera = escoria.object_manager.get_object(escoria.object_manager.CAMERA).node as ESCCamera + var shift_by: Vector2 = Vector2(arguments[0], arguments[1]) + var new_pos: Vector2 = Vector2(camera.position.x + shift_by.x, camera.position.y + shift_by.y) + + if not camera.check_point_is_inside_viewport_limits(new_pos): + generate_viewport_warning(new_pos, camera) + return false + + _camera_tween = camera.get_tween() + + return true + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/camera_shift_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/camera_shift_block.gd.uid new file mode 100644 index 0000000..5b9c59a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/camera_shift_block.gd.uid @@ -0,0 +1 @@ +uid://bpoavmb18a4x1 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/change_scene.gd b/addons/escoria-core/game/core-scripts/esc/commands/change_scene.gd new file mode 100644 index 0000000..2ae6e23 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/change_scene.gd @@ -0,0 +1,109 @@ +## `change_scene(path: String[, enable_automatic_transition: Boolean[, run_events: Boolean]])` +## +## Switches the game from the current scene to another scene. Use this to move the player to a new room when they walk through an unlocked door, for example.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |path|`String`|Path of the new scene|yes|[br] +## |enable_automatic_transition|`Boolean`|Automatically transition to the new scene (default: `true`)|no|[br] +## |run_events|`Boolean`|Run the standard ESC events of the new scene (default: `true`)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name ChangeSceneCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING, TYPE_BOOL, TYPE_BOOL], + [null, true, true] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array) -> bool: + if not super.validate(arguments): + return false + + if not ResourceLoader.exists(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + if not ResourceLoader.exists( + ESCProjectSettingsManager.get_setting(ESCProjectSettingsManager.GAME_SCENE) + ): + raise_error(self, "Game scene not found. The path set in 'ui/game_scene' was not found: %s." + % [ + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_SCENE + ) + ]) + + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.logger.info( + self, + "[%s] Changing scene to %s (enable_automatic_transition = %s)." + % [ + get_command_name(), + command_params[0], # scene file + command_params[1] # enable_automatic_transition + ] + ) + + escoria.room_manager.change_scene_to_file(command_params[0], command_params[1]) + + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/change_scene.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/change_scene.gd.uid new file mode 100644 index 0000000..d3ead58 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/change_scene.gd.uid @@ -0,0 +1 @@ +uid://crsuxfl4gx056 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/change_scene_godot.gd b/addons/escoria-core/game/core-scripts/esc/commands/change_scene_godot.gd new file mode 100644 index 0000000..9be93a8 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/change_scene_godot.gd @@ -0,0 +1,106 @@ +## `change_scene_godot(path: String)` +## +## Switches the game from the current scene to a Godot scene, without using Escoria.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |path|`String`|Path of the new scene|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name ChangeSceneGodotCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING], + [null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array) -> bool: + if not super.validate(arguments): + return false + + if not ResourceLoader.exists(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + if not ResourceLoader.exists( + ESCProjectSettingsManager.get_setting(ESCProjectSettingsManager.GAME_SCENE) + ): + raise_error(self, "Game scene not found. The path set in 'ui/game_scene' was not found: %s." + % [ + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_SCENE + ) + ]) + + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.logger.info( + self, + "[%s] Changing scene to Godot scene %s." + % [ + get_command_name(), + command_params[0] # scene file + ] + ) + + escoria.room_manager.change_scene_to_godot_file(command_params[0]) + + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/change_scene_godot.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/change_scene_godot.gd.uid new file mode 100644 index 0000000..3116874 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/change_scene_godot.gd.uid @@ -0,0 +1 @@ +uid://pmjvn84sk5rt diff --git a/addons/escoria-core/game/core-scripts/esc/commands/custom.gd b/addons/escoria-core/game/core-scripts/esc/commands/custom.gd new file mode 100644 index 0000000..afacce8 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/custom.gd @@ -0,0 +1,125 @@ +## `custom(object: String, node: String, func_name: String[, params...])` +## +## Executes the specified Godot function. This function must be in a script attached to a child node of a registered `ESCItem`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the target `ESCItem`|yes|[br] +## |node|`String`|Name of the child node of the target `ESCItem`|yes|[br] +## |func_name|`String`|Name of the function to be called|yes|[br] +## |params...|`Variant`|Optional arguments passed to the target function (arrays and objects are not supported). Additional positional parameters can be listed after the required ones, e.g. `custom("the_object", "the_node", "the_function", arg1, arg2)`.|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name CustomCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 3, + [TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_ARRAY], + [null, null, null, []], + [true], + true + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + elif not escoria.object_manager.get_object(arguments[0]).node.has_node( + arguments[1] + ): + raise_error(self, "Invalid node. Object with global id %s has no child node called %s." + % [ + arguments[0], + arguments[1], + ]) + return false + elif not escoria.object_manager.get_object(arguments[0]).node\ + .get_node( + arguments[1] + )\ + .has_method( + arguments[2] + ): + raise_error(self, "Invalid function. Object with global id %s and node %s has no function called %s." + % [ + arguments[0], + arguments[1], + arguments[2], + ]) + + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var object = escoria.object_manager.get_object( + command_params[0] + ) + # Global variables can be substituted into the command arguments by wrapping the global + # name in braces. + for loop in command_params[3].size():\ + if typeof(command_params[3][loop]) == TYPE_STRING: + command_params[3][loop] = escoria.globals_manager.replace_globals(command_params[3][loop]) + + object.node.get_node(command_params[1]).call( + command_params[2], + command_params[3] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/custom.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/custom.gd.uid new file mode 100644 index 0000000..7d51555 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/custom.gd.uid @@ -0,0 +1 @@ +uid://b4x0af2lb0r2g diff --git a/addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd b/addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd new file mode 100644 index 0000000..8f91433 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd @@ -0,0 +1,90 @@ +## `dec_global(name: String, value: Float)` +## +## Subtract the given value from the specified global.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Name of the global variable to decrement.|yes|[br] +## |value|`Float`|Value to be subtracted (default: 1)|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name DecGlobalCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING, TYPE_FLOAT], + [null, 1] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + var global_value = escoria.globals_manager.get_global(arguments[0]) + + if not (global_value is float or global_value is int): + raise_error(self, "Invalid global. Global %s isn't a float value." % arguments[0]) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.globals_manager.set_global( + command_params[0], + escoria.globals_manager.get_global(command_params[0]) - \ + command_params[1] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + ## Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd.uid new file mode 100644 index 0000000..0eac005 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd.uid @@ -0,0 +1 @@ +uid://dnh6ge8et23sh diff --git a/addons/escoria-core/game/core-scripts/esc/commands/enable_terrain.gd b/addons/escoria-core/game/core-scripts/esc/commands/enable_terrain.gd new file mode 100644 index 0000000..106c297 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/enable_terrain.gd @@ -0,0 +1,71 @@ +## `enable_terrain(node_name: String)` +## +## Enables the `ESCTerrain`'s `NavigationPolygonInstance` specified by the given node name. It will also disable the previously-activated `NavigationPolygonInstance`. Use this to change where the player can walk, allowing them to walk into the next room once a door has been opened, for example.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |node_name|`String`|Name of the `NavigationPolygonInstance` node to activate|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name EnableTerrainCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING], + [null] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var name: String = command_params[0] + if escoria.room_terrain.has_node(name): + var new_active_navigation_instance = \ + escoria.room_terrain.get_node(name) + escoria.room_terrain.current_active_navigation_instance.enabled = false + escoria.room_terrain.current_active_navigation_instance = \ + new_active_navigation_instance + escoria.room_terrain.current_active_navigation_instance.enabled = true + return ESCExecution.RC_OK + else: + raise_error(self, "Can not find terrain node. Terrain node %s could not be found." % name) + return ESCExecution.RC_ERROR + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/enable_terrain.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/enable_terrain.gd.uid new file mode 100644 index 0000000..263cd36 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/enable_terrain.gd.uid @@ -0,0 +1 @@ +uid://csytu4dbpmlca diff --git a/addons/escoria-core/game/core-scripts/esc/commands/end_block_say.gd b/addons/escoria-core/game/core-scripts/esc/commands/end_block_say.gd new file mode 100644 index 0000000..9a68f4f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/end_block_say.gd @@ -0,0 +1,85 @@ +## `end_block_say()` +## +## `say` commands used subsequent to using the `end_block_say` command will no longer reuse the dialog box type used by the previous `say` command(s) encountered. Using `end_block_say` more than once is safe and idempotent. Example: `block say` `say player "Picture's looking good."` `say player "And so am I."` `end_block_say` This example will reuse the same dialog box type since they are the same between both `say` calls.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name EndBlockSayCommand + + +## Constructor (bypasses the default constructor to avoid)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init() -> void: + pass + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new(0) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.dialog_player.disable_preserve_dialog_box() + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/end_block_say.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/end_block_say.gd.uid new file mode 100644 index 0000000..628013a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/end_block_say.gd.uid @@ -0,0 +1 @@ +uid://geem1o8pvlx1 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/hide_menu.gd b/addons/escoria-core/game/core-scripts/esc/commands/hide_menu.gd new file mode 100644 index 0000000..65567d7 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/hide_menu.gd @@ -0,0 +1,105 @@ +## `hide_menu(menu_type: String)` +## +## Hides either the main menu or the pause menu. Transitions from the menu using the default transition type (set in the Escoria project settings).[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |menu_type|`String`|Which menu to hide. Can be either `main` or `pause` (default: `main`)|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name HideMenuCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 0, + [TYPE_STRING], + ["main"] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not arguments[0] in ["main", "pause"]: + raise_error(self, "Menu %s is invalid." % arguments[0]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var transition_id: int + + # Transition out from menu + transition_id = escoria.main.scene_transition.transition( + "", + ESCTransitionPlayer.TRANSITION_MODE.OUT + ) + + if transition_id != ESCTransitionPlayer.TRANSITION_ID_INSTANT: + while await escoria.main.scene_transition.transition_done != transition_id: + pass + + if command_params[0] == "main": + escoria.game_scene.hide_main_menu() + elif command_params[0] == "pause": + escoria.game_scene.unpause_game() + + if escoria.main.current_scene != null: + transition_id = escoria.main.scene_transition.transition() + + if transition_id != ESCTransitionPlayer.TRANSITION_ID_INSTANT: + while await escoria.main.scene_transition.transition_done != transition_id: + pass + + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/hide_menu.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/hide_menu.gd.uid new file mode 100644 index 0000000..1d71a1f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/hide_menu.gd.uid @@ -0,0 +1 @@ +uid://crfuhlwxnc0lv diff --git a/addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd b/addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd new file mode 100644 index 0000000..2bacd61 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd @@ -0,0 +1,93 @@ +## `inc_global(name: String[, value: Float])` +## +## Adds the given value to the specified global.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Name of the global variable to increment.|yes|[br] +## |value|`Float`|Value to be added (default: 1)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name IncGlobalCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING, TYPE_FLOAT], + [null, 1] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.globals_manager.has(arguments[0]): + raise_error(self, "Invalid global. Global %s does not exist." % arguments[0]) + return false + + var global_value = escoria.globals_manager.get_global(arguments[0]) + + if not (global_value is float or global_value is int): + raise_error(self, "Invalid global. Global %s isn't a float value." % arguments[0]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.globals_manager.set_global( + command_params[0], + escoria.globals_manager.get_global(command_params[0]) +\ + command_params[1] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd.uid new file mode 100644 index 0000000..d5cfa59 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd.uid @@ -0,0 +1 @@ +uid://bqrb2x8misfnu diff --git a/addons/escoria-core/game/core-scripts/esc/commands/inventory_add.gd b/addons/escoria-core/game/core-scripts/esc/commands/inventory_add.gd new file mode 100644 index 0000000..d715dea --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/inventory_add.gd @@ -0,0 +1,89 @@ +## `inventory_add(item: String)` +## +## Adds an item to the inventory. If the player is picking up an object, you may want to use this command in conjunction with the `set_active` command so that the object 'disappears' from the scene as it's added to the inventory.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`String`|Global ID of the `ESCItem` to add to the inventory|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name InventoryAddCommand + + +## List of illegal strings that cannot be used in item names. +const ILLEGAL_STRINGS = ["/"] + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING], + [null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + for s in ILLEGAL_STRINGS: + if s in arguments[0]: + raise_error(self, "Invalid item name. Item name '%s' cannot contain the string '%s'." + % [arguments[0], s]) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.inventory_manager.add_item(command_params[0]) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/inventory_add.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/inventory_add.gd.uid new file mode 100644 index 0000000..8a81db5 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/inventory_add.gd.uid @@ -0,0 +1 @@ +uid://cw2akphgndm0v diff --git a/addons/escoria-core/game/core-scripts/esc/commands/inventory_remove.gd b/addons/escoria-core/game/core-scripts/esc/commands/inventory_remove.gd new file mode 100644 index 0000000..bd45f66 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/inventory_remove.gd @@ -0,0 +1,88 @@ +## `inventory_remove(item: String)` +## +## Removes an item from the inventory. You may wish to use this command in conjuction with the `set_active` command to show an item in the scene, simulating placing the item somewhere, for example.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`String`|Global ID of the `ESCItem` to remove from the inventory|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name InventoryRemoveCommand + +## List of illegal strings that cannot be used in item names. +const ILLEGAL_STRINGS = ["/"] + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING], + [null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + for s in ILLEGAL_STRINGS: + if s in arguments[0]: + raise_error(self, "Invalid item name. Item name '%s' cannot contain the string '%s'." + % [arguments[0], s]) + + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.inventory_manager.remove_item(command_params[0]) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/inventory_remove.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/inventory_remove.gd.uid new file mode 100644 index 0000000..a3d2691 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/inventory_remove.gd.uid @@ -0,0 +1 @@ +uid://wlm8tkgphk3f diff --git a/addons/escoria-core/game/core-scripts/esc/commands/play_snd.gd b/addons/escoria-core/game/core-scripts/esc/commands/play_snd.gd new file mode 100644 index 0000000..90dcc94 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/play_snd.gd @@ -0,0 +1,80 @@ +## `play_snd(file: String[, player: String[, start_position_seconds: Number]])` +## +## Plays the specified sound without blocking the currently running event.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |file|`String`|Sound file to play|yes|[br] +## |player|`String`|Sound player to use. Can either be `_sound`, which is used to play non- looping sound effects; `_music`, which plays looping music; `_ambient`, which plays looping ambient sounds; or `_speech`, which plays non-looping voice files (default: `_sound`)|no|[br] +## |start_position_seconds|`Number`|The position in seconds from which to start playing the sound file (default: `0.0`)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name PlaySndCommand + + +## The specified sound player +var _snd_player: String + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING, TYPE_STRING, TYPE_FLOAT], + [null, "_sound", 0.0] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[1]): + raise_error(self, "Invalid sound player. Sound player %s not registered." % arguments[1]) + return false + if not ResourceLoader.exists(arguments[0]): + raise_error(self, "Invalid argument. File '%s' not found." % arguments[0]) + return false + _snd_player = arguments[1] + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.object_manager.get_object(command_params[1]).node.set_state( + command_params[0], command_params[2] + ) + return ESCExecution.RC_OK diff --git a/addons/escoria-core/game/core-scripts/esc/commands/play_snd.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/play_snd.gd.uid new file mode 100644 index 0000000..c8d622d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/play_snd.gd.uid @@ -0,0 +1 @@ +uid://dlbtv3nvd65i3 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/print_internal.gd b/addons/escoria-core/game/core-scripts/esc/commands/print_internal.gd new file mode 100644 index 0000000..56a601f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/print_internal.gd @@ -0,0 +1,62 @@ +## `print(string: String)` +## +## INTERNAL USE ONLY Prints a message to the Godot debug window. Use this for debugging game state.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |string|`String`|The string to log|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name PrintCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING], + [""] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + # Replace the names of any globals in "{ }" with their value + print(escoria.globals_manager.replace_globals(command_params[0])) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/print_internal.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/print_internal.gd.uid new file mode 100644 index 0000000..a703739 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/print_internal.gd.uid @@ -0,0 +1 @@ +uid://m0d5y8wkwtu7 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/queue_event.gd b/addons/escoria-core/game/core-scripts/esc/commands/queue_event.gd new file mode 100644 index 0000000..f676fda --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/queue_event.gd @@ -0,0 +1,159 @@ +## `queue_event(object: String, event: String[, channel: String[, block: Boolean]])` +## +## Queue an event to run. If you queue multiple events on a channel and none of them are blocking events, all events will effectively run at the same time. As the events are placed on the channel's queue, if one event contains a blocking command, the next event on that channel's queue won't be processed until the blocking command finishes.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Object that holds the ESC script with the event|yes|[br] +## |event|`String`|Name of the event to queue|yes|[br] +## |channel|`String`|Channel to run the event on (default: `_front`). Using a previously unused channel name will create a new channel.|no|[br] +## |block|`Boolean`|Whether to wait for the queue to finish. This is only possible, if the queued event is not to be run on the same event as this command (default: `false`)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name QueueEventCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_BOOL], + [null, null, "_front", false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]) and not _is_current_room(arguments[0]): + raise_error(self, "No object or room with global id '%s' found." % arguments[0]) + return false + + var node = _get_scripted_node(arguments[0]) + + if not "esc_script" in node or node.esc_script == "": + raise_error( + self, + "Object/room with global id '%s' has no ESC script." % arguments[0] + ) + return false + + var esc_script = escoria.esc_compiler.load_esc_file(node.esc_script) + + if not arguments[1] in esc_script.events: + raise_error( + self, + "Event with name '%s' not found." % arguments[1] + ) + return false + + if arguments[3] and not escoria.event_manager.is_channel_free(arguments[2]): + raise_error( + self, + "The queue '%s' can't currently accept new events." % arguments[2] + ) + return false + + return true + + +## Whether global_id represents the current room the player is in.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`String`|The global ID to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns whether global_id represents the current room the player is in. True if global_id represents the current room, false otherwise. (`bool`) +func _is_current_room(global_id: String) -> bool: + return escoria.main.current_scene.global_id == global_id + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|Command parameters `[global_id, event_name, channel, block]` passed to the execution.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(arguments: Array) -> int: + var node = _get_scripted_node(arguments[0]) + + var esc_script = escoria.esc_compiler.load_esc_file(node.esc_script) + + return await escoria.event_manager.queue_event_from_esc( + esc_script, + arguments[1], ## event name + arguments[2], ## channel name + arguments[3] ## whether to block + ) + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass + + +# Fetches the object node or current room containing the desired ESC script. +# +# PRE: If global_id represents a room, then `escoria.main.current_scene` must be valid. +# +## #### Parameters +# +# - global_id: ID of the object or room with the desired ESC script. +# +# *Returns* +# +# The object node corresponding to global_id, or the current room if global_id is invalid or does +# not refer to an object registered with the object manager. +func _get_scripted_node(global_id: String): + var node = null + + if escoria.object_manager.has(global_id): + node = escoria.object_manager.get_object( + global_id + ).node + else: + node = escoria.main.current_scene + + return node diff --git a/addons/escoria-core/game/core-scripts/esc/commands/queue_event.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/queue_event.gd.uid new file mode 100644 index 0000000..f1333b3 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/queue_event.gd.uid @@ -0,0 +1 @@ +uid://dq77sck0tww2j diff --git a/addons/escoria-core/game/core-scripts/esc/commands/queue_resource.gd b/addons/escoria-core/game/core-scripts/esc/commands/queue_resource.gd new file mode 100644 index 0000000..845ae1c --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/queue_resource.gd @@ -0,0 +1,89 @@ +## `queue_resource(path: String[, front_of_queue: Boolean])` +## +## Queues the loading of the given resource into the resource cache.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |path|`String`|Path of the resource to cache|yes|[br] +## |front_of_queue|`Boolean`|Whether to put the resource at the front of the queue in order to load it as soon as possible (default: `false`)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name QueueResourceCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [], + [null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array) -> bool: + if not super.validate(arguments): + return false + + if not ResourceLoader.exists(arguments[0]): + raise_error( + self, + "Invalid resource. Resource %s was not found." % arguments[0] + ) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.resource_cache.queue_resource( + command_params[0], + command_params[1] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/queue_resource.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/queue_resource.gd.uid new file mode 100644 index 0000000..a9659e9 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/queue_resource.gd.uid @@ -0,0 +1 @@ +uid://dj0dmehl3dtqs diff --git a/addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd b/addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd new file mode 100644 index 0000000..a165f8d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd @@ -0,0 +1,83 @@ +## `rand_global(name: String, max_value: Integer)` +## +## Sets the given global to a random integer between 0 and `max_value` (inclusive). e.g. Setting `max_value` to 2 could result in '0', '1' or '2' being returned.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Name of the global variable that will receive the random value.|yes|[br] +## |max_value|`Integer`|Maximum possible integer value (inclusive) (default: 1)|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name RandGlobalCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_STRING, TYPE_INT], + [null, 1] + ) + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array) -> bool: + if not super.validate(arguments): + return false + + return true + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + randomize() + var rnd = randi() % (int(command_params[1]) + 1) + escoria.globals_manager.set_global( + command_params[0], + rnd + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd.uid new file mode 100644 index 0000000..a0ba6c5 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd.uid @@ -0,0 +1 @@ +uid://m375o4mi11k6 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/repeat.gd b/addons/escoria-core/game/core-scripts/esc/commands/repeat.gd new file mode 100644 index 0000000..0c0e4b6 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/repeat.gd @@ -0,0 +1,58 @@ +## `repeat()` +## +## Makes the current script loop back to the start. Currently the only way to exit the loop is via the `stop` command which will stop the script completely.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name RepeatCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 0, + [], + [] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + return ESCExecution.RC_CANCEL + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/repeat.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/repeat.gd.uid new file mode 100644 index 0000000..9c7d66d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/repeat.gd.uid @@ -0,0 +1 @@ +uid://b08e1y3kdavtu diff --git a/addons/escoria-core/game/core-scripts/esc/commands/save_game.gd b/addons/escoria-core/game/core-scripts/esc/commands/save_game.gd new file mode 100644 index 0000000..e830c49 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/save_game.gd @@ -0,0 +1,96 @@ +## `save_game(slot_id: Integer, savegame_description: String)` +## +## Saves the game in the [slot_id] slot, and sets the [savegame_description] in the savegame name/title. Example: `save_game 1 "description of save game"`[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |slot_id|`Integer`|Numeric slot index to store the save data in.|yes|[br] +## |savegame_description|`String`|Text shown to players when listing this save slot.|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SaveGameCommand + + +## Constructor[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init() -> void: + pass + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_INT, TYPE_STRING], + [1, "autosave"] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + return true + + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.save_manager.save_game(command_params[0], command_params[1]) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/save_game.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/save_game.gd.uid new file mode 100644 index 0000000..e9b836c --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/save_game.gd.uid @@ -0,0 +1 @@ +uid://c7oof8ykeylv3 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/say.gd b/addons/escoria-core/game/core-scripts/esc/commands/say.gd new file mode 100644 index 0000000..8c043c2 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/say.gd @@ -0,0 +1,156 @@ +## `say(player: String, text: String[, key: String[, type: String]])` +## +## Displays the specified string as dialog spoken by the player. This command blocks further event execution until the dialog has finished being 'said' (either as displayed text or as audible speech from a file). Global variables can be substituted into the text by wrapping the global name in braces, e.g. say player "I have {coin_count} coins remaining".[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |player|`String`|Global ID of the `ESCPlayer` or `ESCItem` object that is active. You can specify `current_player` in order to refer to the currently active player, e.g. in cases where multiple players are playable such as in games like Maniac Mansion or Day of the Tentacle.|yes|[br] +## |text|`String`|Text to display.|yes|[br] +## |key|`String`|Translation key (default: nil)|no|[br] +## |type|`String`|Dialog type to use. One of `floating` or `avatar`. (default: the value set in the setting "Escoria/UI/Default Dialog Type") The text supports translation keys by prepending the key followed by a colon (`:`) to the text. For more details see: https://docs.escoria-framework.org/en/devel/getting_started/dialogs.html#translations Playing an audio file while the text is being displayed is also supported by this mechanism. For more details see: https://docs.escoria-framework.org/en/devel/getting_started/dialogs.html#recorded_speech Example: `say(player, "Picture's looking good.", "ROOM1_PICTURE")`|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SayCommand + +## The keyword used to refer to the current player +const CURRENT_PLAYER_KEYWORD = "CURRENT_PLAYER" + +## Regex to match global variables in strings +var globals_regex : RegEx + + +## Constructor[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init() -> void: + globals_regex = RegEx.new() + ## Use look-ahead/behind to capture the term (i.e. global) in braces + globals_regex.compile("(?<=\\{)(.*)(?=\\})") + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING], + [ + null, + null, + "", + "" + ], + [ + true, + false, + false, + true + ] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if arguments[0].to_upper() != CURRENT_PLAYER_KEYWORD \ + and not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var dict: Dictionary + + escoria.current_state = escoria.GAME_STATE.DIALOG + + if !escoria.dialog_player: + raise_error( + self, + "No dialog player was registered and the 'say' command was encountered." + ) + escoria.current_state = escoria.GAME_STATE.DEFAULT + return ESCExecution.RC_ERROR + + if not escoria.main.current_scene.player: + escoria.logger.warn( + self, + "[%s]: No player item in the current scene was registered and the say command was encountered." + % get_command_name() + ) + escoria.current_state = escoria.GAME_STATE.DEFAULT + return ESCExecution.RC_CANCEL + + # Replace the names of any globals in "{ }" with their value + command_params[1] = escoria.globals_manager.replace_globals(command_params[1]) + + var speaking_character_global_id = escoria.main.current_scene.player.global_id \ + if command_params[0].to_upper() == CURRENT_PLAYER_KEYWORD \ + else command_params[0] + + escoria.dialog_player.say( + speaking_character_global_id, + command_params[3], + command_params[1], + command_params[2] + ) + await escoria.dialog_player.say_finished + escoria.current_state = escoria.GAME_STATE.DEFAULT + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/say.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/say.gd.uid new file mode 100644 index 0000000..7debc2d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/say.gd.uid @@ -0,0 +1 @@ +uid://cr741o6arn1qr diff --git a/addons/escoria-core/game/core-scripts/esc/commands/sched_event.gd b/addons/escoria-core/game/core-scripts/esc/commands/sched_event.gd new file mode 100644 index 0000000..5b57dca --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/sched_event.gd @@ -0,0 +1,149 @@ +## `sched_event(time: Number, object: String, event: String)` +## +## Schedules an event to run at a later time. If another event is already running when the scheduled event is supposed to start, execution of the scheduled event begins when the already-running event ends.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |time|`Number`|Time in seconds until the scheduled event starts|yes|[br] +## |object|`String`|Global ID of the ESCItem that holds the ESC script|yes|[br] +## |event|`String`|Name of the event to schedule|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SchedEventCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 3, + [[TYPE_FLOAT, TYPE_INT], TYPE_STRING, TYPE_STRING], + [null, null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[1]) and not _is_current_room(arguments[1]): + raise_invalid_object_error(self, arguments[1]) + return false + + var node = _get_scripted_node(arguments[1]) + + if not "esc_script" in node or node.esc_script == "": + raise_error( + self, + "Object/room with global id '%s' has no ESC script." % arguments[1] + ) + return false + + var esc_script = escoria.esc_compiler.load_esc_file(node.esc_script) + + if not arguments[2] in esc_script.events: + raise_error( + self, + "Event with name '%s' not found." % arguments[2] + ) + return false + return true + + +## Whether global_id represents the current room the player is in.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`String`|The global ID to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns whether global_id represents the current room the player is in. True if global_id represents the current room, false otherwise. (`bool`) +func _is_current_room(global_id: String) -> bool: + return escoria.main.current_scene.global_id == global_id + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var node = _get_scripted_node(command_params[1]) + + var esc_script = escoria.esc_compiler.load_esc_file(node.esc_script) + + escoria.event_manager.schedule_event( + esc_script.events[command_params[2]], # ESCGrammarStmts.Event + command_params[0], # timeout float + command_params[1] # object string + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass + + +## Fetches the object node or current room containing the desired ESC script. PRE: If global_id represents a room, then `escoria.main.current_scene` must be valid.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`String`|ID of the object or room with the desired ESC script.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the object node corresponding to global_id, or the current room if global_id is invalid or does not refer to an object registered with the object manager. (`Variant`) +func _get_scripted_node(global_id: String): + var node = null + + if escoria.object_manager.has(global_id): + node = escoria.object_manager.get_object( + global_id + ).node + else: + node = escoria.main.current_scene + + return node diff --git a/addons/escoria-core/game/core-scripts/esc/commands/sched_event.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/sched_event.gd.uid new file mode 100644 index 0000000..52bd92b --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/sched_event.gd.uid @@ -0,0 +1 @@ +uid://bkarxmeql5i7k diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_active.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_active.gd new file mode 100644 index 0000000..90846e5 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_active.gd @@ -0,0 +1,84 @@ +## `set_active(object: String, active: Boolean)` +## +## Changes the "active" state of the object. Inactive objects are invisible in the room.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object whose active state should change.|yes|[br] +## |active|`Boolean`|Whether the object should be marked as active (`true`) or inactive (`false`). object* Global ID of the object active* Whether `object` should be active. `active` can be `true` or `false`.|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetActiveCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_BOOL], + [null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.object_manager.get_object(command_params[0]).active = \ + command_params[1] + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_active.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_active.gd.uid new file mode 100644 index 0000000..6df3f0d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_active.gd.uid @@ -0,0 +1 @@ +uid://csxo71g5vseks diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_active_if_exists.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_active_if_exists.gd new file mode 100644 index 0000000..ebb02cb --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_active_if_exists.gd @@ -0,0 +1,64 @@ +## *** FOR INTERNAL USE ONLY *** `set_active_if_exists(object: String, active: Boolean)` +## +## Changes the "active" state of the object in the current room if it currently exists in the object manager. If it doesn't, then, unlike set_active, we don't fail and we just carry on. Inactive objects are invisible in the room.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object whose active state should change if it is registered.|yes|[br] +## |active|`Boolean`|Whether the object should be marked as active (`true`) or inactive (`false`).|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetActiveIfExistsCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_BOOL], + [null, null] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + if escoria.object_manager.has(command_params[0]): + escoria.object_manager.get_object(command_params[0]).active = \ + command_params[1] + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_active_if_exists.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_active_if_exists.gd.uid new file mode 100644 index 0000000..8d2ef23 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_active_if_exists.gd.uid @@ -0,0 +1 @@ +uid://dkty4qemblf7c diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_angle.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_angle.gd new file mode 100644 index 0000000..ef503c4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_angle.gd @@ -0,0 +1,91 @@ +## `set_angle(object: String, target_degrees: Number[, wait: Number])` +## +## Turns a movable `ESCItem` or `ESCPlayer` to face a given target direction. Angles 0 and 360 are the same and correspond to UP/NORTH, 90 is RIGHT/EAST, 180 is DOWN/SOUTH, 270 is LEFT/WEST etc. The rotation direction will be determined by the shortest path - e.g. rotating from facing up (0 degrees) to left (270) will be a 90 degree turn anti-clockwise rather than a 270 degree clockwise turn. The final animation used is determined by the directions which have been configured for the object. If the item has a direction configured which has been drawn to show it facing to the right, and this direction has been defined to cover the angle from 45 to 135 degrees, setting the target angle to 120 degrees will result in the right-facing animation being used. The number of intermediate animations shown while turning the item will depend on the directions specified in the item's definition. A 16 direction character will turn through 8 different directions to turn 180 degrees, a 4 direction character only 2. The wait time will determine how long the idle animation for each direction is played before using the next direction's animation. As such, if wait was set to 1 second, a 16 direction character would take 8 seconds to turn 180 degrees, a 4 direction character would take 2 seconds.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to turn|yes|[br] +## |target_degrees|`Number`|Number of degrees by which `object` is to be turned|yes|[br] +## |wait|`Number`|Number of seconds to wait for while playing each animation occurring between the current angle of `object` and the target angle. A value of `0` will complete the turn immediately (default: `0`)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetAngleCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, [TYPE_FLOAT, TYPE_INT], [TYPE_FLOAT, TYPE_INT]], + [null, null, 0.0] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + # HACK Countering the fact that angle_to_point() function gives + # angle against X axis not Y, we need to check direction using (angle-90°). + # Since the ESC command already gives the right angle, we add 90. + escoria.object_manager.get_object(command_params[0]).node\ + .set_angle( + wrapi(int(command_params[1]) - 90, 0, 360), + command_params[2] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_angle.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_angle.gd.uid new file mode 100644 index 0000000..74b7a39 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_angle.gd.uid @@ -0,0 +1 @@ +uid://b2f44aoehgpu diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd new file mode 100644 index 0000000..7ae818f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd @@ -0,0 +1,112 @@ +## `set_animations(object: String, animations: String)` +## +## Sets the animation resource for the given `ESCPlayer` or movable `ESCItem`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object whose animation resource is to be updated|yes|[br] +## |animations|`String`|The path of the animation resource to use|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetAnimationsCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING], + [null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + if not ResourceLoader.exists(arguments[1]): + raise_error( + self, + "Invalid animation resource. The animation resource '%s' was not found." % arguments[1] + ) + return false + + (escoria.object_manager.get_object(arguments[0]).node as ESCPlayer).validate_animations(load(arguments[1])) + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(command_params[0]).node as ESCPlayer)\ + .animations = load(command_params[1]) + if not escoria.globals_manager.has( + escoria.room_manager.GLOBAL_ANIMATION_RESOURCES + ): + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_ANIMATION_RESOURCES, + {}, + true + ) + var animations: Dictionary = escoria.globals_manager.get_global( + escoria.room_manager.GLOBAL_ANIMATION_RESOURCES + ) + if animations.is_empty(): + animations = {} + animations[command_params[0]] = command_params[1] + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_ANIMATION_RESOURCES, + animations, + true + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd.uid new file mode 100644 index 0000000..b0da463 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_animations.gd.uid @@ -0,0 +1 @@ +uid://dsj4wgejxknu8 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_direction.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_direction.gd new file mode 100644 index 0000000..43bfd70 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_direction.gd @@ -0,0 +1,106 @@ +## `set_direction(object: String, direction_id: Integer[, wait: Number])` +## +## Turns a movable `ESCItem` or `ESCPlayer` to face a given target direction id (between 0 and 3 for a 4-directional character, or between 0 and 7 for an 8-directional character). 4-directional : 0 : UP / NORTH 1 : RIGHT / EAST 2 : DOWN / SOUTH 3 : LEFT / WEST 8-directional : 0 : UP / NORTH 1 : UP-RIGHT / NORTH-EAST 2 : RIGHT / EAST 3 : DOWN-RIGHT / SOUTH-EAST 4 : DOWN / SOUTH 5 : DOWN-LEFT / SOUTH-WEST 6 : LEFT / WEST 7 : TOP-LEFT / NORTH-WEST[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to turn|yes|[br] +## |direction_id|`Integer`|Target direction index from the animation resource.|yes|[br] +## |wait|`Number`|Seconds to spend on each intermediate step when rotating toward the target direction (0 means immediate).|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetDirectionCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, [TYPE_INT], [TYPE_FLOAT, TYPE_INT]], + [null, null, 0.0] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + escoria.logger.error( + self, + "[%s]: invalid object. Object with global id %s not found." + % [get_command_name(), arguments[0]] + ) + return false + elif not escoria.object_manager.get_object(arguments[0]).node.is_movable: + escoria.logger.error( + self, + "[%s]: invalid object. Object with global id %s is not movable." + % [get_command_name(), arguments[0]] + ) + return false + elif arguments[1] < 0 or arguments[1] > escoria.object_manager.get_object(arguments[0]).node \ + .get_directions_quantity() - 1: + escoria.logger.error( + self, + "[%s]: invalid direction id for object %s. Valid values are integer values between 0 and %s." + % [get_command_name(), arguments[0], escoria.object_manager.get_object(arguments[0]) \ + .node.get_directions_quantity() - 1] + ) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.object_manager.get_object(command_params[0]).node \ + .set_direction(command_params[1], command_params[2]) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_direction.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_direction.gd.uid new file mode 100644 index 0000000..e22848b --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_direction.gd.uid @@ -0,0 +1 @@ +uid://xq7oqymni1p1 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_global.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_global.gd new file mode 100644 index 0000000..687fe16 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_global.gd @@ -0,0 +1,97 @@ +## `set_global(name: String, value: String|Integer|Boolean[, force: Boolean=false])` +## +## Changes the value of a global.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Name of the global variable to set.|yes|[br] +## |value|`String` or `Integer` or `Boolean`|Value to set the global to (can be of type string, boolean, integer.)|yes|[br] +## |force|`Boolean`|if false, setting a global whose name is reserved will trigger an error. Defaults to false. Reserved globals are: ESC_LAST_SCENE, FORCE_LAST_SCENE_NULL, ANIMATION_RESOURCES, ESC_CURRENT_SCENE|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetGlobalCommand + +## The list of illegal strings that cannot be used in global names. +const ILLEGAL_STRINGS = ["/"] + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, [TYPE_INT, TYPE_BOOL, TYPE_STRING], TYPE_BOOL], + [null, null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + for s in ILLEGAL_STRINGS: + if s in arguments[0]: + raise_error( + self, + "Invalid global variable. Global variable '%'s cannot contain the string '%s'." + % [arguments[0], s] + ) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.globals_manager.set_global( + command_params[0], + command_params[1], + command_params[2] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_global.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_global.gd.uid new file mode 100644 index 0000000..42593d4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_global.gd.uid @@ -0,0 +1 @@ +uid://dck41cyhcsmh diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_globals.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_globals.gd new file mode 100644 index 0000000..3107057 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_globals.gd @@ -0,0 +1,65 @@ +## `set_globals(pattern: String, value: String|Integer|Boolean)` +## +## Changes the value of multiple globals using a wildcard pattern, where `*` matches zero or more arbitrary characters and `?` matches any single character except a period (".").[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pattern|`String`|Pattern to use to match the names of the globals to change|yes|[br] +## |value|`String` or `Integer` or `Boolean`|Value to set (can be of type string, boolean, integer or float)|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetGlobalsCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, [TYPE_BOOL, TYPE_STRING, TYPE_INT]], + [null, null] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.globals_manager.set_global_wildcard( + command_params[0], + command_params[1] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_globals.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_globals.gd.uid new file mode 100644 index 0000000..9e2d7fb --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_globals.gd.uid @@ -0,0 +1 @@ +uid://cn8cx2f8vgdoh diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_gui_visible.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_gui_visible.gd new file mode 100644 index 0000000..ec83445 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_gui_visible.gd @@ -0,0 +1,64 @@ +## `set_gui_visible(visible: Boolean)` +## +## Show or hide the GUI.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |visible|`Boolean`|Whether the GUI should be visible (`true` or `false`)|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetGuiVisibleCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [TYPE_BOOL], + [null] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + if command_params[0]: + escoria.main.current_scene.game.show_ui() + else: + escoria.main.current_scene.game.hide_ui() + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_gui_visible.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_gui_visible.gd.uid new file mode 100644 index 0000000..b11a09d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_gui_visible.gd.uid @@ -0,0 +1 @@ +uid://sdpjjxl8hfx2 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_interactive.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_interactive.gd new file mode 100644 index 0000000..007e23a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_interactive.gd @@ -0,0 +1,84 @@ +## `set_interactive(object: String, interactive: Boolean)` +## +## Sets whether an object is interactive.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to change|yes|[br] +## |interactive|`Boolean`|Whether the object should be interactive|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetInteractiveCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_BOOL], + [null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.object_manager.get_object(command_params[0]).interactive = \ + command_params[1] + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_interactive.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_interactive.gd.uid new file mode 100644 index 0000000..db36a16 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_interactive.gd.uid @@ -0,0 +1 @@ +uid://br2bhufl56d diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_item_custom_data.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_item_custom_data.gd new file mode 100644 index 0000000..2e90ce3 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_item_custom_data.gd @@ -0,0 +1,66 @@ +## *** FOR INTERNAL USE ONLY *** `set_item_custom_data(item: String, custom_data: Dictionary)` +## +## Sets the "custom_data" of the item if it currently exists in the object manager.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`String`|Global ID of the item whose `custom_data` should be updated.|yes|[br] +## |custom_data|`Dictionary`|Dictionary assigned to the item's `custom_data` property (an empty dictionary is used when `null`).|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetItemCustomDataCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_DICTIONARY], + [null, null] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var global_id: String = command_params[0] + if escoria.object_manager.has(global_id): + var item = escoria.object_manager.get_object(global_id).node + if item is ESCItem: + item.set_custom_data(command_params[1]) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_item_custom_data.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_item_custom_data.gd.uid new file mode 100644 index 0000000..1728eac --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_item_custom_data.gd.uid @@ -0,0 +1 @@ +uid://b61dtript58y7 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_speed.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_speed.gd new file mode 100644 index 0000000..cd8f2df --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_speed.gd @@ -0,0 +1,82 @@ +## `set_speed(object: String, speed: Integer)` +## +## Sets the speed of a `ESCPlayer` or movable `ESCItem`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the `ESCPlayer` or movable `ESCItem`|yes|[br] +## |speed|`Integer`|Speed value for `object` in pixels per second.|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetSpeedCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_INT], + [null, null] + ) + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + return true + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(command_params[0]).node as ESCItem).\ + set_velocity(command_params[1]) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_speed.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_speed.gd.uid new file mode 100644 index 0000000..7f67e58 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_speed.gd.uid @@ -0,0 +1 @@ +uid://brlhvswmvylqp diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_state.gd b/addons/escoria-core/game/core-scripts/esc/commands/set_state.gd new file mode 100644 index 0000000..597751f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_state.gd @@ -0,0 +1,88 @@ +## `set_state(object: String, state: String[, immediate: Boolean])` +## +## Changes the state of `object` to the one specified. This command is primarily used to play animations. If the specified object's associated animation player has an animation with the same name, that animation is also played. When the "state" of the object is set - for example, a door may be set to a "closed" state - this plays the matching "close" animation if one exists (to show the door closing in the game). When you re-enter the room (via a different entry), or restore a saved game, the state of the door object will be restored - showing the door as a closed door.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object whose state is to be changed|yes|[br] +## |state|`String`|Name of the state to be set|yes|[br] +## |immediate|`Boolean`|If an animation for the state exists, specifies whether it is to skip to the last frame. Can be `true` or `false`.|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SetStateCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_BOOL], + [null, null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(command_params[0]) as ESCObject).set_state( + command_params[1], + command_params[2] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/set_state.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/set_state.gd.uid new file mode 100644 index 0000000..5ec34a6 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/set_state.gd.uid @@ -0,0 +1 @@ +uid://rcayn4b2ah77 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd b/addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd new file mode 100644 index 0000000..3ee2935 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd @@ -0,0 +1,111 @@ +## `show_menu(menu_type: String)` +## +## Shows either the main menu or the pause menu. Transitions to the menu using the default transition type (set in the Escoria project settings).[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |menu_type|`String`|Which menu to show. Can be either `main` or `pause` (default: `main`)|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name ShowMenuCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 0, + [TYPE_STRING], + ["main"] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not arguments[0] in ["main", "pause"]: + raise_error( + self, + "Menu '%s' is invalid." % arguments[0] + ) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + if not escoria.game_scene.is_inside_tree(): + escoria.add_child(escoria.game_scene) + + # Transition out from current scene + var transition_id = escoria.main.scene_transition.transition( + "", + ESCTransitionPlayer.TRANSITION_MODE.OUT + ) + + if transition_id != ESCTransitionPlayer.TRANSITION_ID_INSTANT: + while await escoria.main.scene_transition.transition_done \ + != transition_id: + pass + + if command_params[0] == "main": + escoria.game_scene.show_main_menu() + elif command_params[0] == "pause": + escoria.game_scene.pause_game() + + # Transition in to menu + transition_id = escoria.main.scene_transition.transition() + + if transition_id != ESCTransitionPlayer.TRANSITION_ID_INSTANT: + while await escoria.main.scene_transition.transition_done \ + != transition_id: + pass + + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd.uid new file mode 100644 index 0000000..5329d9b --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/show_menu.gd.uid @@ -0,0 +1 @@ +uid://1nersljovg8b diff --git a/addons/escoria-core/game/core-scripts/esc/commands/slide.gd b/addons/escoria-core/game/core-scripts/esc/commands/slide.gd new file mode 100644 index 0000000..d168dfe --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/slide.gd @@ -0,0 +1,165 @@ +## `slide(object: String, target: String[, speed: Integer])` +## +## Moves `object` towards the position of `target`. This command is non-blocking.[br] +##[br] +## **Warning** This command does not respect the room's navigation polygons, so `object` can be moved even when outside walkable areas![br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object that should slide.|yes|[br] +## |target|`String`|Global ID of the object whose position is used as the destination.|yes|[br] +## |speed|`Integer`|Optional slide speed in pixels per second (defaults to the object's configured speed when negative).|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SlideCommand + + +## A hash of tweens currently active for animated items +var _tweens: Dictionary + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_INT], + [null, null, -1] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_error( + self, + "Invalid first object. Object with global id '%s' not found." % arguments[0] + ) + return false + if not escoria.object_manager.has(arguments[1]): + raise_error( + self, + "Invalid second object. Object with global id '%s' not found." % arguments[1] + ) + return false + return true + + +## Slide the object by generating a tween[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |source|`ESCObject`|The item to slide|yes|[br] +## |destination|`ESCObject`|The destination item to slide to|yes|[br] +## |speed|`int`|The speed at which to slide in pixels per second (will default to the speed configured on the `object`)|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the generated (and started) tween. (`Tween3`) +func _slide_object( + source: ESCObject, + destination: ESCObject, + speed: int = -1 +) -> Tween3: + if speed == -1: + speed = source.node.speed + var tween: Tween3 + if _tweens.has(source.global_id): + tween = (_tweens.get(source.global_id) as Tween3) + tween.stop_all() + else: + tween = Tween3.new(source.node) + tween.finished.connect(_on_tween_completed) + + var duration = source.node.position.distance_to( + destination.node.position + ) / speed + + tween.interpolate_property( + source.node, + "global_position", + source.node.global_position, + destination.node.global_position, + duration + ) + + tween.play() + _tweens[source.global_id] = tween + return tween + + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + _slide_object( + escoria.object_manager.get_object(command_params[0]), + escoria.object_manager.get_object(command_params[1]), + command_params[2] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + for tween in _tweens.values(): + tween.stop_all() + +## Function called when a tween completes.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |tween|`Tween3`|The tween that completed.|yes|[br] +## |_key|`NodePath`|The key of the tween in the `_tweens` dictionary (not used here).|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_tween_completed(tween: Tween3, _key: NodePath): + if tween: + tween.queue_free() diff --git a/addons/escoria-core/game/core-scripts/esc/commands/slide.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/slide.gd.uid new file mode 100644 index 0000000..30ebe49 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/slide.gd.uid @@ -0,0 +1 @@ +uid://c01flwxcnuerv diff --git a/addons/escoria-core/game/core-scripts/esc/commands/slide_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/slide_block.gd new file mode 100644 index 0000000..59fa186 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/slide_block.gd @@ -0,0 +1,51 @@ +## `slide_block(object: String, target: String[, speed: Integer])` +## +## Moves `object` towards the position of `target`. This command is +## blocking.[br] +##[br] +## - *object*: Global ID of the object to move[br] +## - *target*: Global ID of the target object[br] +## - *speed*: The speed at which to slide in pixels per second (will default to +## the speed configured on the `object`)[br] +##[br] +## **Warning** This command does not respect the room's navigation polygons, so +## `object` can be moved even when outside walkable areas. +## +## @ASHES +## @COMMAND +extends SlideCommand +class_name SlideBlockCommand + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var tween = _slide_object( + escoria.object_manager.get_object(command_params[0]), + escoria.object_manager.get_object(command_params[1]), + command_params[2] + ) + await tween.finished + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + super.interrupt() diff --git a/addons/escoria-core/game/core-scripts/esc/commands/slide_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/slide_block.gd.uid new file mode 100644 index 0000000..92d3bd7 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/slide_block.gd.uid @@ -0,0 +1 @@ +uid://h5sb6pic1q7k diff --git a/addons/escoria-core/game/core-scripts/esc/commands/spawn.gd b/addons/escoria-core/game/core-scripts/esc/commands/spawn.gd new file mode 100644 index 0000000..b0f01ba --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/spawn.gd @@ -0,0 +1,126 @@ +## `spawn(identifier: String, path: String[, is_active: Boolean[, position_target: String]])` +## +## Programmatically adds a new item to the scene.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |identifier|`String`|Global ID to use for the new object|yes|[br] +## |path|`String`|Path to the scene file of the object|yes|[br] +## |is_active|`Boolean`|Whether the new object should be set to active (default: `true`)|no|[br] +## |position_target|`String`|Global ID of another object that will be used to position the new object (when omitted, the new object's position is not specified)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name SpawnCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_BOOL, TYPE_STRING], + [null, null, true, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if arguments[0].is_empty() \ + or arguments[0] in escoria.object_manager.RESERVED_OBJECTS: + raise_error( + self, + "global_id (%s) is invalid. The global_id was either empty or is reserved." % arguments[0] + ) + return false + if not ResourceLoader.exists(arguments[1]): + raise_error( + self, + "Invalid scene path: '%s' not found." % arguments[1] + ) + return false + if arguments[3] and not escoria.object_manager.has(arguments[3]): + raise_invalid_object_error(self, arguments[3]) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var res_scene = escoria.resource_cache.get_resource(command_params[1]) + + # Load room scene + var scene = res_scene.instantiate() + if scene: + escoria.main.get_node("/root").add_child(scene) + if command_params[3]: + var obj = escoria.object_manager.get_object(command_params[3]) + scene.set_position(obj.get_global_position()) + escoria.inputs_manager.hotspot_focused = "" + + escoria.object_manager.register_object( + ESCObject.new( + command_params[0], + scene + ), + null, + true + ) + + escoria.object_manager.get_object(command_params[0]).active = \ + command_params[2] + + else: + raise_error( + self, + "Invalid scene. Failed to load scene '%s'." % command_params[1] + ) + + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/spawn.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/spawn.gd.uid new file mode 100644 index 0000000..b6d31d4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/spawn.gd.uid @@ -0,0 +1 @@ +uid://ck8lwe1gbiltb diff --git a/addons/escoria-core/game/core-scripts/esc/commands/stop.gd b/addons/escoria-core/game/core-scripts/esc/commands/stop.gd new file mode 100644 index 0000000..b53183f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/stop.gd @@ -0,0 +1,58 @@ +## `stop()` +## +## Stops the current event's execution. Note that this will stop the current script entirely - if you're within a conditional block, the code after the conditional block will not be executed.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name StopCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 0, + [], + [] + ) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + return ESCExecution.RC_CANCEL + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/stop.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/stop.gd.uid new file mode 100644 index 0000000..046a0d2 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/stop.gd.uid @@ -0,0 +1 @@ +uid://cs6slsqr30loi diff --git a/addons/escoria-core/game/core-scripts/esc/commands/stop_snd.gd b/addons/escoria-core/game/core-scripts/esc/commands/stop_snd.gd new file mode 100644 index 0000000..ae0fc62 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/stop_snd.gd @@ -0,0 +1,113 @@ +## `stop_snd([audio_bus: String])` +## +## Stops the given audio bus's stream. By default there are 4 audio buses set up by Escoria : `_sound`, which is used to play non-looping sound effects; `_music`, which plays looping music; `_ambient`, which plays looping background sounds; and `_speech`, which plays non-looping voice files (default: `_music`). Each simultaneous sound (e.g. multiple game sound effects) will require its own bus. To create additional buses, see the Godot sound documentation : [Audio buses](https://docs.godotengine.org/en/stable/tutorials/audio/audio_buses.html#doc-audio-buses)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |audio_bus|`String`|Bus to stop ("_sound", "_music", "_speech", "_ambient", or a custom audio bus you have created.)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name StopSndCommand + + +## The specified sound player +var _snd_player: String + +## The previous sound state, saved for interrupting +var previous_snd_state: String + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 0, + [TYPE_STRING], + ["_music"] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_error( + self, + "Invalid sound player. Sound player '%s' not registered." % arguments[0] + ) + return false + _snd_player = arguments[0] + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var sound_player = escoria.object_manager.get_object(command_params[0]).node + if sound_player: + if sound_player.get("state"): + previous_snd_state = sound_player.state + sound_player.set_state("off") + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + var _sound_players = [] + if previous_snd_state.is_empty(): + previous_snd_state = "off" + + if _snd_player.is_empty(): + _sound_players = [ + ESCObjectManager.MUSIC, + ESCObjectManager.SOUND, + ESCObjectManager.SPEECH + ] + else: + _sound_players = [_snd_player] + + for snd_player in _sound_players: + if escoria.object_manager.get_object(snd_player).node: + escoria.object_manager.get_object(snd_player).node.set_state( + previous_snd_state + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/stop_snd.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/stop_snd.gd.uid new file mode 100644 index 0000000..a5ec306 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/stop_snd.gd.uid @@ -0,0 +1 @@ +uid://chuw1y4flg6i2 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/teleport.gd b/addons/escoria-core/game/core-scripts/esc/commands/teleport.gd new file mode 100644 index 0000000..ae41393 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/teleport.gd @@ -0,0 +1,103 @@ +## `teleport(object: String, target: String)` +## +## Instantly moves an object to a new position.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to move|yes|[br] +## |target|`String`|Global ID of the object to use as the destination coordinates for `object`|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name TeleportCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING], + [null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_error( + self, + "Invalid first object. Object to teleport with global id '%s' not found." % arguments[0] + ) + return false + + if not (escoria.object_manager.get_object(arguments[0]).node as ESCItem): + raise_error( + self, + "Invalid first object. Object to teleport with global id '%s' must be of or derived from type ESCItem." % arguments[0] + ) + return false + + if not escoria.object_manager.has(arguments[1]): + raise_error( + self, + "Invalid second object. Destination location to teleport to with global id '%s' not found." % arguments[1] + ) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(command_params[0]).node as ESCItem) \ + .teleport( + escoria.object_manager.get_object(command_params[1]).node + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/teleport.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/teleport.gd.uid new file mode 100644 index 0000000..862e634 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/teleport.gd.uid @@ -0,0 +1 @@ +uid://bkb5q0ummfrux diff --git a/addons/escoria-core/game/core-scripts/esc/commands/teleport_pos.gd b/addons/escoria-core/game/core-scripts/esc/commands/teleport_pos.gd new file mode 100644 index 0000000..bf7a4fe --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/teleport_pos.gd @@ -0,0 +1,99 @@ +## `teleport_pos(object: String, x: Integer, y: Integer)` +## +## Instantly moves an object to the specified (absolute) coordinates.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to move|yes|[br] +## |x|`Integer`|X-coordinate of destination position|yes|[br] +## |y|`Integer`|Y-coordinate of destination position|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name TeleportPosCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 3, + [TYPE_STRING, TYPE_INT, TYPE_INT], + [null, null, null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_error( + self, + "Invalid first object. Object to teleport with global id '%s' not found." % arguments[0] + ) + return false + + if not (escoria.object_manager.get_object(arguments[0]).node as ESCItem): + raise_error( + self, + "Invalid first object. Object to teleport with global id '%s' must be of or derived from type ESCItem." % arguments[0] + ) + return false + + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(command_params[0]).node as ESCItem) \ + .teleport_to( + Vector2(int(command_params[1]), int(command_params[2]) + ) + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/teleport_pos.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/teleport_pos.gd.uid new file mode 100644 index 0000000..09cf2c9 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/teleport_pos.gd.uid @@ -0,0 +1 @@ +uid://ck4dtiyd2w8h8 diff --git a/addons/escoria-core/game/core-scripts/esc/commands/transition.gd b/addons/escoria-core/game/core-scripts/esc/commands/transition.gd new file mode 100644 index 0000000..ff2c562 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/transition.gd @@ -0,0 +1,119 @@ +## `transition(transition_name: String, mode: String[, delay: Number])` +## +## Runs a transition effect - generally used when entering or leaving a room. Transitions are implemented as Godot shaders. Custom transitions can be made by creating a shader in the `game/scenes/transitions/shaders/` folder within the escoria-core plugin folder.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |transition_name|`String`|Name of the transition shader from one of the transition directories|yes|[br] +## |mode|`String`|Set to `in` to transition into or `out` to transition out of the room|yes|[br] +## |delay|`Number`|Delay in seconds before starting the transition (default: `1.0`)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name TransitionCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_FLOAT], + [null, null, 1.0] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.main.scene_transition.has_transition(arguments[0]) \ + and not arguments[0].is_empty(): + raise_error( + self, + "Argument invalid. Transition with name '%s' doesn't exist." % arguments[0] + ) + return false + if not arguments[1] in ["in", "out"]: + raise_error( + self, + "Argument invalid. Transition type 'in' or 'out' expected, but '%s' was provided." % arguments[1] + ) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + var transition_id = escoria.main.scene_transition.transition( + command_params[0], + ESCTransitionPlayer.TRANSITION_MODE.OUT if command_params[1] == "out" \ + else ESCTransitionPlayer.TRANSITION_MODE.IN, + command_params[2] + ) + + if transition_id == ESCTransitionPlayer.TRANSITION_ID_INSTANT: + escoria.logger.debug( + self, + "Performing instant transition." + ) + escoria.main.scene_transition.reset_shader_cutoff() + return ESCExecution.RC_OK + + escoria.logger.debug( + self, + "Starting transition #%s [%s, %s]." + % [transition_id, command_params[0], command_params[1]] + ) + while await escoria.main.scene_transition.transition_done != transition_id: + pass + escoria.logger.debug( + self, + "Ending transition #%s [%s, %s]." + % [transition_id, command_params[0], command_params[1]]) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + # Do nothing + pass diff --git a/addons/escoria-core/game/core-scripts/esc/commands/transition.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/transition.gd.uid new file mode 100644 index 0000000..2a19946 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/transition.gd.uid @@ -0,0 +1 @@ +uid://ddycq6hdgbn7w diff --git a/addons/escoria-core/game/core-scripts/esc/commands/turn_to.gd b/addons/escoria-core/game/core-scripts/esc/commands/turn_to.gd new file mode 100644 index 0000000..6cf6fc4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/turn_to.gd @@ -0,0 +1,97 @@ +## `turn_to(object: String, object_to_face: String[, wait: Number])` +## +## Turns `object` to face another object. Unlike movement commands, `turn_to` will not automatically reference an `ESCLocation` that is a child of an `ESCItem.` To turn towards an `ESCLocation` that is a child of an `ESCItem`, give the `ESCLocation` a `Global ID` and use this value as the `object_to_face` parameter. While turning, the number of directions the item faces will depend on the number of `directions` defined for the object. A 16 direction character for example will display 8 directions of animation while turning to face an object that is 180 degrees away, a 4 direction character would only face 2 directions to make the same turn. As the idle animation will be played for `wait` seconds for each direction the object faces, a 16 direction character would take 8 seconds to rotate 180 degrees with a 1 second `wait` time, whereas a 4 direction character would only take 2 seconds to make the same rotation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to be turned|yes|[br] +## |object_to_face|`String`|Global ID of the object to turn towards|yes|[br] +## |wait|`Number`|Length of time to wait in seconds for each intermediate angle. If set to 0, the turnaround is immediate (default: `0`)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name TurnToCommand + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_FLOAT], + [null, null, 0.0] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_invalid_object_error(self, arguments[0]) + return false + if not escoria.object_manager.has(arguments[1]): + raise_error( + self, + "Cannot turn \"%s\" towards \"%s\". \"%s\" was not found." + % [arguments[0], arguments[1] , arguments[1]] + ) + return false + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + (escoria.object_manager.get_object(command_params[0]).node as ESCItem)\ + .turn_to( + escoria.object_manager.get_object(command_params[1]).node, + command_params[2] + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "[%s] interrupt() function not implemented." % get_command_name() + ) diff --git a/addons/escoria-core/game/core-scripts/esc/commands/turn_to.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/turn_to.gd.uid new file mode 100644 index 0000000..b939702 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/turn_to.gd.uid @@ -0,0 +1 @@ +uid://be3fg536tk4to diff --git a/addons/escoria-core/game/core-scripts/esc/commands/wait.gd b/addons/escoria-core/game/core-scripts/esc/commands/wait.gd new file mode 100644 index 0000000..1775452 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/wait.gd @@ -0,0 +1,96 @@ +## `wait(seconds: Number)` +## +## Blocks execution of the current event.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |seconds|`Number`|Number of seconds to block|yes|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name WaitCommand + +## Timer to wait for +var timer: Timer + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 1, + [[TYPE_INT, TYPE_FLOAT]], + [null] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + # We can't wait for 0 or fewer seconds, now, can we? + if arguments[0] <= 0.0: + raise_error( + self, + "Argument invalid. %s is an invalid amount of time to wait (must be positive)." % arguments[0] + ) + return false + + return true + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + timer = Timer.new() + timer.wait_time = float(command_params[0]) + escoria.add_child(timer) + timer.start() + await timer.timeout + escoria.remove_child(timer) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + if timer == null: + return + + timer.timeout.emit() diff --git a/addons/escoria-core/game/core-scripts/esc/commands/wait.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/wait.gd.uid new file mode 100644 index 0000000..094689f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/wait.gd.uid @@ -0,0 +1 @@ +uid://cn0rhd2xiws7e diff --git a/addons/escoria-core/game/core-scripts/esc/commands/walk.gd b/addons/escoria-core/game/core-scripts/esc/commands/walk.gd new file mode 100644 index 0000000..735e199 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/walk.gd @@ -0,0 +1,108 @@ +## `walk(object: String, target: String[, walk_fast: Boolean])` +## +## Moves the specified `ESCPlayer` or movable `ESCItem` to the `target` ESCItem's location while playing `object`'s walking animation. This command is non-blocking. This command will use the normal walk speed by default. If the `target` `ESCItem` has a child `ESCLocation` node, the walk destination will be the position of the `ESCLocation`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to move|yes|[br] +## |target|`String`|Global ID of the target object|yes|[br] +## |walk_fast|`Boolean`|Whether to walk fast (`true`) or normal speed (`false`) (default: false)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name WalkCommand + + +## Walking object +var walking_object_node: ESCItem + +## Target object +var target_object_node: ESCObject + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_BOOL], + [null, null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_error( + self, + "Invalid first object. The object with global id '%s' to make walk was not found." % arguments[0] + ) + return false + if not escoria.object_manager.has(arguments[1]): + raise_error( + self, + "Invalid second object. The object to walk to with global id '%s' was not found." % arguments[1] + ) + return false + + walking_object_node = (escoria.object_manager.get_object( + arguments[0]).node as ESCItem + ) + target_object_node = escoria.object_manager.get_object(arguments[1]) + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.action_manager.do( + escoria.action_manager.ACTION.BACKGROUND_CLICK, + command_params + ) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + if walking_object_node != null: + walking_object_node.stop_walking_now() diff --git a/addons/escoria-core/game/core-scripts/esc/commands/walk.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/walk.gd.uid new file mode 100644 index 0000000..e986ded --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/walk.gd.uid @@ -0,0 +1 @@ +uid://cy07ftsppvqhd diff --git a/addons/escoria-core/game/core-scripts/esc/commands/walk_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/walk_block.gd new file mode 100644 index 0000000..d144a6d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/walk_block.gd @@ -0,0 +1,110 @@ +## `walk_block(object: String, target: String[, walk_fast: Boolean])` +## +## Moves the specified `ESCPlayer` or movable `ESCItem` to the `target` ESCItem's location while playing `object`'s walking animation. This command is blocking. This command will use the normal walk speed by default. If the `target` ESCItem has a child ESCLocation node, the walk destination will be the position of the ESCLocation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to move|yes|[br] +## |target|`String`|Global ID of the target object|yes|[br] +## |walk_fast|`Boolean`|Whether to walk fast (`true`) or normal speed (`false`). (default: false)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name WalkBlockCommand + + +## Walking object +var walking_object_node: ESCItem + +## Target object +var target_object_node: ESCObject + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 2, + [TYPE_STRING, TYPE_STRING, TYPE_BOOL], + [null, null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_error( + self, + "Invalid first object. The object to make walk with global id '%s' was not found." % arguments[0] + ) + return false + if not escoria.object_manager.has(arguments[1]): + raise_error( + self, + "Invalid second object. The object to walk to with global id '%s' was not found." % arguments[1] + ) + return false + + walking_object_node = (escoria.object_manager.get_object( + arguments[0]).node as ESCItem + ) + target_object_node = escoria.object_manager.get_object(arguments[1]) + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.action_manager.do( + escoria.action_manager.ACTION.BACKGROUND_CLICK, + command_params + ) + await walking_object_node.arrived + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + if walking_object_node != null and is_instance_valid(walking_object_node) \ + and not walking_object_node is ESCPlayer: + walking_object_node.stop_walking_now() diff --git a/addons/escoria-core/game/core-scripts/esc/commands/walk_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/walk_block.gd.uid new file mode 100644 index 0000000..9f4d5a9 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/walk_block.gd.uid @@ -0,0 +1 @@ +uid://b20bj83ango1f diff --git a/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos.gd b/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos.gd new file mode 100644 index 0000000..fcbe673 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos.gd @@ -0,0 +1,99 @@ +## `walk_to_pos(object: String, x: Integer, y: Integer[, walk_fast: Boolean])` +## +## Moves the specified `ESCPlayer` or movable `ESCItem` to the absolute coordinates provided while playing the `object`'s walking animation. This command is non-blocking. This command will use the normal walk speed by default.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to move|yes|[br] +## |x|`Integer`|X-coordinate of target position|yes|[br] +## |y|`Integer`|Y-coordinate of target position|yes|[br] +## |walk_fast|`Boolean`|Whether to walk fast (`true`) or normal speed (`false`). (default: false)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name WalkToPosCommand + + +## Walking object +var walking_object_node: ESCItem + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 3, + [TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_BOOL], + [null, null, null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_error( + self, + "Invalid first object. The object to make walk with global id '%s' was not found." % arguments[0] + ) + return false + + walking_object_node = (escoria.object_manager.get_object( + arguments[0]).node as ESCItem + ) + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.action_manager.do(escoria.action_manager.ACTION.BACKGROUND_CLICK, [ + command_params[0], + Vector2(command_params[1], command_params[2]), command_params[3] + ]) + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + if walking_object_node != null and not walking_object_node is ESCPlayer: + walking_object_node.stop_walking_now() diff --git a/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos.gd.uid new file mode 100644 index 0000000..676307d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos.gd.uid @@ -0,0 +1 @@ +uid://bqpocp17n77pp diff --git a/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos_block.gd b/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos_block.gd new file mode 100644 index 0000000..6800522 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos_block.gd @@ -0,0 +1,100 @@ +## `walk_to_pos_block(object: String, x: Integer, y: Integer[, walk_fast: Boolean])` +## +## Moves the specified `ESCPlayer` or movable `ESCItem` to the absolute coordinates provided while playing the `object`'s walking animation. This command is blocking. This command will use the normal walk speed by default.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`String`|Global ID of the object to move|yes|[br] +## |x|`Integer`|X-coordinate of target position|yes|[br] +## |y|`Integer`|Y-coordinate of target position|yes|[br] +## |walk_fast|`Boolean`|Whether to walk fast (`true`) or normal speed (`false`). (default: false)|no|[br] +## [br] +## @ASHES +## @COMMAND +extends ESCBaseCommand +class_name WalkToPosBlockCommand + + +## Walking object +var walking_object_node: ESCItem + + +## The descriptor of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the descriptor of the arguments of this command. The argument descriptor for this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + return ESCCommandArgumentDescriptor.new( + 3, + [TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_BOOL], + [null, null, null, false] + ) + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|The arguments to validate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the arguments are valid, false otherwise. (`bool`) +func validate(arguments: Array): + if not super.validate(arguments): + return false + + if not escoria.object_manager.has(arguments[0]): + raise_error( + self, + "Invalid first object. The object to make walk with global id '%s' was not found." % arguments[0] + ) + return false + + walking_object_node = (escoria.object_manager.get_object( + arguments[0]).node as ESCItem + ) + return true + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|The parameters for the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the execution result code. (`int`) +func run(command_params: Array) -> int: + escoria.action_manager.do(escoria.action_manager.ACTION.BACKGROUND_CLICK, [ + command_params[0], + Vector2(command_params[1], command_params[2]), command_params[3] + ]) + await (escoria.object_manager.get_object(command_params[0]).node as ESCItem).arrived + return ESCExecution.RC_OK + + +## Function called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + if walking_object_node != null and not walking_object_node is ESCPlayer: + walking_object_node.stop_walking_now() diff --git a/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos_block.gd.uid b/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos_block.gd.uid new file mode 100644 index 0000000..0a08396 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/commands/walk_to_pos_block.gd.uid @@ -0,0 +1 @@ +uid://bqmwgeclme60k diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd new file mode 100644 index 0000000..df14c0b --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd @@ -0,0 +1,49 @@ +## Used by the intpreter to track the depth of a `break` statement invoked in a dialog. +extends Object +class_name ESCBreakCounter + + +var _levels_left: int = 0: + set = set_levels_left, + get = get_levels_left + + +## Sets the depth from the root of the dialog tree, with `levels == 0` at the root.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |levels|`int`|Number of dialog nesting levels remaining (0 represents the root).|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_levels_left(levels: int) -> void: + _levels_left = levels + + +## Gets the depth from the root of the dialog tree, with `levels == 0` at the root.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func get_levels_left() -> int: + return _levels_left + + +## Decrements by one the depth from the root of the dialog tree, with `levels == 0` at the root.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func dec_levels_left() -> void: + _levels_left = _levels_left - 1 diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd.uid new file mode 100644 index 0000000..6046bb5 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd.uid @@ -0,0 +1 @@ +uid://cm8mkkhvj7cg0 diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd new file mode 100644 index 0000000..d63c52b --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd @@ -0,0 +1,221 @@ +## Class to track variables and their associated scopes within a script. +## +## Scoping is implemented using this class, including traditional scoping rules. +## Scope binding and resolution are handled in this class. +extends Object +class_name ESCEnvironment + + +var _enclosing: # ESCEnvironment + get = get_enclosing +var _values: Dictionary = {}: + get = get_values + + +## Initializes the scope and sets the scope which encloses this one.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |enclosing|`Variant`|Optional parent environment used to resolve lookups outside this scope.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func init(enclosing) -> void: + _enclosing = enclosing + + +func cleanup() -> void: + if is_instance_valid(_enclosing): + _enclosing.cleanup() + _enclosing = null + + _values.clear() + + +## Determines whether the specified key (i.e. the script variable's name) is valid either within this scope or an enclosing scope.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`ESCToken`|Token describing the variable name to look up.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns `true` if the variable exists in this scope or an enclosing scope, otherwise `false`. (`bool`) +func is_valid_key(name: ESCToken): + if _values.has(name.get_lexeme()): + return true + + if _enclosing: + return _enclosing.is_valid_key(name) + + return false + + +## Fetches the value of the specified key (i.e. the script variable) from this scope or an enclosing one. otherwise, an error is produced[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`ESCToken`|Token describing the variable name whose value is requested.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the value associated with the variable name, or produces an error if undefined. (`Variant`) +func get_value(name: ESCToken): + if _values.has(name.get_lexeme()): + return _values.get(name.get_lexeme()) + + if _enclosing: + return _enclosing.get_value(name) + + return _error(name, "Undefined variable '%s'." % name.get_lexeme()) + + +## Assigns a value to the specified key (i.e. the script variable) from this scope or an enclosing one.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`ESCToken`|Token describing the variable name to assign.|yes|[br] +## |value|`Variant`|the value to be assigned to the script variable|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing on success, or produces an error if the variable is undefined in all scopes. +func assign(name: ESCToken, value): + if _values.has(name.get_lexeme()): + _values[name.get_lexeme()] = value + return + + if _enclosing: + _enclosing.assign(name, value) + return + + return _error(name, "Undefined variable '%s'." % name.get_lexeme()) + + +## Defines a script variable in this scope and assigns it a value.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Variable name to register in this scope.|yes|[br] +## |value|`Variant`|the value to be assigned to the script variable|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func define(name: String, value) -> void: + _values[name] = value + + +## Fetches the scope (environment) that is `distance` levels up the enclosing scope chain.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |distance|`int`|The number of levels up the scope chain to traverse. `distance == 0` returns this scope (self); `distance == 1` returns the immediate enclosing scope; `distance == 2` returns the enclosing scope's enclosing scope, etc.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the environment (scope) at the specified distance up the enclosing scope chain. (`ESCEnvironment`) +func ancestor(distance: int): + var env = self + + for i in range(0, distance): + env = env.get_enclosing() + + return env + + +## Gets the value of the script variable (if it exists) that is at most `distance` levels above this one.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |distance|`int`|The number of scope levels to traverse; e.g. `distance == 0` is this scope, `distance == 1` is the enclosing scope, `distance == 2` is the enclosing scope's enclosing scope, etc.|yes|[br] +## |name|`String`|Variable name to resolve at the requested scope depth.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the value of the variable at the specified scope distance. (`Variant`) +func get_at(distance: int, name: String): + return ancestor(distance).get_values().get(name) + + +## Assigns the specified value to the script variable (if it exists) that is at most `distance` levels above this one.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |distance|`int`|the number of scope levels to traverse; e.g. `distance == 0` is this scope, `distance == 1` is the enclosing scope, `distance == 2` is the enclosing scope's enclosing scope, etc.|yes|[br] +## |name|`ESCToken`|Token describing the variable name to modify at the requested scope depth.|yes|[br] +## |value|`Variant`|the value to assign to the script variable|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. (`void`) +func assign_at(distance: int, name: ESCToken, value): + ancestor(distance).get_values()[name.get_lexeme()] = value + + +func _to_string(): + var result: String = JSON.stringify(_values) + + if _enclosing: + result += " -> %s" % JSON.stringify(_enclosing._to_string()) + + return result + + +## The enclosing scope (environment) for this scope.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the enclosing scope (environment) for this scope. (`Variant`) +func get_enclosing(): + return _enclosing + + +## The dictionary storing all script variables and their associated values located at this scope's level.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the dictionary storing all script variables and their associated values located at this scope's level. (`Dictionary`) +func get_values(): + return _values + + +func _error(token: ESCToken, message: String): + var source: String = token.get_filename() if not token.get_filename().is_empty() else token.get_source() + ESCSafeLogging.log_error( + self, + "%s: Line %s at '%s': %s" % [source, token.get_line(), token.get_lexeme(), message] + ) + + +func _warn(token: ESCToken, message: String): + var source: String = token.get_filename() if not token.get_filename().is_empty() else token.get_source() + escoria.logger.warn( + self, + "%s: Line %s at '%s': %s" % [source, token.get_line(), token.get_lexeme(), message] + ) diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd.uid new file mode 100644 index 0000000..66f565f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd.uid @@ -0,0 +1 @@ +uid://bvftibfiu6pdy diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd new file mode 100644 index 0000000..0e8bda8 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd @@ -0,0 +1,22 @@ +## Base class that expressions must inherit from as part of the "Visitor" pattern. +## +## All inheriting classes must implement an `accept` method to invoke the visitor. +## For more information on the "Visitor" pattern, see: +## (Visitor Pattern)[https://en.wikipedia.org/wiki/Visitor_pattern] +extends RefCounted +class_name ESCGrammarExpr + + +## Method that invokes another method in the visitor against this, the implementing class.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |visitor|`Variant`|Visitor instance that exposes a method for handling this expression.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func accept(visitor): + pass diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd.uid new file mode 100644 index 0000000..a5a5342 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd.uid @@ -0,0 +1 @@ +uid://c6u250aptuhvf diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd new file mode 100644 index 0000000..32d8914 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd @@ -0,0 +1,724 @@ +## Implements all expressions in the scripting language's grammar. These classes +## represent language constructs as part of a script's syntax tree. +class_name ESCGrammarExprs + + +## Represents a logical expression. The left-hand side (LHS) is evaluated against +## the right-hand side (RHS) using a predicate with a determination of truth +## being returned. Both sides must themselves evaluate to a boolean. +## Examples include AND, OR. +class Logical extends ESCGrammarExpr: + var _left: ESCGrammarExpr: + get = get_left + var _operator: ESCToken + var _right: ESCGrammarExpr: + get = get_right + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |left|`ESCGrammarExpr`|Left-hand operand expression.|yes|[br] + ## |operator|`ESCToken`|Token representing the operator applied to the operands.|yes|[br] + ## |right|`ESCGrammarExpr`|Right-hand operand expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(left: ESCGrammarExpr, operator: ESCToken, right: ESCGrammarExpr): + _left = left + _operator = operator + _right = right + + + ## The LHS of the logical expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the LHS of the logical expression. (`ESCGrammarExpr`) + func get_left() -> ESCGrammarExpr: + return _left + + + ## The RHS of the logical expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the RHS of the logical expression. (`ESCGrammarExpr`) + func get_right() -> ESCGrammarExpr: + return _right + + + ## The operator (predicate) of the logical expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the operator (predicate) of the logical expression. (`ESCToken`) + func get_operator() -> ESCToken: + return _operator + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return visitor.visit_logical_expr(self) + + +## Represents a binary expression; that is, an expression that requires two operands +## (a left-hand side, LHS, and a right-hand side, RHS) and an operator that takes +## both operands as input. Examples include addition, subtraction. +class Binary extends ESCGrammarExpr: + var _left: ESCGrammarExpr: + get = get_left + var _operator: ESCToken + var _right: ESCGrammarExpr: + get = get_right + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |left|`ESCGrammarExpr`|Left-hand operand expression.|yes|[br] + ## |operator|`ESCToken`|Token representing the operator applied to the operands.|yes|[br] + ## |right|`ESCGrammarExpr`|Right-hand operand expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(left: ESCGrammarExpr, operator: ESCToken, right: ESCGrammarExpr): + _left = left + _operator = operator + _right = right + + + ## The LHS of the binary expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the LHS of the binary expression. (`ESCGrammarExpr`) + func get_left() -> ESCGrammarExpr: + return _left + + + ## The RHS of the binary expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the RHS of the binary expression. (`ESCGrammarExpr`) + func get_right() -> ESCGrammarExpr: + return _right + + + ## The operator the binary expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the operator the binary expression. (`ESCToken`) + func get_operator() -> ESCToken: + return _operator + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this binary expression. (`Variant`) + func accept(visitor): + return visitor.visit_binary_expr(self) + + +## Represents a unary expression; that is, an expression that requires only one operand +## and an operator that takes the operand as input. Examples include NOT, negative numbers. +class Unary extends ESCGrammarExpr: + var _operator: ESCToken: + get = get_operator + var _right: ESCGrammarExpr: + get = get_right + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |operator|`ESCToken`|Token representing the operator applied to the operands.|yes|[br] + ## |right|`ESCGrammarExpr`|Right-hand operand expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(operator: ESCToken, right: ESCGrammarExpr): + _operator = operator + _right = right + + + ## The sole operand of the unary expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the sole operand of the unary expression. (`ESCGrammarExpr`) + func get_right() -> ESCGrammarExpr: + return _right + + + ## The operator the unary expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the operator the unary expression. (`ESCToken`) + func get_operator() -> ESCToken: + return _operator + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this unary expression. (`Variant`) + func accept(visitor): + return visitor.visit_unary_expr(self) + + +## Not currently used. +class Get extends ESCGrammarExpr: + var _object: ESCGrammarExpr: + get = get_object + var _name: ESCToken: + get = get_name + + + func init(object: ESCGrammarExpr, name: ESCToken): + _object = object + _name = name + + + func accept(visitor): + return visitor.visit_get_expr(self) + + + func get_object() -> ESCGrammarExpr: + return _object + + + func get_name() -> ESCToken: + return _name + + +## Not currently used. +class Set extends ESCGrammarExpr: + var _object: ESCGrammarExpr: + get = get_object + var _name: ESCToken: + get = get_name + var _value: ESCGrammarExpr: + get = get_value + + + func init(object: ESCGrammarExpr, name: ESCToken, value: ESCGrammarExpr): + _object = object + _name = name + _value = value + + + func get_object() -> ESCGrammarExpr: + return _object + + + func get_name() -> ESCToken: + return _name + + + func get_value() -> ESCGrammarExpr: + return _value + + + func accept(visitor): + return visitor.visit_set_expr(self) + + +## Represents a function call. +class Call extends ESCGrammarExpr: + var _callee: ESCGrammarExpr: + get = get_callee + var _paren: ESCToken: + get = get_paren_token + var _arguments: Array: + get = get_arguments + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |callee|`ESCGrammarExpr`|the expression representing the function to be called|yes|[br] + ## |paren|`ESCToken`|token containing debug information for feedback purposes|yes|[br] + ## |arguments|`Array`|array containing arguments to be passed to the callee|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(callee: ESCGrammarExpr, paren: ESCToken, arguments: Array): + _callee = callee + _paren = paren + _arguments = arguments + + + ## The callee expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the callee expression. (`ESCGrammarExpr`) + func get_callee() -> ESCGrammarExpr: + return _callee + + + ## The arguments array.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the arguments array. (`Array`) + func get_arguments() -> Array: + return _arguments + + + ## The paren (debug) token.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the paren (debug) token. (`ESCToken`) + func get_paren_token() -> ESCToken: + return _paren + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this call expression. (`Variant`) + func accept(visitor): + return await visitor.visit_call_expr(self) + + +## Represents a literal value in the script. +class Literal extends ESCGrammarExpr: + var _value: + get = get_value + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |value|`Variant`|the value of the literal|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(value): + _value = value + + + ## The literal's value.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the literal's value. (`Variant`) + func get_value(): + return _value + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this literal expression. (`Variant`) + func accept(visitor): + return await visitor.visit_literal_expr(self) + + +## Represents a variable in the script. +class Variable extends ESCGrammarExpr: + var _name: ESCToken + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |name|`ESCToken`|Token representing the variable's name.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(name: ESCToken): + _name = name + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this variable expression. (`Variant`) + func accept(visitor): + return await visitor.visit_variable_expr(self) + + + ## The variable's name (as an `ESCToken`).[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the variable's name (as an `ESCToken`). (`ESCToken`) + func get_name() -> ESCToken: + return _name + + +## Represents an assignment expression. For example, assigning a value to a +## variable. +class Assign extends ESCGrammarExpr: + var _name: ESCToken: + get = get_name + var _value: ESCGrammarExpr: + get = get_value + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |name|`ESCToken`|Token representing the variable's name to assign.|yes|[br] + ## |value|`ESCGrammarExpr`|the value to assign to the variable; must be an expression itself|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(name: ESCToken, value: ESCGrammarExpr): + _name = name + _value = value + + + ## The variables name (as an `ESCToken`).[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the variables name (as an `ESCToken`). (`ESCToken`) + func get_name() -> ESCToken: + return _name + + + ## The value (as an expression) to be assigned to the variable.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the value (as an expression) to be assigned to the variable. (`ESCGrammarExpr`) + func get_value() -> ESCGrammarExpr: + return _value + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this assignment expression. (`Variant`) + func accept(visitor): + return await visitor.visit_assign_expr(self) + + +## Represents an expression surrounded by parentheses, imparting a higher precedence. +## For example, `(1 + 1) * 2`. +class Grouping extends ESCGrammarExpr: + var _expression: ESCGrammarExpr: + get = get_expression + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |expression|`ESCGrammarExpr`|the expression contained inside the parentheses|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(expression: ESCGrammarExpr): + _expression = expression + + + ## The grouped expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the grouped expression. (`ESCGrammarExpr`) + func get_expression() -> ESCGrammarExpr: + return _expression + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this grouping expression. (`Variant`) + func accept(visitor): + return await visitor.visit_grouping_expr(self) + + +## Represents the "in" operator used to check whether the specified identifier +## currently exists in the user's inventory. +class InInventory extends ESCGrammarExpr: + var _identifer: ESCGrammarExpr + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |identifier|`ESCGrammarExpr`|Expression that resolves to the identifier being modified.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(identifier: ESCGrammarExpr): + _identifer = identifier + + + ## The identifier to be checked against the user's inventory.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the identifier to be checked against the user's inventory. (`ESCGrammarExpr`) + func get_identifier() -> ESCGrammarExpr: + return _identifer + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this inventory check expression. (`Variant`) + func accept(visitor): + return await visitor.visit_in_inventory_expr(self) + + +## Represents the "is" operator; used to check whether the object corresponding +## to the given identifier is 'active' or in an otherwise-specified state. If the +## identifier is a string literal, the corresponding object must be in the user's +## inventory. +class Is extends ESCGrammarExpr: + var _identifer: ESCGrammarExpr + var _state: ESCGrammarExpr + var _descriptor: ESCToken + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |identifier|`ESCGrammarExpr`|the expression representing the identifier of the object to examine|yes|[br] + ## |state|`ESCGrammarExpr`|the expression to evaluate that is used when checking the state of the object represented by `identifier`|yes|[br] + ## |descriptor|`ESCToken`|not currently used|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(identifier: ESCGrammarExpr, state: ESCGrammarExpr, descriptor: ESCToken): + _identifer = identifier + _state = state + _descriptor = descriptor + + + ## The identifier expression of the object to be examined.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the identifier expression of the object to be examined. (`ESCGrammarExpr`) + func get_identifier() -> ESCGrammarExpr: + return _identifer + + + ## The state expression to be checked against the object corresponding to `identifier`[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the state expression to be checked against the object corresponding to `identifier`. (`ESCGrammarExpr`) + func get_state() -> ESCGrammarExpr: + return _state + + + ## Not currently used.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns a `ESCToken` value. (`ESCToken`) + func get_descriptor() -> ESCToken: + return _descriptor + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this expression.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the result of visiting this state check expression. (`Variant`) + func accept(visitor): + return await visitor.visit_is_expr(self) diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd.uid new file mode 100644 index 0000000..16a9d25 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd.uid @@ -0,0 +1 @@ +uid://b16h8iy1ctfww diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd new file mode 100644 index 0000000..1793f0e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd @@ -0,0 +1,22 @@ +## Base class that statements must inherit from as part of the "Visitor" pattern. +## +## All inheriting classes must implement an `accept` method to invoke the visitor. +## For more information on the "Visitor" pattern, see: +## (Visitor Pattern)[https://en.wikipedia.org/wiki/Visitor_pattern] +extends RefCounted +class_name ESCGrammarStmt + + +## Method that invokes another method in the visitor against this, the implementing class.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func accept(visitor): + pass diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd.uid new file mode 100644 index 0000000..c452ecd --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd.uid @@ -0,0 +1 @@ +uid://dra4amn06kp31 diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd new file mode 100644 index 0000000..2e9c575 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd @@ -0,0 +1,1017 @@ +## Implements all statements in the scripting language's grammar. These classes +## represent language constructs as part of a script's syntax tree. +class_name ESCGrammarStmts + + +## Represents a local (non-global) variable declaration and a possible initializer. +class Var extends ESCGrammarStmt: + var _name: ESCToken: + get = get_name + var _initializer: ESCGrammarExpr: + get = get_initializer + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |name|`ESCToken`|Token representing the variable's name.|yes|[br] + ## |initializer|`ESCGrammarExpr`|Expression used to compute the initial value.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(name: ESCToken, initializer: ESCGrammarExpr): + _name = name + _initializer = initializer + + + ## The variable's name (as an `ESCToken`).[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the variable's name (as an `ESCToken`). (`ESCToken`) + func get_name() -> ESCToken: + return _name + + + ## The initalizer expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the initalizer expression. (`ESCGrammarExpr`) + func get_initializer() -> ESCGrammarExpr: + return _initializer + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_var_stmt(self) + + +## Represents a global variable declaration and a possible initializer. +class Global extends ESCGrammarStmt: + var _name: ESCToken: + get = get_name + var _initializer: ESCGrammarExpr: + get = get_initializer + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |name|`ESCToken`|Token representing the variable's name.|yes|[br] + ## |initializer|`ESCGrammarExpr`|Expression used to compute the initial value.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(name: ESCToken, initializer: ESCGrammarExpr): + _name = name + _initializer = initializer + + + ## The global variable's name (as an `ESCToken`).[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the global variable's name (as an `ESCToken`). (`ESCToken`) + func get_name() -> ESCToken: + return _name + + + ## The initalizer expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the initalizer expression. (`ESCGrammarExpr`) + func get_initializer() -> ESCGrammarExpr: + return _initializer + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_global_stmt(self) + + +## Represents an Escoria event, e.g. `:look`. An event can also be given a "target" +## to act on, e.g. `:use "wrench"`. +class Event extends ESCGrammarStmt: + ## Signal fired when the event has finished.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + signal finished + + ## Signal fired if the event has been interrupted.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + signal interrupted + + var _name: ESCToken: + get = get_name + var _target: ESCGrammarExprs.Literal: + get = get_target + var _flags: int: + get = get_flags + var _flags_with_conditions: Dictionary = {}: + get = get_flags_with_conditions + var _body: ESCGrammarStmts.Block: + get = get_body + var _object_global_id: String: # This may be empty, e.g. if the event is attached to a room. + get = get_object_global_id + + var _running_command: + set = set_running_command, + get = get_running_command + + # Indicates whether this event was interrupted. + var _is_interrupted: bool = false: + get = is_interrupted + + var source: String = "" # TODO: Make proper use of this + + + ## Valid event flags[br] + ##[br] + ## * TK: stands for "telekinetic". It means the player won't walk over to + ## the item to say the line.[br] + ## * NO_TT: stands for "No tooltip". It hides the tooltip for the duration of + ## the event. Probably not very useful, because events having multiple + ## say commands in them are automatically hidden.[br] + ## * NO_UI: stands for "No User Inteface". It hides the UI for the duration of + ##  the event. Useful when you want something to look like a cut scene but not + ## disable input for skipping dialog.[br] + ## * NO_SAVE: disables saving. Use this in cut scenes and anywhere a + ## badly-timed autosave would leave your game in a messed-up state. + enum { + FLAG_TK = 1, + FLAG_NO_TT = 2, + FLAG_NO_UI = 4, + FLAG_NO_SAVE = 8 + } + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |name|`ESCToken`|Token representing the event name.|yes|[br] + ## |target|`ESCGrammarExprs.Literal`|a literal representing the global ID of an object the event is meant to act on; can be null.|yes|[br] + ## |flags|`Dictionary`|an array containing event flags to be applied; can be null/empty.|yes|[br] + ## |body|`ESCGrammarStmts.Block`|the body of the event; this is the script block that will be executed when the event is run.|yes|[br] + ## |object_global_id|`String`|the object/room the event is attached to, if any (may be empty)|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(name: ESCToken, target: ESCGrammarExprs.Literal, flags: Dictionary, body: ESCGrammarStmts.Block, object_global_id: String): + _name = name + _target = target + + for flag in flags.keys(): + match flag: + "TK": + _flags |= FLAG_TK + "NO_TT": + _flags |= FLAG_NO_TT + "NO_UI": + _flags |= FLAG_NO_UI + "NO_SAVE": + _flags |= FLAG_NO_SAVE + + _body = body + _flags_with_conditions = flags + _object_global_id = object_global_id + + + ## The event's name (as an `ESCToken`).[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the event's name (as an `ESCToken`). (`ESCToken`) + func get_name() -> ESCToken: + return _name + + + ## The target as a literal.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the target as a literal. (`ESCGrammarExprs.Literal`) + func get_target() -> ESCGrammarExprs.Literal: + return _target + + + ## A string containing the name of the target, if it exists.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns a string containing the name of the target, if it exists. (`String`) + func get_target_name() -> String: + return _target.get_value() if _target != null else "" + + + ## The name of the event as a string.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the name of the event as a string. (`String`) + func get_event_name() -> String: + return _name.get_lexeme() + + + ## The flags set for this event. Note that this is an integer that serves as a collection of mutually exclusive bits.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the flags set for this event. Note that this is an integer that serves as a collection of mutually exclusive bits. (`int`) + func get_flags() -> int: + return _flags + + + func get_flags_with_conditions() -> Dictionary: + return _flags_with_conditions + + + func add_flag(flag: ESCEvent.FLAGS): + _flags |= flag + + + ## The body of the event. This is the script block that will be executed when the event is run.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the body of the event. This is the script block that will be executed when the event is run. (`ESCGrammarStmts.Block`) + func get_body() -> ESCGrammarStmts.Block: + return _body + + + func get_object_global_id() -> String: + return _object_global_id + + + ## The number of top-level statements in the body. Generally only useful for internal purposes.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the number of top-level statements in the body. Generally only useful for internal purposes. (`int`) + func get_num_statements_in_block() -> int: + return _body.get_statements().size() + + + ## The command currently being executed in this event.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the command currently being executed in this event. (`Variant`) + func get_running_command(): + return _running_command + + + ## Sets the command that is currently being executed in this event.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |cmd|`Variant`|Command currently being executed for this event.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func set_running_command(cmd) -> void: + _running_command = cmd + + + ## Clears the currently-running command.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func clear_running_command() -> void: + _running_command = null + + + ## Forces the event to be interrupted.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func interrupt() -> void: + _is_interrupted = true + + if _running_command: + _running_command.interrupt() + + + ## True iff the event has been interrupted.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns true iff the event has been interrupted. (`bool`) + func is_interrupted() -> bool: + return _is_interrupted + + + ## Resets the "is interrupted" flag.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func reset_interrupt() -> void: + _is_interrupted = false + + + ## Emits the `finished` signal along with the indicated return code (`rc`).[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |rc|`int`|Return code emitted when the statement signals completion.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func emit_finished(rc: int): + emit_signal("finished", self, null, rc) + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_event_stmt(self) + + +## Represents a block of statements in a script. +class Block extends ESCGrammarStmt: + var _statements: Array: + get = get_statements + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |statements|`Array`|Statements contained within the block body.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(statements: Array): + _statements = statements + + + ## The statements contained in this block as an array.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the statements contained in this block as an array. (`Array`) + func get_statements() -> Array: + return _statements + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_block_stmt(self) + + +## Represents an expression to be evaluated that is contained within a statement. +class ESCExpression extends ESCGrammarStmt: + var _expression: ESCGrammarExpr: + get = get_expression + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |expression|`ESCGrammarExpr`|Expression evaluated by the statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(expression: ESCGrammarExpr): + _expression = expression + + + ## The expression this `ESCExpression` represents.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the expression this `ESCExpression` represents. (`ESCGrammarExpr`) + func get_expression() -> ESCGrammarExpr: + return _expression + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_expression_stmt(self) + + +## Represents an `if` statement. Uses the form:[br] +##```[br] +##if :[br] +## [br] +##elif :[br] +## [br] +##else:[br] +## [br] +##```[br] +##[br] +## - Can contain multiple `elif` blocks.[br] +## - `elif` and `else` blocks are optional. +class If extends ESCGrammarStmt: + var _condition: ESCGrammarExpr: + get = get_condition + var _then_branch: ESCGrammarStmts.Block: + get = get_then_branch + var _elif_branches: Array: + get = get_elif_branches + var _else_branch: ESCGrammarStmts.Block: + get = get_else_branch + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |condition|`ESCGrammarExpr`|the expression to be evaluated and tested for the `if` block.|yes|[br] + ## |then_branch|`ESCGrammarStmts.Block`|the block of statements to be executed should `condition` evaluate to `true`.|yes|[br] + ## |elif_branches|`Array`|an array containing any desired `elif` branches, with each element corresponding to its own `if` statement.|yes|[br] + ## |else_branch|`ESCGrammarStmts.Block`|the block of staetments to be executed should `condition` evaluate to `false`.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(condition: ESCGrammarExpr, then_branch: ESCGrammarStmts.Block, elif_branches: Array, else_branch: ESCGrammarStmts.Block): + _condition = condition + _then_branch = then_branch + _elif_branches = elif_branches + _else_branch = else_branch + + + ## The expression/predicate to be evaluated for the `if` block.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the expression/predicate to be evaluated for the `if` block. (`ESCGrammarExpr`) + func get_condition() -> ESCGrammarExpr: + return _condition + + + ## The block of statements to be executed should `condition` evaluate to `true`.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the block of statements to be executed should `condition` evaluate to `true`. (`ESCGrammarStmts.Block`) + func get_then_branch() -> ESCGrammarStmts.Block: + return _then_branch + + + ## The array of `elif` branches to include as part of this `if` statement.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the array of `elif` branches to include as part of this `if` statement. (`Array`) + func get_elif_branches() -> Array: + return _elif_branches + + + ## The block of statements to be executed should `condition` evaluate to `false`.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the block of statements to be executed should `condition` evaluate to `false`. (`ESCGrammarStmts.Block`) + func get_else_branch() -> ESCGrammarStmts.Block: + return _else_branch + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_if_stmt(self) + + +## Represents a `while` loop. Uses the form:[br] +##```[br] +##while :[br] +## [br] +##``` +class While extends ESCGrammarStmt: + var _condition: ESCGrammarExpr: + get = get_condition + var _body: ESCGrammarStmt: + get = get_body + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |condition|`ESCGrammarExpr`|the expression to be evaluated and tested in order for the `while` loop to be entered/continue executing.|yes|[br] + ## |body|`ESCGrammarStmt`|the block of statements to be executed should `condition` evaluate to `true`.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(condition: ESCGrammarExpr, body: ESCGrammarStmt): + _condition = condition + _body = body + + + ## The expression/predicate to be evaluated for the `while` block.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the expression/predicate to be evaluated for the `while` block. (`ESCGrammarExpr`) + func get_condition() -> ESCGrammarExpr: + return _condition + + + ## The block of statements to be executed should `condition` evaluate to `true`.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the block of statements to be executed should `condition` evaluate to `true`. (`ESCGrammarStmt`) + func get_body() -> ESCGrammarStmt: + return _body + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_while_stmt(self) + + +## Represents the equivalent of the GDScript `pass` statement and behaves exactly the same way. +class Pass extends ESCGrammarStmt: + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_pass_stmt(self) + + +## Represents a `stop` statement. Will stop the rest of the event from executing. +class Stop extends ESCGrammarStmt: + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_stop_stmt(self) + + +## Represents an option contained within a dialog. Usually of the form:[br] +##[br] +##```[br] +##- [condition][br] +## [br] +##```[br] +##[br] +## ...where:[br] +## - The result of `` is displayed in a dialog choice UI. The player +## will be able to select the displayed text as a dialog option.[br] +## - `[condition]` is optional and, if specified, is used to determine +## whether the dialog option is displayed.[br] +## - `` is a block of statements to be executed and must be indented like +## a typical block.[br] +##[br] +## E.g.[br] +##```[br] +##- "Why are you here?" [!option_already_visited][br] +## say("current_player", "Why are you here?")[br] +## say("worker", "Because I work here.")[br] +##``` +class DialogOption extends ESCGrammarStmt: + var _option: ESCGrammarExpr: + get = get_option + var _condition: ESCGrammarExpr: + get = get_condition + var _body: ESCGrammarStmt: + get = get_body + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |option|`ESCGrammarExpr`|the expression whose results will be displayed on screen as an option to be selected alongside any other options at the current level|yes|[br] + ## |condition|`ESCGrammarExpr`|an optional condition to be evaluated that is used to determine whether the option should be displayed at all|yes|[br] + ## |body|`ESCGrammarStmt`|the block of statements to be executed should this dialog option be selected|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(option: ESCGrammarExpr, condition: ESCGrammarExpr, body: ESCGrammarStmt): + _option = option + _condition = condition + _body = body + + + ## The option expression.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the option expression. (`ESCGrammarExpr`) + func get_option() -> ESCGrammarExpr: + return _option + + + ## The condition for this option, if one exists.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the condition for this option, if one exists. (`ESCGrammarExpr`) + func get_condition() -> ESCGrammarExpr: + return _condition + + + ## The statements of the option to be executed should the option be selected.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the statements of the option to be executed should the option be selected. (`ESCGrammarStmt`) + func get_body() -> ESCGrammarStmt: + return _body + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_dialog_option_stmt(self) + + +## Represents a dialog between characters. Can be nested. Begin each block of dialog +## with `?!`. Can take up to three optional arguments (detailed in the `init` method).[br] +##[br] +## (A block of dialog follows the same rules as a block of script statements, including +## the need to use appropriate indentation.)[br] +##[br] +## E.g.[br] +##```[br] +##?![br] +## - "This is the first dialog option!"[br] +## say("current_player", "This is the first dialog option!")[br] +## say("worker", "That's nice. Can you show me some other dialog options?")[br] +## ?![br] +## - "This is a nested dialog option, and appears on its own!"[br] +## say("current_player", "This is a nested dialog option, and appears on its own!"[br] +##```[br] +##[br] +class Dialog extends ESCGrammarStmt: + var _args: Array: + get = get_args + var _options: Array: + get = get_options + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |args|`Array`|the arguments pertaining to this dialog; the array is typically ordered: Path to avatar to be used for this dialog, Timeout (in seconds) until the default option is automatically selected, Timeout option to be selected once the timeout is reached.|yes|[br] + ## |options|`Array`|the dialog options available to this dialog|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(args: Array, options: Array): + _options = options + _args = args + + + ## The options available to this dialog.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the options available to this dialog. (`Array`) + func get_options() -> Array: + return _options + + + ## The arguments for this dialog as described in the `init` method.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the arguments for this dialog as described in the `init` method. (`Array`) + func get_args() -> Array: + return _args + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_dialog_stmt(self) + + +## Represents a `break` statement. Can be used to break out of a loop or a dialog. +## If used to break out of a nested dialog, an optional argument can be given that +## specifies how many levels of nested dialogs to break out of, including out of +## all nested dialogs. +class Break extends ESCGrammarStmt: + var _levels: ESCGrammarExpr: + get = get_levels + + + ## Initialization method. Must be called after instantiation.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |levels|`ESCGrammarExpr`|an expression whose result will be used to determine how many levels of nested dialogs to break out from; not applicable for loops.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func init(levels: ESCGrammarExpr): + _levels = levels + + + ## The expression whose result represents the number of nested dialog levels to break out from.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the expression whose result represents the number of nested dialog levels to break out from. (`ESCGrammarExpr`) + func get_levels() -> ESCGrammarExpr: + return _levels + + + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_break_stmt(self) + + +## Represents a `done` staetment. Used to end and break out of the top-level of +## the current dialog, regardless of the current nested depth. +class Done extends ESCGrammarStmt: + ## Method to invoke visitor-specific code.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |visitor|`Variant`|Visitor instance invoked to process this statement.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func accept(visitor): + return await visitor.visit_done_stmt(self) diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd.uid new file mode 100644 index 0000000..4a64e52 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd.uid @@ -0,0 +1 @@ +uid://brtiqt1b4hj5b diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd new file mode 100644 index 0000000..0944486 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd @@ -0,0 +1,936 @@ +## The actual interpreter that processes a parsed ASHES script. +extends RefCounted +class_name ESCInterpreter + + +## Used to represent the current player character in scripts. +const CURRENT_PLAYER_KEYWORD = "CURRENT_PLAYER" +const CURRENT_OBJECT = "THIS" + + +var _globals: ESCEnvironment +var _environment: ESCEnvironment = _globals + +var _locals: Dictionary = {} + +# The interpreter largely doesn't need to track state; however, for interruptions, +# we need a way to know which event a command is running for. This tracking is +# minimal and contained within the interpreter itself. +# +# Because yielding for a coroutine stores the current call stack and its state, +# if _current_event changes (e.g. some other event is being interpreted), resuming +# should remember the appropriate _current_event. In the case of a non-yielding +# command, the flow is synchronous, and so it isn't possible for _current_event +# to be changed and thus affect interruption. +# +# Still, this should be tested extensively, and, if at all possible, with multiple +# concurrent events running. +var _current_event: ESCGrammarStmts.Event + +# While most of the time we only run a single event at a time, it is possible for +# multiple events to +var _event_stack: Array = [] + +var _builtin_functions: Array = [ + "print" +] + + +func _init(callables: Array, globals: Dictionary): + _globals = ESCEnvironment.new() + + for callable in callables: + _globals.define(callable.get_command_name(), callable) + + for key in globals.keys(): + _globals.define(key, globals[key]) + + if not Engine.is_editor_hint(): + escoria.globals_manager.global_changed.connect(_on_global_changed) + + +func cleanup() -> void: + if is_instance_valid(_globals): + _globals.cleanup() + _globals = null + + if is_instance_valid(_environment): + _environment.cleanup() + _environment = null + + _locals.clear() + + if not Engine.is_editor_hint(): + escoria.globals_manager.global_changed.disconnect(_on_global_changed) + + +## The dictionary containing any and all global variables.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the dictionary containing any and all global variables. (`Dictionary`) +func get_global_values() -> Dictionary: + return _globals.get_values() + + +## Resets the interpreter, specifically any locally-scoped variables.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func reset() -> void: + _locals = {} + + +## Issues an interrupt to the currently-running event, if there is one.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt() -> void: + if _current_event and _current_event.get_running_command(): + _current_event.interrupt() + + +## The main entry point for the interpreter. Takes one or more statements and begins to interpret them. These usually represent the statements at the top level of the script being processed.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |statements|`Variant`|a single `ESCGrammarStmt`-derived statement or an array of them, representing the statements to be interpreted|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interpret(statements): + if not statements is Array: + statements = [statements] + + var rc = 0 + + for stmt in statements: + rc = await _execute(stmt) + + if rc is ESCParseError: + # TODO: runtime error handling + pass + + return rc + + +# Visitor implementations + +## Executes code relevant to interpreting a statement block.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Block`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_block_stmt(stmt: ESCGrammarStmts.Block): + var env: ESCEnvironment = ESCEnvironment.new() + env.init(_environment) + + return await _execute_block(stmt.get_statements(), env) + + +## Executes code relevant to interpreting an event in ASHES, e.g. `:look`. Emits the statement's `finished` signal upon completion, containing the return code.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Event`|the `ESCGrammarStmt` representing the ASHES event|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_event_stmt(stmt: ESCGrammarStmts.Event): + _current_event = stmt + + stmt.reset_interrupt() + + escoria.logger.debug( + self, + "Event %s started." % stmt.get_event_name() + ) + + # TODO: Handle interrupts. + var rc = await _execute(stmt.get_body()) + escoria.logger.debug( + self, + "Event (%s) was completed." % stmt.get_event_name() + ) + + _current_event = null + + # For now, we don't always return a return code from an event (e.g. when + # a 'done' statement is issued, since we don't have a good way to tell when + # we're at the top of a coroutine stack) + if typeof(rc) != TYPE_INT: + rc = ESCExecution.RC_OK + + if stmt.is_interrupted(): + rc = ESCExecution.RC_INTERRUPTED + + stmt.emit_finished(rc if rc else ESCExecution.RC_OK) + #var event: ESCEvent = ESCEvent.new("") + #event.init(stmt.get_name().get_lexeme(), stmt.get_flags()) + + #var statements = [] + + #for statement in stmt.get_statements(): + # statements.append(_execute(statement)) + + #event.statements = statements + + #return event + + +## Executes code relevant to interpreting an expression contained in a statement.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.ESCExpression`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_expression_stmt(stmt: ESCGrammarStmts.ESCExpression): + return await _evaluate(stmt.get_expression()) + + +## Executes code relevant to interpreting a function/method call.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Call`|the expression representing the function/method call to make|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_call_expr(expr: ESCGrammarExprs.Call): + var callee = await _evaluate(expr.get_callee()) + + var args: Array = [] + + for arg in expr.get_arguments(): + arg = await _evaluate(arg) + + # "Adapter" for current ESC commands since they don't currently take + # ESCObject's (or ESCRoom's) as arguments. + if arg is ESCObject or arg is ESCRoom: + arg = arg.global_id + + args.append(arg) + + # if we don't have a command to run, check against any built-in functions and, + # if one is found and is executed, we're done + if not callee is ESCBaseCommand: + if not callee in _builtin_functions: + escoria.logger.error( + self, + "Can only call valid commands." + ) + else: + return _handle_builtin_function(callee, args) + + var command = ESCCommand.new() + command.parameters = args + command.name = callee.get_command_name() + command.parser_token = expr.get_paren_token() + + var rc = ESCExecution.RC_OK + + if command.is_valid() and not _current_event.is_interrupted(): + _current_event.set_running_command(command) + rc = await command.run() + escoria.logger.debug( + self, + "Statement (%s) was completed." % command + ) + + _current_event.clear_running_command() + + return rc + + +# TODO: If we end up having functions that need to return values, use an 'out' parameter. +func _handle_builtin_function(fn_name: String, args: Array) -> int: + var rc = ESCExecution.RC_ERROR + + match fn_name: + 'print': + if args.size() > 1: + escoria.logger.error( + self, + "'print' only takes one argument" + ) + + _print(args) + rc = ESCExecution.RC_OK + + return rc + + +func _print(value): + if value[0] == null: + value[0] = "nil" + + print(value[0]) + + +## Executes code relevant to interpreting an `if` statement.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.If`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_if_stmt(stmt: ESCGrammarStmts.If): + if _is_truthy(await _evaluate(stmt.get_condition())): + return await _execute(stmt.get_then_branch()) + else: + for branch in stmt.get_elif_branches(): + if _is_truthy(await _evaluate(branch.get_condition())): + return await _execute(branch) + + if stmt.get_else_branch(): + return await _execute(stmt.get_else_branch()) + + return null + + +## Executes code relevant to interpreting a `while` loop.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.While`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_while_stmt(stmt: ESCGrammarStmts.While): + while _is_truthy(await _evaluate(stmt.get_condition())): + var ret = await _execute(stmt.get_body()) + + if ret is ESCGrammarStmts.Break: + break + + return null + + +## Executes code relevant to interpreting a `pass` statement.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Pass`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_pass_stmt(stmt: ESCGrammarStmts.Pass): + pass + + +## Executes code relevant to interpreting a `stop` statement. Relevant only to dialogs.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Stop`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_stop_stmt(stmt: ESCGrammarStmts.Stop): + return stmt + + +## Executes code relevant to interpreting a variable declaration and possible initialization.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Var`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_var_stmt(stmt: ESCGrammarStmts.Var): + var value = null + + if stmt.get_initializer(): + value = await _evaluate(stmt.get_initializer()) + + _environment.define(stmt.get_name().get_lexeme(), value) + return null + + +## Executes code relevant to interpreting a global variable declaration and possible initialization.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Global`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_global_stmt(stmt: ESCGrammarStmts.Global): + var value = null + + if stmt.get_initializer(): + value = await _evaluate(stmt.get_initializer()) + + # Only define the global if we haven't already done so; otherwise, just + # ignore it + if not _globals.get_values().has(stmt.get_name().get_lexeme()): + escoria.globals_manager.set_global(stmt.get_name().get_lexeme(), value) + _globals.define(stmt.get_name().get_lexeme(), value) + + return null + + +## Executes code relevant to interpreting a block of dialog.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Dialog`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_dialog_stmt(stmt: ESCGrammarStmts.Dialog): + var dialog: ESCDialog = ESCDialog.new() + var rc = ESCExecution.RC_OK + + while true: + dialog.options = [] + + for dialog_option in stmt.get_options(): + var option: ESCDialogOption = ESCDialogOption.new() + # TODO: Translation keys + option.source_option = dialog_option + option.option = await _evaluate(dialog_option.get_option()) + + if dialog_option.get_condition(): + option.set_is_valid(await _evaluate(dialog_option.get_condition())) + else: + option.set_is_valid(true) + + dialog.options.append(option) + + if dialog.options.size() == 0: + break + + if dialog.is_valid() and not _current_event.is_interrupted(): + var chosen_option = await dialog.run() + + if chosen_option: + var execute_ret = await _execute(chosen_option.source_option.get_body()) + escoria.logger.debug( + self, + "Chosen dialog option (%s) was completed." % chosen_option + ) + + if execute_ret is ESCGrammarStmts.Break: + var break_tracker: ESCBreakCounter = ESCBreakCounter.new() + + if execute_ret.get_levels(): + break_tracker.set_levels_left(await _evaluate(execute_ret.get_levels()) - 1) + else: + break_tracker.set_levels_left(0) + + return break_tracker + elif execute_ret is ESCGrammarStmts.Done: + return execute_ret + elif execute_ret is ESCBreakCounter: + if execute_ret.get_levels_left() > 0: + execute_ret.dec_levels_left() + return execute_ret + + return rc + + +## Executes code relevant to interpreting a dialog option, although this is more of a placeholder to keep the processing of the syntax tree going since dialog options are handled by the overall dialog block.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.DialogOption`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_dialog_option_stmt(stmt: ESCGrammarStmts.DialogOption): + pass + + +## Executes code relevant to interpreting a `break` statement.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Break`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_break_stmt(stmt: ESCGrammarStmts.Break): + return stmt + + +## Executes code relevant to interpreting a `done` statement. Only relevant when processing dialogs.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Done`|Statement node to interpret.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_done_stmt(stmt: ESCGrammarStmts.Done): + return stmt + + +## Executes code relevant to interpreting an expression that assigns a value to a variable. Enforces traditional scoping rules.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Assign`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_assign_expr(expr: ESCGrammarExprs.Assign): + var value = await _evaluate(expr.get_value()) + + var distance: int = _locals.get(expr, -1) + + if distance == -1: + _globals.assign(expr.get_name(), value) + escoria.globals_manager.set_global(expr.get_name().get_lexeme(), value) + else: + _environment.assign_at(distance, expr.get_name(), value) + + return value + + +## Executes code relevant to interpreting an `in` statement when checking whether an item in Escoria is in the player's inventory.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.InInventory`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_in_inventory_expr(expr: ESCGrammarExprs.InInventory): + var arg = await _evaluate(expr.get_identifier()) + + if arg is ESCObject: + arg = arg.global_id + + return escoria.inventory_manager.inventory_has(arg) + + +## Executes code relevant to interpreting an `is` statement when checking whether an item in Escoria has a particular state and/or is active.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Is`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_is_expr(expr: ESCGrammarExprs.Is): + var arg = await _evaluate(expr.get_identifier()) + + if typeof(arg) == TYPE_STRING: + if escoria.inventory_manager.inventory_has(arg): + arg = _look_up_object_by_global_id(arg) + + if expr.get_state(): + return arg.get_state() == await _evaluate(expr.get_state()) + + return arg.is_active() + + +## Executes code relevant to interpreting binary expressions, e.g. `==`, `!=`, etc.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Binary`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_binary_expr(expr: ESCGrammarExprs.Binary): + var left_part = await _evaluate(expr.get_left()) + var right_part = await _evaluate(expr.get_right()) + + match expr.get_operator().get_type(): + ESCTokenType.TokenType.EQUAL_EQUAL: + return _is_equal(left_part, right_part) + ESCTokenType.TokenType.BANG_EQUAL: + return not _is_equal(left_part, right_part) + ESCTokenType.TokenType.GREATER: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part > right_part + ESCTokenType.TokenType.GREATER_EQUAL: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part >= right_part + ESCTokenType.TokenType.LESS: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part < right_part + ESCTokenType.TokenType.LESS_EQUAL: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part <= right_part + ESCTokenType.TokenType.MINUS: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part - right_part + ESCTokenType.TokenType.PLUS: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part, false) + + if check: + return left_part + right_part + + check = _check_at_least_one_string(left_part, right_part) + + if check: + return str(left_part) + str(right_part) + + escoria.logger.error( + self, + "%s: Operands must be numbers or strings." % expr.get_operator().get_lexeme() + ) + ESCTokenType.TokenType.SLASH: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part / right_part + ESCTokenType.TokenType.STAR: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part * right_part + + return null + + +## Executes code relevant to interpreting unary expressions, e.g. `!`, `-`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Unary`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_unary_expr(expr: ESCGrammarExprs.Unary): + var right_part = await _evaluate(expr.get_right()) + + match expr.get_operator().get_type(): + ESCTokenType.TokenType.BANG, ESCTokenType.TokenType.NOT: + return not _is_truthy(right_part) + ESCTokenType.TokenType.MINUS: + var check = _check_is_number(right_part, expr.get_operator()) + return -right_part + + return null + + +## Executes code relevant to interpreting variable expressions. Worth noting is that variables prefixed with a `$` are treated as global IDs, e.g. `$npc_character` is interperted as the global ID `npc_character`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Variable`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_variable_expr(expr: ESCGrammarExprs.Variable): + if expr.get_name().get_lexeme().begins_with("$"): + return _look_up_object(expr.get_name()) + + if expr.get_name().get_lexeme() in _builtin_functions: + return expr.get_name().get_lexeme() + + return look_up_variable(expr.get_name(), expr) + + +## Executes code relevant to interpreting literal expressions, e.g. `"some string", `1234`, etc.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Literal`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_literal_expr(expr: ESCGrammarExprs.Literal): + return expr.get_value() + + +## Executes code relevant to interpreting logical expressions, e.g. `AND`, `OR`, etc.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Logical`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_logical_expr(expr: ESCGrammarExprs.Logical): + var left = await _evaluate(expr.get_left()) + + if expr.get_operator().get_type() == ESCTokenType.TokenType.OR: + if _is_truthy(left): + return left + else: + if not _is_truthy(left): + return left + + return await _evaluate(expr.get_right()) + + +## Executes code relevant to interpreting grouped expressions, e.g. those surrounded by parentheses.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Grouping`|Expression node to evaluate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_grouping_expr(expr: ESCGrammarExprs.Grouping): + return await _evaluate(expr.get_expression()) + + +## Performs resolution of the specified expression by specifying its scope depth.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExpr`|the expression requiring access to locally scoped variables|yes|[br] +## |depth|`int`|the scope depth of the local variables to be used by the expression|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func resolve(expr: ESCGrammarExpr, depth: int): + _locals[expr] = depth + + +## Fetches the value of the variable specified by `name` provided it exists within the applicable scope for the expression `expr`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`ESCToken`|Token representing the variable name to resolve.|yes|[br] +## |expr|`ESCGrammarExpr`|the expression containing the variable identified by `name`|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the value stored for the resolved variable. (`Variant`) +func look_up_variable(name: ESCToken, expr: ESCGrammarExpr): + var distance: int = _locals[expr] if _locals.has(expr) else -1 + + if distance == -1: + return _globals.get_value(name) + else: + return _environment.get_at(distance, name.get_lexeme()) + + +# Private methods +func _look_up_object(name: ESCToken): + var global_id: String = name.get_lexeme().substr(1) + + if global_id.to_upper() == CURRENT_PLAYER_KEYWORD: + global_id = escoria.main.current_scene.player.global_id + elif global_id.to_upper() == CURRENT_OBJECT: + # check if the event is attached to an object; if it isn't, then + # assume it's associated to the current scene (room) + global_id = _current_event.get_object_global_id() + + if global_id.is_empty(): + global_id = escoria.main.current_scene.global_id + + return _look_up_object_by_global_id(global_id) + + +func _look_up_object_by_global_id(global_id: String): + var obj = escoria.object_manager.get_object(global_id) # ESCObject + + if obj: + return obj + + if escoria.main.current_scene.global_id == global_id: + return escoria.main.current_scene + + escoria.logger.error( + self, + "Unable to resolve object with global ID '%s'." % global_id + ) + + return null + +func look_up_global(name: ESCToken): + if _globals.get_values().has(name.get_lexeme()): + return _globals.get_value(name) + + return null + + +func _evaluate(expr: ESCGrammarExpr): + var ret = await expr.accept(self) + + # TODO: Error handling + return ret + + +func _execute(stmt: ESCGrammarStmt): + var ret = await stmt.accept(self) + + # TODO: Error handling + return ret + + +func _execute_block(statements: Array, env: ESCEnvironment): + var previous_env = _environment + var ret = null + + _environment = env + + for stmt in statements: + ret = await _execute(stmt) + + if ret is ESCGrammarStmts.Break \ + or ret is ESCGrammarStmts.Done \ + or ret is ESCGrammarStmts.Stop: + + return ret + + # TODO: Proper error handling per statement? + #if ret: + # break + + _environment = previous_env + + return ret + + +func _is_truthy(value) -> bool: + if value == null: + return false + + if typeof(value) in [TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_BOOL]: + return bool(value) == true + + return false + + +func _is_equal(left_part, right_part) -> bool: + if not left_part and not right_part: + return true + + if not left_part: + return false + + return left_part == right_part + + +func _check_are_numbers(value_1, operator: ESCToken, value_2, strict: bool = true): + if typeof(value_1) in [TYPE_INT, TYPE_FLOAT] and typeof(value_2) in [TYPE_INT, TYPE_FLOAT]: + return true + + if strict: + escoria.logger.error( + self, + "%s: Operands must be numbers." % operator.get_lexeme() + ) + + return false + + +func _check_is_number(value, operator: ESCToken, strict: bool = true): + if typeof(value) in [TYPE_INT, TYPE_FLOAT]: + return true + + if strict: + escoria.logger.error( + self, + "%s: Operand must be number." % operator.get_lexeme() + ) + + return false + + +func _check_at_least_one_string(value_1, value_2): + return typeof(value_1) == TYPE_STRING || typeof(value_2) == TYPE_STRING + + +func _on_global_changed(key: String, old_value, new_value) -> void: + # Shoehorn this in as an adapter + var token: ESCToken = ESCToken.new() + token.init(ESCTokenType.TokenType.IDENTIFIER, key, null, "", -1, "") + + if _globals.get_values().has(key): + _globals.assign(token, new_value) + elif escoria.save_manager.is_loading_game: + _globals.define(key, new_value) diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd.uid new file mode 100644 index 0000000..6206b8e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd.uid @@ -0,0 +1 @@ +uid://cr0qvv1uugopd diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd new file mode 100644 index 0000000..4205f92 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd @@ -0,0 +1,31 @@ +## Factory class for producing an ASHES interpreter. +extends RefCounted +class_name ESCInterpreterFactory + + +static var _interpreter: ESCInterpreter = null + + +## Produces an interpreter as a singleton.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCInterpreter` value. (`ESCInterpreter`) +static func create_interpreter() -> ESCInterpreter: + if not _interpreter: + _interpreter = load("res://addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd").new(ESCCompiler.load_commands(), ESCCompiler.load_globals()) + return _interpreter + + _interpreter = load("res://addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd").new([], _interpreter.get_global_values()) + + return _interpreter + + +static func reset_interpreter() -> void: + if is_instance_valid(_interpreter): + _interpreter.cleanup() + _interpreter = null diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd.uid new file mode 100644 index 0000000..0062fdb --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd.uid @@ -0,0 +1 @@ +uid://of4w5bd42dnu diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd new file mode 100644 index 0000000..5fd01a3 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd @@ -0,0 +1,3 @@ +## Class to represent an error that occured during parsing. +extends Object +class_name ESCParseError diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd.uid new file mode 100644 index 0000000..b5b5eb5 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd.uid @@ -0,0 +1 @@ +uid://we4vs7stuggd diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd new file mode 100644 index 0000000..b88adcb --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd @@ -0,0 +1,975 @@ +## Class that handles parsing of scanned tokens in order to generate a list of +## statements for the interpreter to execute. +## +## Note that the vast majority of this class consists of (effectively) private methods +## in order to facilitate encapsulation, but if you're interested in how the parser +## is implemented, [check out the class in GitHub](https://github.com/godot-escoria/escoria-demo-game/blob/main/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd) +extends RefCounted +class_name ESCParser + + +var _tokens: Array +var _current: int = 0 +var _associated_object_global_id = "" + +var _loop_level: int = 0 +var _dialog_level: int = 0 + +var _compiler + + +## Initialization method. Must be called after instantiation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |compiler|`Variant`|a reference to the ASHES compiler; used primarily for error tracking|yes|[br] +## |tokens|`Array`|an array of tokens produced by the ASHES scanner|yes|[br] +## |associated_object_global_id|`String`|string containing the global ID of the associated object/room to be passed on to events|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func init(compiler, tokens: Array, associated_object_global_id: String) -> void: + _compiler = compiler + _tokens = tokens + _associated_object_global_id = associated_object_global_id + + +## Entry point for the parser. Begins parsing the tokens passed in to the `init` method.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns an array of statements for the interpreter to execute. (`Array`) +func parse() -> Array: + _loop_level = 0 + _dialog_level = 0 + var statements: Array = [] + + while not _at_end(): + statements.append(_declaration()) + + return statements + + +func _declaration() -> ESCGrammarStmt: + var retStmt + + if _match(ESCTokenType.TokenType.COLON): + retStmt = _event_declaration() + + if retStmt is ESCParseError: + _synchronize() + return null + else: + return retStmt + + if _match(ESCTokenType.TokenType.VAR): + retStmt = _var_declaration() + + if retStmt is ESCParseError: + _synchronize() + return null + else: + return retStmt + + if _match(ESCTokenType.TokenType.GLOBAL): + retStmt = _global_declaration() + + if retStmt is ESCParseError: + _synchronize() + return null + else: + return retStmt + + retStmt = _statement() + + if retStmt is ESCParseError: + _synchronize() + return null + + return retStmt + + +func _event_declaration(): + var name = _consume(ESCTokenType.TokenType.IDENTIFIER, "Expect event name. Got '%s' instead." % _peek().get_lexeme()) + + if name is ESCParseError: + return name + + var target = null + + if _check(ESCTokenType.TokenType.STRING) or _check(ESCTokenType.TokenType.IDENTIFIER): + var expr = _primary() + + if expr is ESCParseError: + return expr + + if expr is ESCGrammarExprs.Variable: + target = _literal_from_identifier(expr) + else: + target = expr + + var flags: Dictionary = {} + + var has_flags: bool = _match(ESCTokenType.TokenType.PIPE) + + while has_flags: + var flag = _consume( + ESCTokenType.TokenType.IDENTIFIER, + "Event '%s': Expect valid flag name. Got '%s' instead." % [name.get_lexeme(), _peek().get_lexeme()]) + + if flag is ESCParseError: + return flag + + var flag_condition = null + + if _match(ESCTokenType.TokenType.LESS): + if not _check(ESCTokenType.TokenType.IDENTIFIER): + return _error(_peek(), "Condition for flag '%s' must be a global variable." % flag.get_lexeme()) + + flag_condition = _primary() + + if flag_condition is ESCParseError: + return flag_condition + + var close_predicate_token = _consume( + ESCTokenType.TokenType.GREATER, + "For flag '%s', only one (global) variable may be used and must be enclosed between '<' and '>'." % flag.get_lexeme()) + + if close_predicate_token is ESCParseError: + return close_predicate_token + + flags[flag.get_lexeme()] = flag_condition + + has_flags = _match(ESCTokenType.TokenType.PIPE) + + if _match_in_order([ESCTokenType.TokenType.NEWLINE, ESCTokenType.TokenType.INDENT]): + var body = ESCGrammarStmts.Block.new() + body.init(_block()) + + var ret = ESCGrammarStmts.Event.new() + ret.init(name, target, flags, body, _associated_object_global_id) + + return ret + else: + return _error(_peek(), "Expected block after event declaration for '%s'. Code blocks require tab(s) at the start of a line." % name.get_lexeme()) + + +func _literal_from_identifier(expr: ESCGrammarExprs.Variable) -> ESCGrammarExprs.Literal: + var lexeme = expr.get_name().get_lexeme() + + if lexeme.begins_with(ESCScanner.GLOBAL_ID_PREFIX): + lexeme = lexeme.substr(1) + + var literal = ESCGrammarExprs.Literal.new() + literal.init(lexeme) + + return literal + + +func _expression(): + var expr = _assignment() + + return expr + + +func _statement(): + if _match(ESCTokenType.TokenType.IF): + var stmt = _if_statement() + return stmt + if _match(ESCTokenType.TokenType.WHILE): + var stmt = _while_statement() + return stmt + if _match(ESCTokenType.TokenType.PASS): + var stmt = _pass_statement() + return stmt + if _match(ESCTokenType.TokenType.STOP): + var stmt = _stop_statement() + return stmt + if _match(ESCTokenType.TokenType.QUESTION_BANG): + var stmt = _dialog_statement() + return stmt + #if _match_in_order([ESCTokenType.TokenType.BREAK, ESCTokenType.TokenType.NEWLINE]): + if _match(ESCTokenType.TokenType.BREAK): + if _loop_level == 0 and _dialog_level == 0: + return _error(_second_previous(), "'break' only allowed inside loops and dialogs") + + var stmt = _break_statement() + return stmt + if _match_in_order([ESCTokenType.TokenType.DONE, ESCTokenType.TokenType.NEWLINE]): + if _dialog_level == 0: + return _error(_second_previous(), "'done' only allowed inside dialogs") + + var stmt = _done_statement() + return stmt + if _match_in_order([ESCTokenType.TokenType.NEWLINE, ESCTokenType.TokenType.INDENT]) \ + or (_previous().get_type() == ESCTokenType.TokenType.NEWLINE and _match(ESCTokenType.TokenType.INDENT)): + var block = _block() + + if block is ESCParseError: + return block + + var ret = ESCGrammarStmts.Block.new() + ret.init(block) + return ret + + return _expression_statement() + + +func _if_statement(): + var condition = _expression() + + if condition is ESCParseError: + return condition + + var colon_token = _consume(ESCTokenType.TokenType.COLON, "Expect ':' after if condition.") + + if colon_token is ESCParseError: + return colon_token + + var consume = _consume_new_block_start("start of if statement") + + if consume: + return consume + + var then_branch_block = _block() + + if then_branch_block is ESCParseError: + return then_branch_block + + var then_branch = ESCGrammarStmts.Block.new() + then_branch.init(then_branch_block) + + var elif_branches = [] + + while _match(ESCTokenType.TokenType.ELIF): + var elif_branch = _elif_statement() + + if elif_branch is ESCParseError: + return elif_branch + + elif_branches.append(elif_branch) + + var else_branch = null + + if _match(ESCTokenType.TokenType.ELSE): + colon_token = _consume(ESCTokenType.TokenType.COLON, "Expect ':' after 'else'.") + + if colon_token is ESCParseError: + return colon_token + + consume = _consume_new_block_start("start of else statement") + + if consume: + return consume + + var else_branch_block = _block() + + else_branch = ESCGrammarStmts.Block.new() + else_branch.init(else_branch_block) + + if else_branch is ESCParseError: + return else_branch + + var to_ret = ESCGrammarStmts.If.new() + to_ret.init(condition, then_branch, elif_branches, else_branch) + return to_ret + + +func _elif_statement(): + var condition = _expression() + + if condition is ESCParseError: + return condition + + var colon_token = _consume(ESCTokenType.TokenType.COLON, "Expect ':' after elif condition.") + + var consume = _consume_new_block_start("start of elif statement") + + if consume: + return consume + + var then_branch_block = _block() + + if then_branch_block is ESCParseError: + return then_branch_block + + var then_branch = ESCGrammarStmts.Block.new() + then_branch.init(then_branch_block) + + var to_ret = ESCGrammarStmts.If.new() + to_ret.init(condition, then_branch, [], null) + return to_ret + + +func _while_statement(): + _loop_level += 1 + + var condition = _expression() + + if condition is ESCParseError: + return condition + + var colon_token = _consume(ESCTokenType.TokenType.COLON, "Expect ':' after condition.") + + if colon_token is ESCParseError: + return colon_token + + var consume = _consume_new_block_start("start of while loop") + + if consume: + return consume + + var block = _block() + + if block is ESCParseError: + return block + + var body = ESCGrammarStmts.Block.new() + body.init(block) + + var to_ret = ESCGrammarStmts.While.new() + to_ret.init(condition, body) + + _loop_level -= 1 + + return to_ret + + +func _pass_statement(): + var consume = _consume(ESCTokenType.TokenType.NEWLINE, "Expected NEWLINE after pass statement.") + + if consume is ESCParseError: + return consume + + var to_ret = ESCGrammarStmts.Pass.new() + return to_ret + + +func _stop_statement(): + var consume = _consume(ESCTokenType.TokenType.NEWLINE, "Expected NEWLINE after stop statement.") + + if consume is ESCParseError: + return consume + + var to_ret = ESCGrammarStmts.Stop.new() + return to_ret + + +func _dialog_statement(): + _dialog_level += 1 + + var args: Array = [] + + if _match(ESCTokenType.TokenType.LEFT_PAREN): + while true: + var arg = _expression() + + if arg is ESCParseError: + return arg + + args.append(arg) + + if not _match(ESCTokenType.TokenType.COMMA): + break + + var consume = _consume(ESCTokenType.TokenType.RIGHT_PAREN, "Expect ')' after start dialog arguments.") + + if consume is ESCParseError: + return consume + + if args.size() > 3: + return _error(_peek(), "Start dialog cannot have more than 3 arguments.") + + var consume = _consume_new_block_start("dialog start") + + if consume is ESCParseError: + return consume + + var options: Array = [] + + while true: + var dialog_option = _dialog_option_statement() + + if dialog_option is ESCParseError: + return dialog_option + + options.append(dialog_option) + + if _match(ESCTokenType.TokenType.DEDENT): + break + + var dialog: ESCGrammarStmts.Dialog = ESCGrammarStmts.Dialog.new() + dialog.init(args, options) + + _dialog_level -= 1 + + return dialog + + +func _consume_new_block_start(line_type: String): + var consume = _consume(ESCTokenType.TokenType.NEWLINE, "Expect NEWLINE after %s." % line_type) + + if consume is ESCParseError: + return consume + + consume = _consume(ESCTokenType.TokenType.INDENT, "Expect INDENT after %s." % line_type) + + if consume is ESCParseError: + return consume + + return null + + +func _dialog_option_statement(): + var consume = _consume(ESCTokenType.TokenType.MINUS, "Expect '-' before dialog option") + + if consume is ESCParseError: + return consume + + var expr = _expression() + + if expr is ESCParseError: + return expr + + var condition = null + + if _match(ESCTokenType.TokenType.LEFT_SQUARE): + condition = _expression() + + if condition is ESCParseError: + return condition + + consume = _consume(ESCTokenType.TokenType.RIGHT_SQUARE, "Expect ']' after dialog option condition") + + consume = _consume_new_block_start("dialog option") + + var block_stmts = _block() + + if block_stmts is ESCParseError: + return block_stmts + + var block = ESCGrammarStmts.Block.new() + block.init(block_stmts) + + var option: ESCGrammarStmts.DialogOption = ESCGrammarStmts.DialogOption.new() + option.init(expr, condition, block) + return option + + +func _break_statement(): + var levels = null + + if _check([ESCTokenType.TokenType.IDENTIFIER, ESCTokenType.TokenType.NUMBER]): + levels = _expression() + + if levels is ESCParseError: + return levels + + var consume = _consume(ESCTokenType.TokenType.NEWLINE, "Expected either expression and NEWLINE, or just NEWLINE, after break statement") + + if consume is ESCParseError: + return consume + + var ret = ESCGrammarStmts.Break.new() + ret.init(levels) + return ret + + +func _done_statement(): + var ret = ESCGrammarStmts.Done.new() + return ret + + +func _expression_statement(): + var expr = _expression() + + if expr is ESCParseError: + return expr + + var consume = _consume(ESCTokenType.TokenType.NEWLINE, "Expect NEWLINE after expression statement.") + + if consume is ESCParseError: + return consume + + var ret = ESCGrammarStmts.ESCExpression.new() + ret.init(expr) + return ret + + +func _block(): + var statements: Array = [] + + while not _check(ESCTokenType.TokenType.DEDENT) and not _at_end(): + var decl = _declaration() + + if decl is ESCParseError: + return decl + + statements.append(decl) + + _consume(ESCTokenType.TokenType.DEDENT, "Expected DEDENT after block.") + return statements + + +func _assignment(): + var expr = _or() + + if expr is ESCParseError: + return expr + + if _match(ESCTokenType.TokenType.EQUAL): + var equals: ESCToken = _previous() + var value = _assignment() + + if value is ESCParseError: + return value + + if expr is ESCGrammarExprs.Variable: + var name = expr.get_name() + var ret = ESCGrammarExprs.Assign.new() + ret.init(name, value) + return ret + elif expr is ESCGrammarExprs.Get: + var ret = ESCGrammarExprs.Set.new() + ret.init(expr.get_object(), expr.get_name(), value) + return ret + + return _error(equals, "Invalid assignment type.") + + return expr + + +func _or(): + var expr = _and() + + if expr is ESCParseError: + return expr + + while _match(ESCTokenType.TokenType.OR): + var operator: ESCToken = _previous() + var right = _and() + + if right is ESCParseError: + return right + + var left_expr = expr + expr = ESCGrammarExprs.Logical.new() + expr.init(left_expr, operator, right) + + return expr + + +func _and(): + var expr = _equality() + + if expr is ESCParseError: + return expr + + while _match(ESCTokenType.TokenType.AND): + var operator: ESCToken = _previous() + var right = _equality() + + if right is ESCParseError: + return right + + var left_expr = expr + expr = ESCGrammarExprs.Logical.new() + expr.init(left_expr, operator, right) + + return expr + + +func _equality(): + var expr = _comparison() + + if expr is ESCParseError: + return expr + + while _match([ESCTokenType.TokenType.BANG_EQUAL, ESCTokenType.TokenType.EQUAL_EQUAL]): + var operator: ESCToken = _previous() + var right = _comparison() + + if right is ESCParseError: + return right + + var left_expr = expr + expr = ESCGrammarExprs.Binary.new() + expr.init(left_expr, operator, right) + + return expr + + +func _comparison(): + var expr = _term() + + if expr is ESCParseError: + return expr + + # Flag predicates (e.g. TK) should not be treated as comparisons. + if _check(ESCTokenType.TokenType.GREATER) \ + and _check_next([ESCTokenType.TokenType.NEWLINE, ESCTokenType.TokenType.PIPE]): + + return expr + + while _match([ + ESCTokenType.TokenType.GREATER, + ESCTokenType.TokenType.GREATER_EQUAL, + ESCTokenType.TokenType.LESS, + ESCTokenType.TokenType.LESS_EQUAL]): + + var operator: ESCToken = _previous() + var right = _term() + + if right is ESCParseError: + return right + + var left_expr = expr + expr = ESCGrammarExprs.Binary.new() + expr.init(left_expr, operator, right) + + return expr + + +func _term(): + var expr = _factor() + + if expr is ESCParseError: + return expr + + while _match([ESCTokenType.TokenType.MINUS, ESCTokenType.TokenType.PLUS]): + var operator: ESCToken = _previous() + var right = _factor() + + if right is ESCParseError: + return right + + var left_expr = expr + expr = ESCGrammarExprs.Binary.new() + expr.init(left_expr, operator, right) + + return expr + + +func _factor(): + var expr = _unary() + + if expr is ESCParseError: + return expr + + while _match([ESCTokenType.TokenType.SLASH, ESCTokenType.TokenType.STAR]): + var operator: ESCToken = _previous() + var right = _unary() + + if right is ESCParseError: + return right + + var left_expr = expr + expr = ESCGrammarExprs.Binary.new() + expr.init(left_expr, operator, right) + + return expr + + +func _unary(): + if _match([ESCTokenType.TokenType.BANG, ESCTokenType.TokenType.NOT, ESCTokenType.TokenType.MINUS]): + var operator: ESCToken = _previous() + var right: ESCGrammarExpr = _unary() + + var ret = ESCGrammarExprs.Unary.new() + ret.init(operator, right) + return ret + + return _contains() + + +func _contains(): + var expr = _is_checking() + + if expr is ESCParseError: + return expr + + if _match(ESCTokenType.TokenType.IN): + var in_token: ESCToken = _previous() + + var consume = _consume(ESCTokenType.TokenType.INVENTORY, "Expected 'inventory' after 'in'.") + + if consume is ESCParseError: + return consume + + var ret = ESCGrammarExprs.InInventory.new() + ret.init(expr) + return ret + + return expr + + +func _is_checking(): + var expr = _call() + + if expr is ESCParseError: + return expr + + if _match(ESCTokenType.TokenType.IS): + var in_token: ESCToken = _previous() + + if _match(ESCTokenType.TokenType.ACTIVE): + var ret = ESCGrammarExprs.Is.new() + ret.init(expr, null, _previous()) + return ret + else: + var state_expr = _expression() + + if state_expr is ESCParseError: + return state_expr + + var ret = ESCGrammarExprs.Is.new() + ret.init(expr, state_expr, null) + return ret + + return expr + + +func _call(): + var expr = _primary() + + if expr is ESCParseError: + return expr + + while true: + if _match(ESCTokenType.TokenType.LEFT_PAREN): + expr = _finish_call(expr) + + if expr is ESCParseError: + return expr + else: + break + + return expr + + +func _finish_call(callee: ESCGrammarExpr): + var args: Array = [] + var to_return = null + + if not _check(ESCTokenType.TokenType.RIGHT_PAREN): + var done: bool = false + + while not done: + if args.size() >= 255: + return _error(_peek(), "Can't have more than 255 arguments.") + + var expr = _expression() + + if expr is ESCParseError: + return expr + + args.append(expr) + + done = not _match(ESCTokenType.TokenType.COMMA) + #done = _peek().get_type() == ESCTokenType.TokenType.NEWLINE + + var paren = _consume(ESCTokenType.TokenType.RIGHT_PAREN, "Expect ')' after arguments.") + + #if paren.get_type() != ESCTokenType.TokenType.NEWLINE: + # return _error(ESCTokenType.TokenType.NEWLINE, "Expect NEWLINE after arguments.") + + var ret = ESCGrammarExprs.Call.new() + ret.init(callee, paren, args) + return ret + + +func _primary(): + if _match(ESCTokenType.TokenType.FALSE): + var ret = ESCGrammarExprs.Literal.new() + ret.init(false) + return ret + + if _match(ESCTokenType.TokenType.TRUE): + var ret = ESCGrammarExprs.Literal.new() + ret.init(true) + return ret + + if _match(ESCTokenType.TokenType.NIL): + var ret = ESCGrammarExprs.Literal.new() + ret.init(null) + return ret + + if _match([ESCTokenType.TokenType.NUMBER, ESCTokenType.TokenType.STRING]): + var ret = ESCGrammarExprs.Literal.new() + ret.init(_previous().get_literal()) + return ret + + if _match(ESCTokenType.TokenType.IDENTIFIER): + var ret = ESCGrammarExprs.Variable.new() + ret.init(_previous()) + return ret + + if _match(ESCTokenType.TokenType.LEFT_PAREN): + var expr = _expression() + + if expr is ESCParseError: + return expr + + _consume(ESCTokenType.TokenType.RIGHT_PAREN, "Expect ')' after expression.") + + var ret = ESCGrammarExprs.Grouping.new() + ret.init(expr) + return ret + + return _error(_peek(), "Expect expression.") + + +func _var_declaration() -> ESCGrammarStmt: + var name = _consume(ESCTokenType.TokenType.IDENTIFIER, "Expect variable name.") + + var initializer: ESCGrammarExpr = null + + if _match(ESCTokenType.TokenType.EQUAL): + initializer = _expression() + + _consume(ESCTokenType.TokenType.NEWLINE, "Expect newline after variable declaration.") + + var ret = ESCGrammarStmts.Var.new() + ret.init(name, initializer) + return ret + + +func _global_declaration() -> ESCGrammarStmt: + var name = _consume(ESCTokenType.TokenType.IDENTIFIER, "Expect global variable name.") + + var initializer: ESCGrammarExpr = null + + if _match(ESCTokenType.TokenType.EQUAL): + initializer = _expression() + + _consume(ESCTokenType.TokenType.NEWLINE, "Expect newline after global variable declaration.") + + var ret = ESCGrammarStmts.Global.new() + ret.init(name, initializer) + return ret + + +func _at_end() -> bool: + return _peek().get_type() == ESCTokenType.TokenType.EOF + + +func _peek() -> ESCToken: + return _tokens[_current] + + +func _match(tokenTypes) -> bool: + if not tokenTypes is Array: + tokenTypes = [tokenTypes] + + for type in tokenTypes: + if _check(type): + _advance() + return true + + return false + + +func _match_in_order(tokenTypes) -> bool: + if not tokenTypes is Array: + tokenTypes = [tokenTypes] + + for type in tokenTypes: + if not _check(type): + return false + + _advance() + + return true + + +func _consume(tokenType, message: String): + if _check(tokenType): + return _advance() + + return _error(_peek(), message) + + +func _check(tokenTypes) -> bool: + if not tokenTypes is Array: + tokenTypes = [tokenTypes] + + if _at_end(): + return false + + for type in tokenTypes: + if _peek().get_type() == type: + return true + + return false + + +# This turns the parser into an LL(2). +func _check_next(tokenTypes) -> bool: + if not tokenTypes is Array: + tokenTypes = [tokenTypes] + + if _at_end(): + return false + + _current += 1 + + if _at_end(): + _current -= 1 + return false + + for type in tokenTypes: + if _peek().get_type() == type: + _current -= 1 + return true + + _current -= 1 + + return false + + +func _previous() -> ESCToken: + return _tokens[_current - 1] + + +func _second_previous() -> ESCToken: + return _tokens[_current - 2] + + +func _advance() -> ESCToken: + if not _at_end(): + _current += 1 + + return _previous() + + +func _error(token: ESCToken, message: String) -> ESCParseError: + _compiler.had_error = true + var source: String = token.get_filename() if not token.get_filename().is_empty() else token.get_source() + + ESCSafeLogging.log_warn(self, "%s: Line %s at '%s': %s" % [source, token.get_line(), token.get_lexeme(), message]) + + return ESCParseError.new() + + +func _synchronize() -> void: + _advance() + + while not _at_end(): + if _previous().get_type() == ESCTokenType.TokenType.NEWLINE: + return + + match _peek().get_type(): + ESCTokenType.TokenType.VAR,\ + ESCTokenType.TokenType.IF,\ + ESCTokenType.TokenType.WHILE,\ + ESCTokenType.TokenType.RETURN: + return + + _advance() diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd.uid new file mode 100644 index 0000000..1257987 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd.uid @@ -0,0 +1 @@ +uid://r1niry3uhi6o diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd new file mode 100644 index 0000000..2b32b35 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd @@ -0,0 +1,506 @@ +## Resolves script variables to ensure that scoping rules are obeyed. +## +## The class will throw errors if scoping rules are broken or otherwise not followed. +extends RefCounted +class_name ESCResolver + + +var _interpreter +var _compiler +var _scopes: Array = [] + + +func _init(interpreter) -> void: + _interpreter = interpreter + + +## Entry point for the resolver. Begins attempting to resolve one or more statements passed in.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |statements|`Variant`|a single `ESCGrammarStmt`-derived statement or an array of them, representing the statements to be resolved|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func resolve(statements): + if not statements is Array: + statements = [statements] + + var ret = null + + for stmt in statements: + ret = _resolve_stmt(stmt) + + # TODO: Error handling? + + +## Attempts to resolve an Escoria event, e.g. `:look`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Event`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_event_stmt(stmt: ESCGrammarStmts.Event): + resolve(stmt.get_body()) + + +## Attempts to resolve a block statement.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Block`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_block_stmt(stmt: ESCGrammarStmts.Block): + _begin_scope() + + resolve(stmt.get_statements()) + + _end_scope() + + +## Attempts to resolve an expression contained in a statement.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.ESCExpression`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_expression_stmt(stmt: ESCGrammarStmts.ESCExpression): + _resolve_expr(stmt.get_expression()) + + +## Attempts to resolve the various parts of an `if` statement, i.e. the predicates and related branches.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.If`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_if_stmt(stmt: ESCGrammarStmts.If): + _resolve_expr(stmt.get_condition()) + _resolve_stmt(stmt.get_then_branch()) + + for branch in stmt.get_elif_branches(): + _resolve_expr(branch.get_condition()) + _resolve_stmt(branch.get_then_branch()) + + if stmt.get_else_branch(): + _resolve_stmt(stmt.get_else_branch()) + + +## Attempts to resolve the predicate of a `while` loop along with the loop body.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.While`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_while_stmt(stmt: ESCGrammarStmts.While): + _resolve_expr(stmt.get_condition()) + _resolve_stmt(stmt.get_body()) + + +## `pass` statements contain nothing to resolve, so are skipped over.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Pass`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_pass_stmt(stmt: ESCGrammarStmts.Pass): + pass + + +## `stop` statements contain nothing to resolve, so are skipped over.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Stop`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_stop_stmt(stmt: ESCGrammarStmts.Stop): + pass + + +## Attempts to resolve a variable declaration statement along with its possible initializer.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Var`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_var_stmt(stmt: ESCGrammarStmts.Var): + _declare(stmt.get_name()) + + if stmt.get_initializer(): + _resolve_expr(stmt.get_initializer()) + + _define(stmt.get_name()) + + +## `global` statements are available at all scoping levels and so require no resolution.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Global`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_global_stmt(stmt: ESCGrammarStmts.Global): + pass + + +## Attempts to resolve the various parts of a dialog option, i.e. the option expression, the condition (if one exists), and the body of the option.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.DialogOption`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_dialog_option_stmt(stmt: ESCGrammarStmts.DialogOption): + _resolve_expr(stmt.get_option()) + + if stmt.get_condition(): + _resolve_expr(stmt.get_condition()) + + _resolve_stmt(stmt.get_body()) + + +## Attempts to resolve a `break` statement if used inside a dialog.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Break`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_break_stmt(stmt: ESCGrammarStmts.Break): + if stmt.get_levels(): + _resolve_expr(stmt.get_levels()) + + +## `done` statements contain nothing to resolve, so are skipped over.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Done`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_done_stmt(stmt: ESCGrammarStmts.Done): + pass + + +## Attempts to resolve a dialog's arguments and options.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |stmt|`ESCGrammarStmts.Dialog`|Statement node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_dialog_stmt(stmt: ESCGrammarStmts.Dialog): + for arg in stmt.get_args(): + _resolve_expr(arg) + + resolve(stmt.get_options()) + + +## Literals contain nothing to resolve, so are skipped over.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Literal`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_literal_expr(expr: ESCGrammarExprs.Literal): + pass + + +## Attempts to resolve a function/method call expression, including its name and arguments.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Call`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_call_expr(expr: ESCGrammarExprs.Call): + _resolve_expr(expr.get_callee()) + + for arg in expr.get_arguments(): + _resolve_expr(arg) + + +## Attempts to resolve an assignment expression.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Assign`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_assign_expr(expr: ESCGrammarExprs.Assign): + _resolve_expr(expr.get_value()) + _resolve_local(expr, expr.get_name()) + + +## Attempts to resolve an `in` expression, specifically the item identifier.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.InInventory`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_in_inventory_expr(expr: ESCGrammarExprs.InInventory): + _resolve_expr(expr.get_identifier()) + + +## Attempts to resolve an `is` expression, including the item identifier and the state (if one is specified).[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Is`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_is_expr(expr: ESCGrammarExprs.Is): + _resolve_expr(expr.get_identifier()) + + if expr.get_state(): + _resolve_expr(expr.get_state()) + + +## Attempts to resolve a binary expression.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Binary`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_binary_expr(expr: ESCGrammarExprs.Binary): + _resolve_expr(expr.get_left()) + _resolve_expr(expr.get_right()) + + +## Attempts to resolve a logical expression.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Logical`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_logical_expr(expr: ESCGrammarExprs.Logical): + _resolve_expr(expr.get_left()) + _resolve_expr(expr.get_right()) + + +## Attempts to resolve a `get` expression (possibly not used at the moment).[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Get`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_get_expr(expr: ESCGrammarExprs.Get): + _resolve_expr(expr.get_object()) + + +## Attempts to resolve a `set` expression (possibly not used at the moment).[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Set`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_set_expr(expr: ESCGrammarExprs.Set): + _resolve_expr(expr.get_object()) + _resolve_expr(expr.get_value()) + + +## Attempts to resolve a grouping expression.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Grouping`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_grouping_expr(expr: ESCGrammarExprs.Grouping): + _resolve_expr(expr.get_expression()) + + +## Attempts to resolve a unary expression.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Unary`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_unary_expr(expr: ESCGrammarExprs.Unary): + _resolve_expr(expr.get_right()) + + +## Attempts to resolve a local variable expression[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |expr|`ESCGrammarExprs.Variable`|Expression node being resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func visit_variable_expr(expr: ESCGrammarExprs.Variable): + # If this is an ESCObject reference, we don't need to resolve it as we deal + # with this when interpreting, similar to a global. + if expr.get_name().get_lexeme().begins_with("$"): + return + + if not _scopes.is_empty() \ + and _scopes.front().has(expr.get_name().get_lexeme()) \ + and not _scopes.front()[expr.get_name().get_lexeme()]: + + ESCSafeLogging.log_error(self, "Can't read local variable in own initializer.") + + var resolved: bool = _resolve_local(expr, expr.get_name()) + + +# Private methods +func _resolve_stmt(stmt: ESCGrammarStmt): + return stmt.accept(self) + + +func _resolve_expr(expr: ESCGrammarExpr): + return expr.accept(self) + + +func _begin_scope(): + _scopes.push_front({}) + + +func _end_scope(): + _scopes.pop_front() + + +func _declare(name: ESCToken): + if _scopes.is_empty(): + return + + var scope: Dictionary = _scopes.front() + + if scope.has(name.get_lexeme()): + # TODO: Better error handling + ESCSafeLogging.log_error(self, "Already a variable with name '%s' in this scope." % name.get_lexeme()) + + scope[name.get_lexeme()] = false + + +func _define(name: ESCToken): + if _scopes.is_empty(): + return + + _scopes.front()[name.get_lexeme()] = true + + +func _resolve_local(expr: ESCGrammarExpr, name: ESCToken) -> bool: + for i in range(0, _scopes.size()): + if _scopes[i].has(name.get_lexeme()): + _interpreter.resolve(expr, i) + return true + + return false diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd.uid new file mode 100644 index 0000000..6b4f37e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd.uid @@ -0,0 +1 @@ +uid://bb1slslpmyi8v diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd new file mode 100644 index 0000000..08642a9 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd @@ -0,0 +1,393 @@ +## ASHES language scanner. +## +## This is the first link in the ASHES compiler toolchain, and takes in ASHES +## script as text in order to provide a list of tokens to be handled by the +## ASHES parser. +extends RefCounted +class_name ESCScanner + + +## In ASHES, an identifier starting with `$` is interpreted as a lookup in +## `ESCObjectManager.get_object()` with the value after the `$`. +const GLOBAL_ID_PREFIX: String = "$" + + +var _tokens: Array = [] + +var _keywords: Dictionary +var _start: int = 0 +var _current: int = 0 +var _line: int = 1 +var _at_start_of_line: bool = true +var _empty_line: bool = false +var _indent_level: int = 0 + +var _escape_chars: Array = [] + +var _source: String: + set = set_source +var _filename: String: + set = set_filename, + get = get_filename + +var _alpha_regex: RegEx +var _digit_regex: RegEx +var _alphanumeric_regex: RegEx + +var _indent_level_stack: Array + + +func _init(): + _keywords["active"] = ESCTokenType.TokenType.ACTIVE + _keywords["and"] = ESCTokenType.TokenType.AND + _keywords["break"] = ESCTokenType.TokenType.BREAK + _keywords["done"] = ESCTokenType.TokenType.DONE + _keywords["elif"] = ESCTokenType.TokenType.ELIF + _keywords["else"] = ESCTokenType.TokenType.ELSE + _keywords["false"] = ESCTokenType.TokenType.FALSE + _keywords["global"] = ESCTokenType.TokenType.GLOBAL + _keywords["if"] = ESCTokenType.TokenType.IF + _keywords["in"] = ESCTokenType.TokenType.IN + _keywords["inventory"] = ESCTokenType.TokenType.INVENTORY + _keywords["is"] = ESCTokenType.TokenType.IS + _keywords["nil"] = ESCTokenType.TokenType.NIL + _keywords["not"] = ESCTokenType.TokenType.NOT + _keywords["or"] = ESCTokenType.TokenType.OR + _keywords["pass"] = ESCTokenType.TokenType.PASS + _keywords["stop"] = ESCTokenType.TokenType.STOP + _keywords["true"] = ESCTokenType.TokenType.TRUE + _keywords["var"] = ESCTokenType.TokenType.VAR + _keywords["while"] = ESCTokenType.TokenType.WHILE + + _escape_chars.append('"') + + _alpha_regex = RegEx.new() + _alpha_regex.compile("[a-zA-Z_\\$]") + + _digit_regex = RegEx.new() + _digit_regex.compile("[0-9]") + + _alphanumeric_regex = RegEx.new() + _alphanumeric_regex.compile("[a-zA-Z0-9_\\$]") + + _indent_level_stack.push_front(0) + + +## Sets the ASHES source as a string. This is the source that will be scanned and turned into tokens.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |source|`String`|Raw ASHES source text to tokenize.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_source(source: String) -> void: + # If we don't have a newline terminator, then we add one to be safe. + if not source.ends_with("\n"): + source += "\n" + + _source = source + + +## Sets the path of the file containing the source to be scanned.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |filename|`String`|Path associated with the source (used for error reporting).|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_filename(filename: String) -> void: + _filename = filename + + +## The path of the file containing the source to be scanned.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the path of the file containing the source to be scanned. (`String`) +func get_filename() -> String: + return _filename + + +## Entry point for the scanner. Begins scanning the source and returns an array of tokens corresponding to the source. `set_source` must be called prior to calling this method.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Array` value. (`Array`) +func scan_tokens() -> Array: + while not _at_end(): + _start = _current + _scan_token() + + # Generate a DEDENT token for each indent level > 0 left on the stack + while _indent_level_stack.pop_front() > 0: + _add_token(ESCTokenType.TokenType.DEDENT, null) + + var token: ESCToken = ESCToken.new() + token.init(ESCTokenType.TokenType.EOF, "", null, _source, _line, _filename) + _tokens.append(token) + + return _tokens + + +func _scan_token(): + var c: String = _advance() + + # We need to check for entirely empty/blank lines, so do some lookahead stuff. + if _at_start_of_line: + var line_indent_level: int = 0 + var done: bool = false + + while not done: + if c == ' ': + _start = _current + c = _advance() + elif c == '\t': + line_indent_level += 1 + _start = _current + c = _advance() + else: + done = true + + match c: + '#', '\r', '\n': + _empty_line = true + _: + _empty_line = false + + if not _empty_line: + _check_indent(line_indent_level) + + _at_start_of_line = false + + match c: + '(': + _add_token(ESCTokenType.TokenType.LEFT_PAREN, null) + ')': + _add_token(ESCTokenType.TokenType.RIGHT_PAREN, null) + '[': + _add_token(ESCTokenType.TokenType.LEFT_SQUARE, null) + ']': + _add_token(ESCTokenType.TokenType.RIGHT_SQUARE, null) + ',': + _add_token(ESCTokenType.TokenType.COMMA, null) + '.': + _add_token(ESCTokenType.TokenType.DOT, null) + '-': + _add_token(ESCTokenType.TokenType.MINUS, null) + '+': + _add_token(ESCTokenType.TokenType.PLUS, null) + '*': + _add_token(ESCTokenType.TokenType.STAR, null) + ':': + _add_token(ESCTokenType.TokenType.COLON, null) + '/': + _add_token(ESCTokenType.TokenType.SLASH, null) + '|': + _add_token(ESCTokenType.TokenType.PIPE, null) + '?': + _add_token(ESCTokenType.TokenType.QUESTION_BANG if _match('!') else ESCTokenType.TokenType.QUESTION, null) + '#': + # the rest of the line is a comment + while _peek() != '\n' and not _at_end(): + _advance() + + '\r', '\n': + if not _empty_line: + _add_token(ESCTokenType.TokenType.NEWLINE, null) + + _line += 1 + _at_start_of_line = true + '!': + if _match('='): + _add_token(ESCTokenType.TokenType.BANG_EQUAL, null) + elif _match('?'): + _add_token(ESCTokenType.TokenType.BANG_QUESTION, null) + else: + _add_token(ESCTokenType.TokenType.BANG, null) + '=': + _add_token(ESCTokenType.TokenType.EQUAL_EQUAL if _match('=') else ESCTokenType.TokenType.EQUAL, null) + '<': + _add_token(ESCTokenType.TokenType.LESS_EQUAL if _match('=') else ESCTokenType.TokenType.LESS, null) + '>': + _add_token(ESCTokenType.TokenType.GREATER_EQUAL if _match('=') else ESCTokenType.TokenType.GREATER, null) + + ' ', '\t': + pass + + '"': + _string() + + _: + if _is_digit(c): + _number() + elif _is_alpha(c): + _identifier() + else: + _error(_line, "Unexpected character: %s" % c) + + +func _check_indent(indent_level: int) -> void: + if indent_level > _indent_level_stack.front(): + _indent_level_stack.push_front(indent_level) + _add_token(ESCTokenType.TokenType.INDENT, null) + elif indent_level < _indent_level_stack.front(): + while _indent_level_stack.front() > indent_level: + _indent_level_stack.pop_front() + _add_token(ESCTokenType.TokenType.DEDENT, null) + + if _indent_level_stack.front() != indent_level: + _error(_line, "Inconsistent indent.") + + +func _is_digit(c: String) -> bool: + return _digit_regex.search(c) != null + + +func _is_alpha(c: String) -> bool: + return _alpha_regex.search(c) != null + + +func _is_alphanumeric(c: String) -> bool: + return _alphanumeric_regex.search(c) != null + + +func _identifier() -> void: + # Identifiers starting with '$' are used as shorthand for global IDs and + # are allowed to contain hyphens (e.g., `$closet-door`). Other identifiers + # still treat '-' as the subtraction operator. + var allow_hyphen_in_identifier: bool = _source[_start] == GLOBAL_ID_PREFIX + + while true: + var current_char = _peek() + if _is_alphanumeric(current_char) or (allow_hyphen_in_identifier and current_char == '-'): + _advance() + else: + break + + var text: String = _source.substr(_start, _current - _start) + var type = _keywords.get(text) + + if not type: + type = ESCTokenType.TokenType.IDENTIFIER + + _add_token(type, null) + + +func _string() -> void: + while (_is_escape_char() or _peek() != '"') and not _at_end(): + if _peek() == '\n': + _line += 1 + _advance() + + if _at_end(): + _error(_line, "Unterminated string.") + return + + # Closing " + _advance() + + # Trim surrounding quotes + var value: String = _source.substr(_start + 1, (_current - 1) - (_start + 1)) + + # Deal with escape chars + for c in _escape_chars: + value = value.replace("\\" + c, c) + + _add_token(ESCTokenType.TokenType.STRING, value) + + +func _number() -> void: + while _is_digit(_peek()): + _advance() + + # Fraction part? + if _peek() == '.' and _is_digit(_peek_next()): + _advance() + + while _is_digit(_peek()): + _advance() + + _add_token(ESCTokenType.TokenType.NUMBER, _source.substr(_start, _current - _start).to_float()) + + +func _is_escape_char() -> bool: + if _peek() == '\\' and _peek_next() in _escape_chars: + _advance() + return true + + return false + + +# TODO: Move error reporting up when compiler is updated. +func _error(line: int, message: String) -> void: + ESCSafeLogging.log_error(self, "[Line %s]: %s" % [line, message]) + + +func _match(expected: String) -> bool: + if _at_end(): + return false; + + if _source[_current] != expected: + return false + + _current += 1 + + return true + + +func _peek() -> String: + if _at_end(): + return "\\0" + + return _source[_current] + + +func _peek_next() -> String: + if _current + 1 >= _source.length(): + return '\\0' + + return _source[_current + 1] + + +func _at_end() -> bool: + return _current >= _source.length() + + +func _advance() -> String: + var c: String = _source[_current] + _current += 1 + return c + + +func _rewind() -> void: + _current -= 1 + + if _current <= 0: + _current = 0 + + +func _add_token(type: int, literal) -> void: + var text: String = "" + + if not type in [ESCTokenType.TokenType.INDENT, ESCTokenType.TokenType.DEDENT]: + text = _source.substr(_start, _current - _start) + + var token: ESCToken = ESCToken.new() + token.init(type, text, literal, _source, _line, _filename) + _tokens.append(token) diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd.uid new file mode 100644 index 0000000..da18a8b --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd.uid @@ -0,0 +1 @@ +uid://b0uvne22seenx diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd new file mode 100644 index 0000000..46ba408 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd @@ -0,0 +1,117 @@ +## Convenience class to help build scripts in code for use with the ASHES compiler +## toolchain. Loosely follows the Builder pattern. +extends RefCounted +class_name ESCScriptBuilder + + +# Not really a "Builder" pattern per se, but helps with readability. + + +var _script: String = "" +var _indent_level: int = 0 + + +## The script as it currently exists. No building is actually performed and therefore no error checking of any kind is performed.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the script as it currently exists. No building is actually performed and therefore no error checking of any kind is performed. (`String`) +func build(): + return _script + + +## Adds an Escoria event to the script.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Event identifier to add to the script.|yes|[br] +## |flags|`Array`|an array of flags for the event, e.g. `TK`|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func add_event(name: String, flags: Array): + _script += ":" + name + + if flags: + _script += " | " + _script += " ".join(flags) + + _script += "\n" + + return self + + +## Begins a new block/scope for the script.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns `self` for method chaining. (`ESCScriptBuilder`) +func begin_block(): + _indent_level += 1 + + return self + + +## Closes the current block/scope of the script.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns `self` for method chaining. (`ESCScriptBuilder`) +func end_block(): + _indent_level -= 1 + + return self + + +## Adds a command to be called in the script.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Command name to append to the script.|yes|[br] +## |args|`Variant`|the arguments for the command; can be a single argument or an array|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func add_command(name: String, args): + if not args is Array: + args = [args] + + _script += "\t".repeat(_indent_level) + _script += name + + _script += "(" + + var args_string: String = "" + + for arg in args: + if !args_string.is_empty(): + args_string += ", " + + if arg is String: + args_string += "\"" + arg + "\"" + else: + args_string += str(arg) + + _script += args_string + ")" + + _script += "\n" + + return self diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd.uid new file mode 100644 index 0000000..da517c4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd.uid @@ -0,0 +1 @@ +uid://yscamgxx8jk1 diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd new file mode 100644 index 0000000..ec95d98 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd @@ -0,0 +1,144 @@ +## Represents a token generated by the ASHES scanner to be parsed by the ASHES +## parser. +## +## This class contains any information required by the parser but also carries +## with it associated information useful for debugging and tracing purposes. +extends RefCounted +class_name ESCToken + + +var _tokenType: int: # ESCScanner.TokenType enum + get = get_type +var _lexeme: String: + get = get_lexeme +var _literal: + get = get_literal +var _line: int: + get = get_line +var _source: String: + get = get_source, + set = set_source +var _filename: String: + get = get_filename + + +## Initialization method. Must be called after instantiation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |tokenType|`int`|a value from `ESCTokenType.TokenType`|yes|[br] +## |lexeme|`String`|text corresponding to the scanned token|yes|[br] +## |literal|`Variant`|if the token represents a literal value, this is that value; can be null|yes|[br] +## |source|`String`|the source code (script) containing the token, if available|yes|[br] +## |line|`int`|the line in the source containing the token, if available|yes|[br] +## |filename|`String`|the path of the file containing the token, if available|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func init(tokenType: int, lexeme: String, literal, source: String, line: int, filename: String) -> void: + _tokenType = tokenType + _lexeme = lexeme + _literal = literal + _source = source + _line = line + _filename = filename + + +## Sets the source of the token. This is typically the entire script file as a string.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`String`|Full script source used to resolve this token.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_source(value: String) -> void: + _source = value + + +## The source of the token.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the source of the token. (`String`) +func get_source() -> String: + return _source + + +## The type of token this is; will be a value from `ESCTokenType.TokenType`.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the type of token this is; will be a value from `ESCTokenType.TokenType`. (`int`) +func get_type() -> int: + return _tokenType + + +## The literal value of the token, if it exists.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the literal value of the token, if it exists. (`Variant`) +func get_literal(): + return _literal + + +## The lexeme of the token; this is typically the text corresponding to the token.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the lexeme of the token; this is typically the text corresponding to the token. (`String`) +func get_lexeme() -> String: + return _lexeme + + +## The line number of the token.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the line number of the token. (`int`) +func get_line() -> int: + return _line + + +## The path of the file containing the token.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the path of the file containing the token. (`String`) +func get_filename() -> String: + return _filename + + +func _to_string() -> String: + return ESCTokenType.get_token_type_name(_tokenType) + " " + str(_lexeme) + " " + (str(_literal) if _literal else "") diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd.uid new file mode 100644 index 0000000..ca197e8 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd.uid @@ -0,0 +1 @@ +uid://cie52sn4teqyy diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_token_type.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token_type.gd new file mode 100644 index 0000000..5c8e22f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token_type.gd @@ -0,0 +1,42 @@ +## Defines all possible token types for ASHES. +## +## Note that this way of representing enums is a holdover from Godot 3.x, and +## should be updated to the Godot 4 paradigm at some point. +class_name ESCTokenType + + +## An exhaustive list of all token types recognized by ASHES. +enum TokenType { + INDENT, DEDENT, NEWLINE, + + COLON, COMMA, DOT, LEFT_PAREN, LEFT_SQUARE, RIGHT_PAREN, RIGHT_SQUARE, + MINUS, PIPE, PLUS, SLASH, STAR, + + BANG, BANG_EQUAL, BANG_QUESTION, + EQUAL, EQUAL_EQUAL, + GREATER, GREATER_EQUAL, + LESS, LESS_EQUAL, + QUESTION, QUESTION_BANG, + + IDENTIFIER, STRING, NUMBER, + + ACTIVE, AND, BREAK, DONE, ELIF, ELSE, FALSE, GLOBAL, IF, IN, INVENTORY, IS, + NIL, NOT, OR, PASS, RETURN, STOP, TRUE, VAR, WHILE, + + EOF +} + + +## Translates a given token type into its name.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |type|`int`|TokenType enumeration value to translate to its name.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `String` value. (`String`) +static func get_token_type_name(type: int) -> String: + return TokenType.keys()[type] diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_token_type.gd.uid b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token_type.gd.uid new file mode 100644 index 0000000..8f4b046 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token_type.gd.uid @@ -0,0 +1 @@ +uid://db8rex4ue043w diff --git a/addons/escoria-core/game/core-scripts/esc/design-principles.md b/addons/escoria-core/game/core-scripts/esc/design-principles.md new file mode 100644 index 0000000..4a99c0a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/design-principles.md @@ -0,0 +1,40 @@ +# ASHES Design Principles +## Introduction +This document is meant to reflect the *current* design principles and the rationale behind the decisions made with respect to those principles. + +## Principles +### Ease of Use +Escoria is meant to be easy to use, especially for those who don't necessarily come from a strong programming background. ASHES' predecessor, ESCscript, also followed the same design goal, following a similar principle that the original SCUMM sought to embrace. + +To that end, ASHES borrows heavily from interpreted languages similar to Python and GDScript. The primary justification for this is to leverage well-established constructs and idioms from languages known for their ability to be learned by inexperienced programmers. + +Although this design principle is noble, it is at fundamental odds with other design principles such as the expressiveness and power of a language. To that end, it is possible that the "ease of use" principle may shift in the near future to take into account the increase in uptake of languages such as Python which has undoubtedly reduced the need for Escoria to remain as "basic" as possible. + +### Expressiveness/Power +ESCscript was created as a "regular language" (for a discussion on regular languages vs other types, e.g. "context-free", consult your local Google machine). As such, its expressiveness and power were limited. By moving to a context-free language (via a context-free grammar), we do away with the majority of those limitations and fall more in line with more traditional general-purpose languages, albeit with the view of remaining a domain-specific language (or "DSL"). + +This does not, however, mean that ASHES is as powerful or full-featured as Python or GDScript. In fact, ASHES is meant to act like a DSL (as stated above), meaning that is purpose-built to act on the domain of adventure/point-and-click games. To that end, wherever possible, we use the vernacular and vocabulary of the domain and limit the scope of what the language can do (partly in an attempt from giving game developers too much rope to hang themselves with). + +Currently, ASHES is meant to replace ESCscript and maintain certain conventions that are (again, currently) fundamental to Escoria. Such conventions include: + +#### Events +Events are explained in-depth in the Escoria documentation (https://docs.escoria-framework.org/en/devel/scripting/z_esc_reference.html#events). They form the backbone of the scripts used by Escoria. While they may just look like a block of ASHES code--much like an ordinary function might--they differ in the following ways: +- Events are typically called from within Escoria itself. +- Events can be called via a specific command in ASHES (`sched_event`), but as can be seen this is done in an indirect manner, different from calling a function by name directly. +- Events don't return values. + +#### Commands and Functions +"Commands" are considered to be those that are implemented in GDScript in the "commands" directory (currently `escoria-core/game/core-scripts/esc/commands`), and are called in ASHES by the same convention you would call any function. + +For example, to call the `say` command in ASHES, you can use the line: `say($player, "I am talking.")` and ASHES will bridge the call to the `say` command. + +ASHES currently has no facility to accommodate user-defined functions inside of a script itself. In other words, implementing your own function in a script file is not supported. This may be revisited in the future should there be sufficient demand. + +You can, however, create your own "command" in the directory mentioned above by extending the base command class and implementing the appropriate methods (see other command scripts in the same directory for examples). While such commands don't currently have access to what is located in the calling ASHES script, it can serve as a one-way bridge to GDScript. Any new commands implemented in this manner can be called directly from ASHES scripts without needing to include them explicitly. + +#### Blocking vs Non-blocking Commands +Commands can be either blocking or non-blocking. This isn't an issue unless you are dealing with commands that involve potentially long-running sequences, e.g. an NPC being made to walk a certain route or a camera being slowly panned. In those cases, there exist both blocking and non-blocking versions of the same command. Blocking versions by convention are suffixed with `_block` (e.g .`walk_block`) whereas non-blocking versions have no suffix (e.g. `walk`). + +Events are considered to be finished when the last statement has been executed. As such, it is worth being vigilant with the use of blocking and non-blocking commands. For example, if an event consists of a sole `walk` command that has an NPC move through a set of waypoints, the event itself will be considered finished once the `walk` command itself finishes executing. **This does not mean that once the character movement has ceased; rather, when the command that initiates the walk returns.** If you wish for the event to remain active for the duration of the movement, use its blocking equivalent, `walk_block`. + +Asynchronous code and state is difficult to manage. As such, we rely on GDScript's underlying facilities to ease that burden, and, as a result, we have chosen to put the responsibility of using blocking and non-blocking commands in the hands of the game developer. diff --git a/addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd b/addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd new file mode 100644 index 0000000..7bf4a37 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd @@ -0,0 +1,914 @@ +## Manages actions currently being carried out. +## @MANAGER +extends Resource +class_name ESCActionManager + + +## ESCPlayer resource +const ESCPlayer = preload("res://addons/escoria-core/game/core-scripts/esc_player.gd") + + +## The current action verb was changed.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal action_changed + +## Emitted when an action has been completed.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal action_finished + +## Emitted when the action input state has changed.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal action_input_state_changed + + +## States of the action input (verb, item, target):[br] +## (I) -> AWAITING_VERB_OR_ITEM -> AWAITING_ITEM -> COMPLETED -> (E)[br] +## or[br] +## (I) -> AWAITING_VERB_OR_ITEM -> AWAITING_ITEM -> AWAITING_TARGET_ITEM -> COMPLETED -> (E)[br] +## or[br] +## (I) -> AWAITING_VERB_OR_ITEM -> AWAITING_VERB -> AWAITING_VERB_CONFIRMATION -> COMPLETED -> (E) +enum ACTION_INPUT_STATE { + AWAITING_VERB_OR_ITEM, ## Initial state + AWAITING_ITEM, ## After initial state, verb is defined + AWAITING_TARGET_ITEM, ## Item defined requires combine, waiting for target + AWAITING_VERB, ## After initial state, item is defined + AWAITING_VERB_CONFIRMATION, ## Item was defined first, next verb, need verb confirmation + COMPLETED ## Final state +} + +## Actions understood by the do(...) method:[br] +## * BACKGROUND_CLICK: Object is to move from its current position.[br] +## * ITEM_LEFT_CLICK: Item has been clicked on with LMB.[br] +## * ITEM_RIGHT_CLICK: Item has been clicked on with RMB.[br] +## * TRIGGER_IN: Character has moved into a trigger area.[br] +## * TRIGGER_OUT: Character has moved out of a trigger area. +enum ACTION { + BACKGROUND_CLICK, + ITEM_LEFT_CLICK, + ITEM_RIGHT_CLICK, + TRIGGER_IN, + TRIGGER_OUT +} + + +# Basic required internal actions + +## Action (event) triggered when a character has reached a destination. +const ACTION_ARRIVED = "arrived" + +## Action (event) triggered when a scene is being exited. +const ACTION_EXIT_SCENE = "exit_scene" + +## Action (event) triggered when a character is walking. +const ACTION_WALK = "walk" + + +## Current verb used. +var current_action: String = "": set = set_current_action + +## Current tool (`ESCItem`/`ESCInventoryItem`) used. +var current_tool: ESCObject + +## Current target where the tool is being used on/with (if any). +var current_target: ESCObject + +## Current action input state. +var action_state = ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM: + set = set_action_input_state + + +## Runs a generic action.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |action|`int`|type of the action to run|yes|[br] +## |params|`Array`|Parameters for the action|no|[br] +## |can_interrupt|`bool`|if true, this command will interrupt any ongoing event before it is finished BACKGROUND_CLICK [moving_obj, target, walk_fast] ITEM_LEFT_CLICK [item, input_event] ITEM_RIGHT_CLICK [item, input_event] TRIGGER_IN [trigger_id, object_id, trigger_in_verb] TRIGGER_OUT [trigger_id, object_id, trigger_out_verb]|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func do(action: int, params: Array = [], can_interrupt: bool = false) -> void: + if escoria.current_state == escoria.GAME_STATE.DEFAULT: + match action: + ACTION.BACKGROUND_CLICK: + if can_interrupt: + escoria.event_manager.interrupt([], false) + + var walk_fast = false + if params.size() > 2: + walk_fast = true if params[2] else false + + # Check moving object. + if not escoria.object_manager.has(params[0]): + escoria.logger.error( + self, + "Walk action requested for nonexisting object: %s." + % params[0] + ) + return + + var moving_obj = escoria.object_manager.get_object(params[0]) + var target + + if params[1] is String: + if not escoria.object_manager.has(params[1]): + escoria.logger.error( + self, + "Walk action requested to nonexisting destination object: %s." + % params[1] + ) + return + + target = escoria.object_manager.get_object(params[1]) + elif params[1] is Vector2: + target = params[1] + + self.perform_walk(moving_obj, target, walk_fast) + + ACTION.ITEM_LEFT_CLICK: + if params[0] is String: + escoria.logger.info( + self, + "item_left_click on item %s." % params[0] + ) + + if can_interrupt: + escoria.event_manager.interrupt([], false) + + var item = escoria.object_manager.get_object(params[0]) + + self.perform_inputevent_on_object(item, params[1]) + + ACTION.ITEM_RIGHT_CLICK: + if params[0] is String: + escoria.logger.info( + self, + "item_right_click on item %s." % params[0] + ) + + if can_interrupt: + escoria.event_manager.interrupt([], false) + + var item = escoria.object_manager.get_object(params[0]) + + self.perform_inputevent_on_object(item, params[1]) + + ACTION.TRIGGER_IN: + var trigger_id = params[0] + var object_id = params[1] + var trigger_in_verb = params[2] + escoria.logger.info( + self, + "trigger_in on trigger %s activated by %s." % [ + trigger_id, + object_id + ] + ) + if trigger_in_verb in escoria.object_manager.get_object(trigger_id).events: + escoria.event_manager.queue_event( + escoria.object_manager.get_object(trigger_id).events[ + trigger_in_verb + ] + ) + else: + escoria.logger.info( + self, + "%s event not found for object %s so nothing to do." % \ + [trigger_in_verb, trigger_id]) + + ACTION.TRIGGER_OUT: + var trigger_id = params[0] + var object_id = params[1] + var trigger_out_verb = params[2] + escoria.logger.info( + self, + "trigger_out on trigger %s activated by %s." % [ + trigger_id, + object_id + ] + ) + if trigger_out_verb in escoria.object_manager.get_object(trigger_id).events: + escoria.event_manager.queue_event( + escoria.object_manager.get_object(trigger_id).events[ + trigger_out_verb + ] + ) + else: + escoria.logger.info( + self, + "%s event not found for object %s so nothing to do." % \ + [trigger_out_verb, trigger_id]) + + + _: + escoria.logger.warn( + self, + "Action received: '%s' with params %s." % [action, params] + ) + elif escoria.current_state == escoria.GAME_STATE.WAIT: + pass + elif escoria.current_state == escoria.GAME_STATE.LOADING: + pass + + +## Sets the current state of action input. ## Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_state|`Variant`|the action input state to set|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_action_input_state(p_state) -> void: + action_state = p_state + action_input_state_changed.emit() + + +## Sets the current action verb. ## Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |action|`String`|the action verb to set|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_current_action(action: String) -> void: + if action != current_action: + clear_current_tool() + + current_action = action + + if action_state == ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM: + set_action_input_state(ACTION_INPUT_STATE.AWAITING_ITEM) + elif action_state == ACTION_INPUT_STATE.AWAITING_VERB: + set_action_input_state(ACTION_INPUT_STATE.AWAITING_VERB_CONFIRMATION) + + action_changed.emit() + + +## Clears the current action.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func clear_current_action() -> void: + set_current_action("") + set_action_input_state(ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM) + action_changed.emit() + + +## Clears the current tool.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func clear_current_tool() -> void: + current_tool = null + current_target = null + if action_state == ACTION_INPUT_STATE.AWAITING_VERB: + set_action_input_state(ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM) + elif action_state == ACTION_INPUT_STATE.AWAITING_TARGET_ITEM: + set_action_input_state(ACTION_INPUT_STATE.AWAITING_ITEM) + + +## Checks if the specified action is valid and returns the associated event; otherwise, we see if there's a "fallback" event and use that if necessary and, if not, we return no event as there's nothing to do. or there's a reason not to run an event.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |action|`String`|Action to execute (defined in attached ESC file and in action verbs UI) eg: arrived, use, look, pickup...|yes|[br] +## |target|`ESCObject`|Target ESC object.|yes|[br] +## |combine_with|`ESCObject`|ESC object to combine with.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCGrammarStmts.Event` value. (`ESCGrammarStmts.Event`) +func _get_event_to_queue( + action: String, + target: ESCObject, + combine_with: ESCObject = null +) -> ESCGrammarStmts.Event: + + escoria.logger.info( + self, + "Checking if action '%s' on '%s' is valid..." % [action, target.global_id if target is ESCObject else target] + ) + + var event_to_return = null + + # If we're using an action which item requires to combine + if target.node is ESCItem \ + and action in target.node.combine_when_selected_action_is_in: + + # Check if object is in inventory or is not required to be in inventory to be used + if escoria.inventory_manager.inventory_has(target.global_id) or not target.node.use_from_inventory_only: + # We check the element to use on + if combine_with: + var do_combine = true + if combine_with.node is ESCItem \ + and combine_with.node.use_from_inventory_only\ + and not escoria.inventory_manager.inventory_has( + combine_with.global_id + ): + do_combine = false + + if do_combine: + var action_name = action + if combine_with.global_id: + action_name += " " + combine_with.global_id + + var combine_name = action + if combine_with.global_id: + combine_name += " " + target.global_id + + if _has_event_with_target(target.events, action_name, combine_with.global_id): + event_to_return = target.events[action_name] + + elif _has_event_with_target(combine_with.events, combine_name, target.global_id)\ + and not combine_with.node.combine_is_one_way: + + event_to_return = combine_with.events[combine_name] + else: + # Check to see if there isn't a "fallback" action to + # run before we declare this a failure. + if escoria.action_default_script \ + and escoria.action_default_script.events.has(action): + + event_to_return = escoria.action_default_script.events[action] + else: + var errors = [ + "Attempted to execute action '%s' between item %s and item %s" % [ + action, + target.global_id, + combine_with.global_id + ], + "Check that action ':%s %s' exists in the script of item '%s'" % [ + action, + target.global_id, + combine_with.global_id + ] + ] + + if combine_with.node.combine_is_one_way: + errors.append( + ("Reason: %s's item interaction " + \ + "is one-way.") % combine_with.global_id + ) + + escoria.logger.warn( + self, + "Invalid action: " + str(errors) + ) + else: + escoria.logger.warn( + self, + "Invalid action on item: " + + ( + "Trying to combine object %s with %s, "+ + "but %s is not in inventory." + ) % [ + target.global_id, + combine_with.global_id, + combine_with.global_id + ] + ) + else: + escoria.logger.warn( + self, + "Invalid action on item: " + + "Trying to run action '%s' on object %s, " % + [ + action, + target.node.global_id + ] + + "but item must be in inventory." + ) + else: + if _check_target_has_proper_action(target, action): +# ##SAVEGAME +# if target.events[action].is_completed: +# target.events[action].is_completed = false +# target.events[action].from_statement_id = 0 + event_to_return = target.events[action] + elif escoria.action_default_script \ + and escoria.action_default_script.events.has(action): + + # If there's a "fallback" action to run, return it + event_to_return = escoria.action_default_script.events[action] + else: + escoria.logger.warn( + self, + "Invalid action: " + + "Event for action '%s' on object '%s' not found." % [ + action, + target.global_id + ] + ) + + return event_to_return + + +## Check to make sure `target` contains the specific `action`. If `target` has an entry for `action` that also requires a target itself (e.g. `:use "wrench"`), then we return `false` as combinations are handled elsewhere.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target|`ESCObject`|`ESCObject` whose events we are to check to see if `action` has a corresponding event.|yes|[br] +## |action|`String`|The action to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _check_target_has_proper_action(target: ESCObject, action: String) -> bool: + if target.events.has(action): + if target.events[action].get_target(): + return false + + return true + + return false + + +## Determines whether the specified events dictionary contains an event with the specified event name and event target, e.g. :give "filled_out_form".[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |events_dict|`Dictionary`|dictionary with events to check.|yes|[br] +## |event_name|`String`|the event name to search for.|yes|[br] +## |event_target|`String`|the target for the specified event to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _has_event_with_target(events_dict: Dictionary, event_name: String, event_target: String): + var event = events_dict.get(event_name) + if event == null: + return false + return true + + +## Runs the specified event.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`Variant`|the event to be run.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func _run_event(event) -> int: + escoria.event_manager.queue_event(event) + + var event_returned = await escoria.event_manager.event_finished + + while event_returned[1] != event.get_event_name(): + event_returned = await escoria.event_manager.event_finished + + clear_current_action() + action_finished.emit() + + return event_returned[0] + + +## Makes an object walk to a destination. This can be either a 2D position or another object.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |moving_obj|`ESCObject`|Escoria object that should move toward the destination.|yes|[br] +## |destination|`Variant`|Either a `Vector2` position or an `ESCObject` whose location is targeted.|yes|[br] +## |is_fast|`bool`|Whether to use the object's fast movement speed when walking.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func perform_walk( + moving_obj: ESCObject, + destination, + is_fast: bool = false +) -> void: + # Walk to Position2D. + if destination is Vector2: + var walk_context = ESCWalkContext.new( + null, + destination, + is_fast, + true + ) + moving_obj.node.walk_to(destination, walk_context) + + # Walk to object + elif destination is ESCObject: + if destination.node: + var target_position: Vector2 + if destination.node is ESCLocation: + target_position = destination.node.global_position + else: + target_position = destination.node.get_interact_position() + + var walk_context = ESCWalkContext.new( + destination, + target_position, + is_fast, + true + ) + + moving_obj.node.walk_to(target_position, walk_context) + + else: + escoria.logger.error( + self, + "Function expected either a Vector2 or ESCObject type " + \ + "for destination parameter. Destination provided was: %s." % destination + ) + return + + +## Event handler called when an object/item is clicked.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |obj|`ESCObject`|Object that was left clicked.|yes|[br] +## |event|`InputEvent`|Input event that was received.|yes|[br] +## |default_action|`bool`|If `true`, run the inventory default action.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func perform_inputevent_on_object( + obj: ESCObject, + event: InputEvent, + default_action: bool = false +) -> void: + """ + This algorithm: + - validates the requested action + - grabs the corresponding event for the action, if available + - makes the player move to the clicked object location, if needed + (if it is located in the room for example) and wait for reaching. + - when reached, performs an action depending on current defined action + * no current action defined: do nothing else + * current action defined: + * item requires no combination: perform the current action + on the item + * item requires combination: check the status of the combination + A combination requires 3 elements to fulfill: + 1/ a verb action + 2/ a first "tool" (item to use) + 3/ a second "tool" (item to use ON) + Whatever the user inputs to fulfill the combination (this is + determined by gamedev in his game.gd script) + - combination not fulfilled: no not perform until fulfilled + - combination fulfilled: perform the combination. + * else do nothing, except if default_action is requested. + In this case, perform the default_action on the item. + """ + + escoria.logger.info( + self, + "%s to perform event %s." % [obj.global_id, event] + ) + + # Don't interact after player movement towards object + # (because object is inactive for example) + var dont_interact = false + + # We need to have the new action input state BEFORE initiating the player + # move so we determine now if the object clicked will require a combination + # depending on the used action verb. + var tool_just_set = _set_tool_and_action(obj, default_action) + var need_combine = _check_item_needs_combine() + + # If the clicked item is not in the inventory and the current tool was not set, + # then this is our first item, make it the tool. + if (not escoria.inventory_manager.inventory_has(obj.global_id) and not current_tool) \ + or (current_tool and not need_combine): + current_tool = obj + # Else, if we have a tool and combination required, this is our second item, + # make it the target. + elif need_combine and not tool_just_set: + current_target = obj + + # Update the action input state + if action_state == ACTION_INPUT_STATE.AWAITING_TARGET_ITEM and current_target: + set_action_input_state(ACTION_INPUT_STATE.COMPLETED) + elif action_state == ACTION_INPUT_STATE.AWAITING_ITEM and \ + not need_combine: + set_action_input_state(ACTION_INPUT_STATE.COMPLETED) + elif action_state == ACTION_INPUT_STATE.AWAITING_ITEM and \ + need_combine and not tool_just_set: + set_action_input_state(ACTION_INPUT_STATE.AWAITING_TARGET_ITEM) + + var event_to_queue: ESCGrammarStmts.Event = null + + + # Manage exits first, actions last + # If the clicked object is an exit and current action is "walk"/unset, we need to run the exit scene action. + if obj.node.is_exit and current_action in ["", ACTION_WALK]: + event_to_queue = _get_event_to_queue(ACTION_EXIT_SCENE, obj) + # If the clicked object is not an exit, we check if the current action is "walk"/unset + # If so, and if the object is not in the inventory, we need to run the arrived action. + elif current_action in ["", ACTION_WALK] and not escoria.inventory_manager.inventory_has(obj.global_id): + event_to_queue = _get_event_to_queue(ACTION_ARRIVED, obj) + # If the current action is set and different from "walk"/unset, we need to check for combinations. + elif not current_action in ["", ACTION_WALK]: + # If clicked object needs a combination, and current target is set, then perform the combination. + if need_combine and current_target: + event_to_queue = _get_event_to_queue(current_action, current_tool, current_target) + # If clicked object needs a combination then we need to wait for the target. + elif need_combine: + set_action_input_state(ACTION_INPUT_STATE.AWAITING_TARGET_ITEM) + # If object is in inventory make it current tool. + if escoria.inventory_manager.inventory_has(obj.global_id): + current_tool = obj + return + # If clicked object doesn't need a combination, then we simply run the action. + else: + event_to_queue = _get_event_to_queue(current_action, obj) + + # Get out of here if there's a specified action but an event couldn't be found. + # Note that `event_to_queue` may still be null, but we do need to start the + # player walking towards the destination. + if current_action and not event_to_queue: + clear_current_action() + action_finished.emit() + return + + var event_flags = event_to_queue.get_flags() if event_to_queue else 0 + + if escoria.main.current_scene.player: + var destination_position: Vector2 = escoria.main.current_scene.player \ + .global_position + + # If clicked object not in inventory, player walks towards it + if not obj.node is ESCPlayer and \ + not escoria.inventory_manager.inventory_has(obj.global_id) and \ + not _telekinetic_applies_to(event_to_queue): + var context = await _walk_towards_object( + obj, + event.position, + event.double_click + ) + + # In case of an interrupted walk, we don't want to proceed. + if context == null: + return + + destination_position = context.target_position + dont_interact = context.dont_interact_on_arrival + + var player_global_pos = escoria.main.current_scene.player.global_position + var clicked_position = event.position + + # Using this instead of is_equal_approx due to + # https://github.com/godotengine/godot/issues/65257 + if (player_global_pos - destination_position).length() > 1.0: + dont_interact = true + escoria.logger.info( + self, + "Player could not reach destination coordinates %s. " % str(destination_position) \ + + "Any requested action for %s will not fire." % obj.global_id + ) + if escoria.event_manager.EVENT_CANT_REACH in obj.events: + escoria.event_manager.queue_event(obj.events[escoria.event_manager.EVENT_CANT_REACH]) + else: + escoria.logger.info( + self, + "%s event not found for object %s so nothing to do." % \ + [escoria.event_manager.EVENT_CANT_REACH, obj.global_id] + ) + + # If no interaction should happen after player has arrived, leave + # immediately. + if not dont_interact and event_to_queue: + _run_event(event_to_queue) + + +func _telekinetic_applies_to(event: ESCGrammarStmts.Event) -> bool: + if event.get_flags_with_conditions().has("TK"): + var tk_flag_condition = event.get_flags_with_conditions().get("TK") + + if tk_flag_condition: + var interpreter: ESCInterpreter = ESCInterpreterFactory.create_interpreter() + + var result = interpreter.look_up_global(tk_flag_condition.get_name()) + + return false if result == null else bool(result) + + return true + + return false + + +## Determines whether the object in question can be acted upon.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`String`|The global ID of the item to examine.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func is_object_actionable(global_id: String) -> bool: + var obj: ESCObject = escoria.object_manager.get_object(global_id) as ESCObject + + return _is_object_actionable(obj) + + +## Prepare the "obj" object for current_action: if required, set the object as current tool.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |obj|`ESCObject`|the ESCObject to prepare.|yes|[br] +## |default_action|`bool`|if true, the default action set on the item is used.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_tool_and_action(obj: ESCObject, default_action: bool): + var tool_just_set: bool = false + # Check if current_action and current_tool are already set + if current_action and current_tool: + if not current_action in escoria.action_manager \ + .current_tool.node.combine_when_selected_action_is_in: + current_tool = obj + tool_just_set = true + elif default_action: + if escoria.inventory_manager.inventory_has(obj.global_id): + current_action = obj.node.default_action_inventory + else: + current_action = obj.node.default_action + elif current_action in obj.node.combine_when_selected_action_is_in: + current_tool = obj + tool_just_set = true + return tool_just_set + + +## Checks if object requires a combination with another, according to currently selected action verb (or check with default action of the item).[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _check_item_needs_combine() -> bool: + return current_action \ + and current_tool \ + and current_action in current_tool.node.combine_when_selected_action_is_in + + +## Makes the player character walk towards the clicked item. Returns the resulting walk context.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |obj|`ESCObject`|the object that was clicked.|yes|[br] +## |clicked_position|`Vector2`|the Position2D of the input click.|yes|[br] +## |walk_fast|`bool`|if true, the player will walk fast to the object.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCWalkContext` value. (`ESCWalkContext`) +func _walk_towards_object( + obj: ESCObject, + clicked_position: Vector2, + walk_fast: bool +) -> ESCWalkContext: + var destination_position: Vector2 + var dont_interact: bool = false + + if obj == null || obj.node == null: + escoria.logger.error( + self, + "walk_towards_object error. obj or obj.node not populated." + ) + var interact_position = obj.node.get_interact_position() + # If clicked object is interactive, get destination position from it. + if escoria.object_manager.get_object(obj.global_id).interactive: + if interact_position != null: + destination_position = interact_position + else: + destination_position = obj.node.position + else: + destination_position = clicked_position + dont_interact = true + + # Create walk context + var walk_context = ESCWalkContext.new( + obj, + destination_position, + walk_fast, + dont_interact + ) + + # Walk towards the clicked object + escoria.main.current_scene.player.walk_to(destination_position, + walk_context) + + escoria.logger.debug( + self, + "Player walking to destination. Yielding." +) + + # Wait for the player to arrive before continuing with action. + var context: ESCWalkContext = await escoria.main.current_scene.player.arrived + + if context.target_object != obj: + escoria.logger.debug( + self, + "Original walk context target does not match " \ + + "yielded walk context. Likely interrutped walk.") + return + + escoria.logger.info( + self, + "Context arrived: %s." % context + ) + + # Confirm that reached item was the one user clicked in the first place. + # Don't interact if that is not the case. + if (context.target_object and context.target_object.\ + global_id != walk_context.\ + target_object.global_id) or \ + (context.target_position != walk_context.target_position): + walk_context.dont_interact_on_arrival = true + + return context + + +## Determines whether the object in question can be acted upon.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |obj|`ESCObject`|the ESCObject to examine.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _is_object_actionable(obj: ESCObject) -> bool: + var object_is_actionable: bool = true + + if not obj: + return false + + if not obj.active: + escoria.logger.trace( + self, + "Item %s is not active." % obj.global_id + ) + object_is_actionable = false + elif not obj.interactive: + escoria.logger.trace( + self, + "Item %s is not interactive." % obj.global_id + ) + object_is_actionable = false + + return object_is_actionable diff --git a/addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd.uid b/addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd.uid new file mode 100644 index 0000000..977a937 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd.uid @@ -0,0 +1 @@ +uid://bcyr2cntloadi diff --git a/addons/escoria-core/game/core-scripts/esc/esc_command_registry.gd b/addons/escoria-core/game/core-scripts/esc/esc_command_registry.gd new file mode 100644 index 0000000..fdea1ec --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_command_registry.gd @@ -0,0 +1,58 @@ +## A registry of ASHES command objects. +extends RefCounted +class_name ESCCommandRegistry + + +## The registry of registered commands. +var registry: Dictionary = {} + + +## Load a command by its name.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_name|`String`|Name of command to load.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCBaseCommand` value. (`ESCBaseCommand`) +func load_command(command_name: String) -> ESCBaseCommand: + for command_directory in ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.COMMAND_DIRECTORIES + ): + if ResourceLoader.exists("%s/%s.gd" % [command_directory, command_name]): + registry[command_name] = load( + "%s/%s.gd" % [ + command_directory.trim_suffix("/"), + command_name + ] + ).new() + return registry[command_name] + + escoria.logger.error( + self, + "No command class could be found for command %s." + % command_name + ) + + return null + + +## Retrieves a command from the command registry.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_name|`String`|The name of the command.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCBaseCommand` value. (`ESCBaseCommand`) +func is_command_or_control_pressed(command_name: String) -> ESCBaseCommand: + if self.registry.has(command_name): + return self.registry[command_name] + else: + return self.load_command(command_name) diff --git a/addons/escoria-core/game/core-scripts/esc/esc_command_registry.gd.uid b/addons/escoria-core/game/core-scripts/esc/esc_command_registry.gd.uid new file mode 100644 index 0000000..3ed524e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_command_registry.gd.uid @@ -0,0 +1 @@ +uid://v1bk0jsvowgk diff --git a/addons/escoria-core/game/core-scripts/esc/esc_compiler.gd b/addons/escoria-core/game/core-scripts/esc/esc_compiler.gd new file mode 100644 index 0000000..c029f47 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_compiler.gd @@ -0,0 +1,204 @@ +## Compiler for the ASHES language. +extends Resource +class_name ESCCompiler + + +## This must match `ESCProjectSettingsManager.COMMAND_DIRECTORIES`. +## We do not reference it directly in order to avoid circular dependencies. +const COMMAND_DIRECTORIES = "escoria/main/command_directories" + + +## Whether an error has been encountered during the compilation process. +var had_error: bool = false + +## Initialize the ESCCompiler and assure command list preference.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(): + # Assure command list preference + # (we use ProjectSettings instead of ESCProjectSettingsManager + # here because this is called from escoria._init()) + if not ProjectSettings.has_setting(COMMAND_DIRECTORIES): + ProjectSettings.set_setting(COMMAND_DIRECTORIES, [ + "res://addons/escoria-core/game/core-scripts/esc/commands" + ]) + var property_info = { + "name": COMMAND_DIRECTORIES, + "type": TYPE_PACKED_STRING_ARRAY + } + ProjectSettings.add_property_info(property_info) + + +## Static method to pre-load all ASHES commands from `ESCProjectSettingsManager.COMMAND_DIRECTORIES`. All commands must extend `ESCBaseCommand`.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Array` value. (`Array`) +static func load_commands() -> Array: + var commands: Array = [] + + for command_directory in ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.COMMAND_DIRECTORIES + ): + var dir = DirAccess.open(command_directory) + dir.list_dir_begin() + + var file_name = dir.get_next() + + while file_name: + if ResourceLoader.exists("%s/%s" % [command_directory, file_name]): + commands.append(load( + "%s/%s" % [ + command_directory.trim_suffix("/"), + file_name + ] + ).new()) + + file_name = dir.get_next() + + return commands + + +## Static method to load all Escoria reserved objects and reserved globals.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` value. (`Dictionary`) +static func load_globals() -> Dictionary: + var globals: Dictionary = {} + + for obj in ESCObjectManager.RESERVED_OBJECTS: + globals[obj] = obj + + for obj in ESCRoomManager.RESERVED_GLOBALS: + globals[obj] = ESCRoomManager.RESERVED_GLOBALS[obj] + + return globals + + +## Compile the given ESC source code into an ESCScript object.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |source|`String`|The ESC source code to compile.|yes|[br] +## |filename|`String`|Optional filename for error reporting.|no|[br] +## |associated_global_id|`String`|Optional global id associated with the script.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns an `ESCScript` object representing the compiled script. +func _compiler_shim(source: String, filename: String = "", associated_global_id: String = ""): + var scanner: ESCScanner = ESCScanner.new() + scanner.set_source(source) + scanner.set_filename(filename) + had_error = false + + var tokens = scanner.scan_tokens() + + var parser: ESCParser = ESCParser.new() + parser.init(self, tokens, associated_global_id) + + var parsed_statements = parser.parse() + + if not filename.is_empty(): + var error_message: String = ("Error(s) found during parsing" if had_error else "No errors found during parsing") + ("" if filename.is_empty() else " of script '%s'" % filename) + + ESCSafeLogging.log_result(self, error_message, not had_error) + + # Some static analysis + if not had_error and _run_script_analysis(): + var resolver: ESCResolver = ESCResolver.new(ESCInterpreterFactory.create_interpreter()) + resolver.resolve(parsed_statements) + + if not filename.is_empty(): + ESCSafeLogging.log_info(self, "Analyzing '%s'..." % filename) + + var static_analyzers = ESCStaticAnalyzers.new(parsed_statements) + static_analyzers.run() + + ESCSafeLogging.log_info(self, "Finished analyzing '%s'." % filename) + + var script = ESCScript.new() + + script.filename = filename + + if not had_error: + for ps in parsed_statements: + var event_name = ps.get_event_name() + if ps.get_target() != null: + event_name += " " + ps.get_target_name() + script.events[event_name] = ps + return script + + +## Load an ESC file from a file resource. We also accept an optional global ID of whatever object is associated with the ESC file. Note that we don't need to do the same for a room-attached script since the current room's global_id is always available as an Escoria global.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |path|`String`|the path of the script file to load|yes|[br] +## |associated_global_id|`String`|global ID of the object/room associated with the script file, if any (may be empty)|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCScript` value. (`ESCScript`) +func load_esc_file(path: String, associated_global_id: String = "") -> ESCScript: + ESCSafeLogging.log_debug(self, "Loading file '%s' for parsing..." % path) + + if not FileAccess.file_exists(path): + ESCSafeLogging.log_error(self, "Unable to find ESC file: '%s' could not be found." % path) + + return null + + var file = FileAccess.open(path, FileAccess.READ) + + return _compiler_shim(file.get_as_text(), path, associated_global_id) + + +## Compiles the passed-in script. TODO: `path` is extraneous and left in for legacy purposes; at some point, this will be removed which will require updating other methods that reference this one to ensure they don't pass in a second argument.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |script|`String`|A `String` containing the entirety of the script to be compiled.|yes|[br] +## |path|`String`|NOT USED.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCScript` value. (`ESCScript`) +func compile(script: String, path: String = "") -> ESCScript: + return _compiler_shim(script) + + +## True if this is being called in-editor or the appropriate project setting is enabled.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns true if this is being called in-editor or the appropriate project setting is enabled. (`bool`) +func _run_script_analysis() -> bool: + if Engine.is_editor_hint(): + return true + + return ProjectSettings.get_setting(ESCProjectSettingsManager.PERFORM_SCRIPT_ANALYSIS_AT_RUNTIME) diff --git a/addons/escoria-core/game/core-scripts/esc/esc_compiler.gd.uid b/addons/escoria-core/game/core-scripts/esc/esc_compiler.gd.uid new file mode 100644 index 0000000..e533163 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_compiler.gd.uid @@ -0,0 +1 @@ +uid://d17to8l8bp04y diff --git a/addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd b/addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd new file mode 100644 index 0000000..47fda29 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd @@ -0,0 +1,752 @@ +## Manager for running events. +## +## There are different "channels" an event can run on. The usual events happen in +## the foreground channel _front, but additional event queues can be added as required. +## Additionally, events can be scheduled to be queued in the future. +## @MANAGER +extends Node +class_name ESCEventManager + + +## Emitted when the event has begun execution.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event_name|`Variant`|Name of the event whose execution has started.|yes|[br] +## [br] +signal event_started(event_name) + +## Emitted when an event is started in a channel of the background queue.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |channel_name|`Variant`|Name of the background channel where the event runs.|yes|[br] +## |event_name|`Variant`|Name of the event that began on the background channel.|yes|[br] +## [br] +signal background_event_started(channel_name, event_name) + +## Emitted when the event has finished running.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |return_code|`Variant`|Execution result returned by the event.|yes|[br] +## |event_name|`Variant`|Name of the event that just finished.|yes|[br] +## [br] +signal event_finished(return_code, event_name) + +## Emitted when a background event has finished.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |return_code|`Variant`|Execution result returned by the background event.|yes|[br] +## |event_name|`Variant`|Name of the background event that finished.|yes|[br] +## |channel_name|`Variant`|Background channel where the event finished.|yes|[br] +## [br] +signal background_event_finished(return_code, event_name, channel_name) + + +# Pre-defined ASHES events. + +## Used for local processing. +const EVENT_PRINT = "print" + +## Used for local processing. +const EVENT_EXIT_SCENE = "exit_scene" + +## Used for local processing. +const EVENT_INIT = "init" + +## Used for local processing. +const EVENT_LOAD = "load" + +## Used for local processing. +const EVENT_RESUME = "resume" + +## Used for local processing. +const EVENT_NEW_GAME = "newgame" + +## Used for local processing. +const EVENT_READY = "ready" + +## Used for local processing. +const EVENT_SETUP = "setup" + +## Used for local processing. +const EVENT_TRANSITION_IN = "transition_in" + +## Used for local processing. +const EVENT_TRANSITION_OUT = "transition_out" + +## Used for local processing. +const EVENT_CANT_REACH = "cant_reach" + +## Events that MUST exist in order for Escoria to run properly. +const REQUIRED_EVENTS = [ + EVENT_INIT, + EVENT_NEW_GAME +] + +# Event channel names. + +## The primary channel for running events. +const CHANNEL_FRONT = "_front" + + +## A list of currently scheduled events. +var scheduled_events: Array = [] + +## A list of constantly running events, possibly in multiple background channels. +var events_queue: Dictionary = { + CHANNEL_FRONT: [] +} + +## Currently running event in background channels. +var _running_events: Dictionary = {} + +## Those commands that are currently running per channel. +var _running_commands: Dictionary = {} + +## Channel currently being processed. +var _current_channel: String = "" + +## Whether an event can be played on a specific channel. +var _channels_state: Dictionary = {} + +## Whether we're currently waiting for an async event to complete, per channel. +var _yielding: Dictionary = {} + +## Whether we're currently changing the scene. +var _changing_scene: bool = false: set = set_changing_scene + +## ESC "change_scene" commands. +var _change_scene: ChangeSceneCommand +var _change_scene_godot: ChangeSceneGodotCommand + + +## Constructor.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(): + _change_scene = ChangeSceneCommand.new() + _change_scene_godot = ChangeSceneGodotCommand.new() + + +## Make sure to stop when pausing the game.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + name = "event_manager" + self.process_mode = Node.PROCESS_MODE_PAUSABLE + + +## Handle the events queue and scheduled events.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |delta|`float`|Time passed since the last process call.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _process(delta: float) -> void: + var channel_yielding: bool + + for channel_name in events_queue.keys(): + channel_yielding = _yielding.get(channel_name, false) + + if events_queue[channel_name].size() == 0 or channel_yielding: + continue + if is_channel_free(channel_name): + _current_channel = channel_name + _channels_state[channel_name] = false + _running_events[channel_name] = \ + events_queue[channel_name].pop_front() + escoria.logger.debug( + self, + "Popping event '%s' from background queue %s " % [ + _running_events[channel_name].get_event_name(), + channel_name, + ] + + "to source %s." % _running_events[channel_name].source \ + if not _running_events[channel_name].source.is_empty() + else "(unknown)" + ) + if not _running_events[channel_name].is_connected( + "finished", _on_event_finished + ): + _running_events[channel_name].finished.connect( + _on_event_finished.bind(channel_name), + CONNECT_ONE_SHOT + ) + if not _running_events[channel_name].is_connected( + "interrupted", _on_event_finished + ): + _running_events[channel_name].interrupted.connect( + _on_event_finished.bind(channel_name), + CONNECT_ONE_SHOT + ) + + if channel_name == CHANNEL_FRONT: + event_started.emit( + _running_events[channel_name].get_event_name() + ) + else: + background_event_started.emit( + channel_name, + _running_events[channel_name].get_event_name() + ) + + var event_flags = _running_events[channel_name].get_flags() + if event_flags & ESCEvent.FLAGS.NO_TT: + escoria.main.current_scene.game.tooltip_node.hide() + + if event_flags & ESCEvent.FLAGS.NO_UI: + escoria.main.current_scene.game.hide_ui() + + if event_flags & ESCEvent.FLAGS.NO_SAVE: + escoria.save_manager.save_enabled = false + + #var rc = _running_events[channel_name].run() + #escoria.interpreter.reset() + #var resolver: ESCResolver = ESCResolver.new(escoria.interpreter) + var interpreter: ESCInterpreter = ESCInterpreterFactory.create_interpreter() + var resolver: ESCResolver = ESCResolver.new(interpreter) + var event = _running_events[channel_name] + + escoria.logger.trace( + self, + "Processing event '%s' from script '%s'." \ + % [ + event.get_name().get_lexeme(), + event.get_name().get_filename() if event.get_name().get_filename() else "" + ] + ) + + resolver.resolve(event) + + var rc = await interpreter.interpret(event) + + #if rc is GDScriptFunctionState: + #_yielding[channel_name] = true + #rc = await (rc, "completed") + #_yielding[channel_name] = false + + for event in self.scheduled_events: + (event as ESCScheduledEvent).timeout -= delta + if (event as ESCScheduledEvent).timeout <= 0: + self.scheduled_events.erase(event) + self.events_queue[CHANNEL_FRONT].append(event.event) + + +## Queues a new event based on input from an ASHES command, most likely `queue_event`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |script_object|`ESCScript`|Compiled script object, i.e. the one with the event to queue.|yes|[br] +## |event|`String`|Name of the event to queue.|yes|[br] +## |channel|`String`|Channel to run the event on (default: `_front`).|yes|[br] +## |block|`bool`|Whether to wait for the queue to finish. This is only possible, if the queued event is not to be run on the same event as this command (default: `false`).|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func queue_event_from_esc(script_object: ESCScript, event: String, + channel: String, block: bool) -> int: + + if _changing_scene: + return ESCExecution.RC_WONT_QUEUE + + if channel == CHANNEL_FRONT: + queue_event(script_object.events[event]) + else: + queue_background_event( + channel, + script_object.events[event] + ) + if block: + if channel == CHANNEL_FRONT: + var rc = await self.event_finished + while rc[1] != event: + rc = await self.event_finished + return rc[0] + else: + var rc = await self.background_event_finished + while rc[1] != event and rc[2] != channel: + rc = await self.background_event_finished + return rc[0] + + return ESCExecution.RC_OK + + +## Queues a new event to run in the foreground.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`ESCGrammarStmts.Event`|The event to run.|yes|[br] +## |force|`bool`|(optional) Events won't normally queue during scene changes. This parameter overrides that behaviour.|no|[br] +## |as_first|`Variant`|(optional) Put the event at the head of the queue.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func queue_event(event: ESCGrammarStmts.Event, force: bool = false, as_first = false) -> void: + if _changing_scene and not force: + escoria.logger.info( + self, + "Changing scenes. Won't queue event '%s'." % event.get_event_name() + ) + return + + # Don't queue the same event more than once in a row. + var last_event = _get_last_event_queued(CHANNEL_FRONT) + + # Check the queue first to see if appending the event will result in + # consecutive occurrences of the event. If not, be sure to check if the same + # event is currently running. + if last_event != null and last_event.get_event_name() == event.get_event_name(): + var message = "Event '%s' is already the most-recently queued event in channel '%s'." + \ + " Won't be queued again." + + escoria.logger.debug(self, message % [event.get_event_name(), CHANNEL_FRONT]) + return + elif _is_event_running(event, CHANNEL_FRONT): + # Don't queue the same event if it's already running. + escoria.logger.debug( + self, + "Event %s already running in channel '%s'. Won't be queued." + % [event.get_event_name(), CHANNEL_FRONT] + ) + + return + + escoria.logger.debug( + self, + "Queueing event %s in channel %s." % [event.get_event_name(), CHANNEL_FRONT] + ) + if as_first: + self.events_queue[CHANNEL_FRONT].push_front(event) + else: + self.events_queue[CHANNEL_FRONT].append(event) + + +## Schedules an event to run after a timeout.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`ESCGrammarStmts.Event`|The event to run.|yes|[br] +## |timeout|`float`|Number of seconds to wait before adding the event to the front queue.|yes|[br] +## |object|`String`|The target object.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func schedule_event(event: ESCGrammarStmts.Event, timeout: float, object: String) -> void: + scheduled_events.append(ESCScheduledEvent.new(event, timeout, object)) + + +## Queues an event to run in a background channel.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |channel_name|`String`|The name of the channel to use.|yes|[br] +## |event|`ESCGrammarStmts.Event`|The event to run; must be of type `ESCGrammarEvents.Event`.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func queue_background_event(channel_name: String, event: ESCGrammarStmts.Event) -> void: + if not channel_name in events_queue: + events_queue[channel_name] = [] + + # Don't queue the same event more than once in a row. + var last_event = _get_last_event_queued(channel_name) + + # Check the queue first to see if appending the event will result in + # consecutive occurrences of the event. If not, be sure to check if the same + # event is currently running. + if last_event != null and last_event.get_event_name() == event.get_event_name(): + var message = "Event '%s' is already the most-recently queued event in channel '%s'." + \ + " Won't be queued again." + + escoria.logger.debug(self, message % [event.get_event_name(), channel_name]) + return + elif _is_event_running(event, CHANNEL_FRONT): + # Don't queue the same event if it's already running. + escoria.logger.debug( + self, + "Event %s already running in channel '%s'. Won't be queued." + % [event.get_event_name(), channel_name] + ) + + return + + events_queue[channel_name].append(event) + + +## Interrupts the events currently running and any that are pending.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |exceptions|`PackedStringArray`|An optional list of events which should be left running or queued.|no|[br] +## |stop_walking|`Variant`|boolean value (default true) determining whether the player (if any) has to be interrupted walking or not.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(exceptions: PackedStringArray = [], stop_walking = true) -> void: + if stop_walking \ + and escoria.main.current_scene != null \ + and escoria.main.current_scene.player != null \ + and escoria.main.current_scene.player.is_moving(): + escoria.main.current_scene.player.stop_walking_now() + + for channel_name in _running_events.keys(): + if _running_events[channel_name] != null and not _running_events[channel_name].get_event_name() in exceptions: + escoria.logger.debug( + self, + "Interrupting running event %s in channel %s..." + % [_running_events[channel_name].get_event_name(), channel_name]) + _running_events[channel_name].interrupt() + _channels_state[channel_name] = true + + var events_to_clear: Array = [] + + for channel_name in events_queue.keys(): + if events_queue[channel_name] != null: + var found_exception: bool = false + + for event in events_queue[channel_name]: + if event.get_event_name() in exceptions: + found_exception = true + continue + + escoria.logger.debug( + self, + "Interrupting queued event %s in channel %s..." + % [event.get_event_name(), channel_name]) + #event.interrupt() # Is this even needed if the event hasn't started and we're just going to remove it from the queue? + events_to_clear.append(event) + + # If we found an exception, we can't just clear out the entire + # channel's queue and so we remove everything but the exceptions in + # the channel. Otherwise, we're safe to just clear it out. + if found_exception: + for event in events_to_clear: + if events_queue[channel_name].has(event): + events_queue[channel_name].erase(event) + else: + events_queue[channel_name].clear() + + +## Interrupts any events in the specified channel.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |channel_name|`String`|The name of the channel containing the events to be interrupted.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt_channel(channel_name: String) -> void: + for command in _running_commands.get(channel_name, []): + command.interrupt() + + +## Clears the event queues.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func clear_event_queue() -> void: + for channel_name in events_queue.keys(): + events_queue[channel_name].clear() + + +## Checks whether a channel is free to run more events.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Name of the channel to inspect.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func is_channel_free(name: String) -> bool: + return _channels_state[name] if name in _channels_state else true + + +## Gets the currently running event in a channel. `null` if there is none.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Name of the channel whose running event should be returned.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCGrammarStmts.Event` value. (`ESCGrammarStmts.Event`) +func get_running_event(name: String) -> ESCGrammarStmts.Event: + return _running_events[name] if name in _running_events else null + + +## Setter for _changing_scene.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_is_changing_scene|`bool`|New value indicating whether a scene transition is currently running.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_changing_scene(p_is_changing_scene: bool) -> void: + escoria.logger.trace( + self, + "Setting _changing_scene to %s." % p_is_changing_scene + ) + + _changing_scene = p_is_changing_scene + + # If we're changing scenes, interrupt any (other) running events and purge + # all event queues. + if _changing_scene: + interrupt([ + EVENT_INIT, + EVENT_EXIT_SCENE, + EVENT_NEW_GAME, + EVENT_LOAD, + EVENT_RESUME, + _change_scene.get_command_name(), + _change_scene_godot.get_command_name() + ]) + + +# This probably won't work since _current_channel could have changed after +# a yielding command resumes. Also the event itself isn't logged, just the command, +# creating a problem. + +## Adds a currently-running command to the current channel.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command|`ESCCommand`|The `ESCCommand` to be added to the current channel.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func add_running_command(command: ESCCommand) -> void: + if _running_commands.get(_current_channel, []) == []: + _running_commands[_current_channel] = [command] + else: + _running_commands[_current_channel].append(command) + + +## Removes the specified command from the current channel.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command|`ESCCommand`|The `ESCCommand` to be removed from the current channel.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func running_command_finished(command: ESCCommand) -> void: + if command in _running_commands[_current_channel]: + _running_commands[_current_channel].erase(command) + + +## The event finished running.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |finished_event|`Variant`|statement object representing the event that finished.|yes|[br] +## |finished_statement|`Variant`|statement object representing the "deepest" statement (most likely a command) that just completed; this is useful for interrupted or failed statements especially.|yes|[br] +## |return_code|`int`|Return code of the finished event.|yes|[br] +## |channel_name|`String`|Name of the channel that the event came from.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_event_finished(finished_event, finished_statement, return_code: int, channel_name: String) -> void: + var event = _running_events[channel_name] + if not event: + escoria.logger.warn( + self, + "Event '%s' finished without being in _running_events[%s]." + % [finished_event.get_event_name(), channel_name] + ) + return + + escoria.logger.debug( + self, + "Event '%s' ended with return code %d." % [event.get_event_name(), return_code] + ) + + var event_flags = event.get_flags() + if event_flags & ESCEvent.FLAGS.NO_TT: + escoria.main.current_scene.game.tooltip_node.show() + + if event_flags & ESCEvent.FLAGS.NO_UI: + escoria.main.current_scene.game.show_ui() + + if event_flags & ESCEvent.FLAGS.NO_SAVE: + escoria.save_manager.save_enabled = true + + # If the return code was RC_CANCEL due to an event finishing with "stop" command for example + # we convert it to RC_OK so that other processed waiting for RC_OK can carry on. + # + # We also make sure that a failed command/event doesn't leave the game in a state where it + # isn't accepting inputs, e.g. if a previous command in the event was `accept_input NONE`. + if return_code == ESCExecution.RC_CANCEL: + return_code = ESCExecution.RC_OK + elif return_code == ESCExecution.RC_ERROR: + _generate_statement_error_warning(finished_statement, event.get_event_name()) + + escoria.inputs_manager.input_mode = escoria.inputs_manager.INPUT_ALL + + _running_events[channel_name] = null + _channels_state[channel_name] = true + + if channel_name == CHANNEL_FRONT: + event_finished.emit( + return_code, + event.get_event_name() + ) + else: + background_event_finished.emit( + return_code, + event.get_event_name(), + channel_name + ) + + +## Gets the event at the tail of the specified channel's event queue, if one exists.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |channel_name|`String`|The name of the channel to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _get_last_event_queued(channel_name: String): + if self.events_queue[channel_name].size() > 0: + return self.events_queue[channel_name].back() + + return null + + +## Checks to see if the specified event is already running in the given channel.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`ESCGrammarStmts.Event`|The event to check to see if it's already running.|yes|[br] +## |channel_name|`String`|The name of the channel to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _is_event_running(event: ESCGrammarStmts.Event, channel_name: String) -> bool: + var running_event: ESCGrammarStmts.Event = get_running_event(channel_name) + + return running_event != null and running_event.get_event_name() == event.get_event_name() + + +## Generates a logger warning concerning an errored-out statement.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |statement|`ESCStatement`|The statement that returned an error.|yes|[br] +## |event_name|`String`|The name of the event in which the error occurred.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _generate_statement_error_warning(statement: ESCStatement, event_name: String) -> void: + var warning_string: String = "Statement '%s' returned an error in event '%s'" \ + % [statement.get_name(), event_name] + + if statement is ESCCommand and statement.parameters.size() > 0: + var statement_params: String = "[" + ", ".join(PackedStringArray(statement.parameters)) + "]" + + warning_string += " with parameters: %s" % statement_params + + warning_string += ". Resetting input mode to 'ALL'." + + escoria.logger.warn( + self, + warning_string + ) + + +## Save the running event in the savegame, if any.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_savegame|`ESCSaveGame`|`ESCSaveGame` resource that holds all save data.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func save_game(p_savegame: ESCSaveGame) -> void: + # Scheduled events + var sched_events_array: Array = [] + for sched_event in scheduled_events: + sched_events_array.push_back((sched_event as ESCScheduledEvent).exported()) + p_savegame.events.sched_events = sched_events_array diff --git a/addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd.uid b/addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd.uid new file mode 100644 index 0000000..59b6f56 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd.uid @@ -0,0 +1 @@ +uid://b8ulrsu8kot3w diff --git a/addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd b/addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd new file mode 100644 index 0000000..61c1e38 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd @@ -0,0 +1,228 @@ +## A resource that manages the ASHES global states. +## +## The ASHES global state is basically simply a dictionary of keys with +## values. Values can be bool, integer or strings. +## @MANAGER +extends Resource +class_name ESCGlobalsManager + + +## Emitted when a global has changed.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global|`Variant`|Key of the global that changed.|yes|[br] +## |old_value|`Variant`|Value stored under the key before the change.|yes|[br] +## |new_value|`Variant`|Updated value stored for the key.|yes|[br] +## [br] +signal global_changed(global, old_value, new_value) + + +# The globals registry. +@export var _globals: Dictionary = {} + +## Registry of globals that are to be reserved for internal use only. +var _reserved_globals: Dictionary = {} + +# Use look-ahead/behind to capture the term in braces +var _globals_regex: RegEx = RegEx.new() + +## Constructor.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(): + _globals_regex.compile("(?<=\\{)(.*)(?=\\})") + + +## Checks whether a global has already been registered.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |key|`String`|The global key to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func has(key: String) -> bool: + return _globals.has(key) + + +## Clears all globals.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func clear() -> void: + _globals.clear() + if (escoria.inventory == null): + escoria.logger.error( + self, + "The escoria.inventory property is null." + + "Please verify that the inventory scene (inheriting ESCInventory)" + + " main script's _ready() function calls super._ready()." + ) + escoria.inventory.clear() + + +## Registers a global as being reserved and initializes it.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |key|`String`|The key of the global to register.|yes|[br] +## |value|`Variant`|The key's initial value (optional).|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_reserved_global(key: String, value = null) -> void: + if key in _reserved_globals: + escoria.logger.error( + self, + "Can not override reserved global: Global key %s is already " + + "registered as reserved." + % key + ) + var old_value = _globals[key] if _globals.has(key) else "" + _reserved_globals[key] = value + _globals[key] = value + + if value != null: + global_changed.emit(key, old_value, _globals[key]) + + +## Retrieves the current value of a global.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |key|`String`|The key of the global to return the value.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Variant` value representing the global's value, or `null` if not found. (`Variant`) +func get_global(key: String): + if _globals.has(key): + return _globals[key] + return null + + +## Filters the globals and return all matching keys and their values as a dictionary. Check out [the Godot docs](https://docs.godotengine.org/en/stable/classes/class_string.html#class-string-method-match) for the pattern format.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pattern|`String`|The pattern that the keys have to match.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` value. (`Dictionary`) +func filter(pattern: String) -> Dictionary: + var ret = {} + for global_key in _globals.keys(): + if global_key.match(pattern): + ret[global_key] = _globals[global_key] + return ret + + +## Sets the value of a global.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |key|`String`|The key of the global to modify.|yes|[br] +## |value|`Variant`|The new value to be stored with the key.|yes|[br] +## |ignore_reserved|`bool`|If `true`, allows overriding globals marked as reserved.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_global(key: String, value, ignore_reserved: bool = false) -> void: + if key in _reserved_globals and not ignore_reserved: + escoria.logger.error( + self, + "Global key %s is reserved and can not be overridden." % key + ) + + global_changed.emit( + key, + _globals[key] if _globals.has(key) else null, + value + ) + _globals[key] = value + + +## Sets all globals that match the pattern to the value. Check out [the Godot docs](https://docs.godotengine.org/en/stable/classes/class_string.html#class-string-method-match) for the pattern format.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pattern|`String`|The wildcard pattern to match.|yes|[br] +## |value|`Variant`|The new value to be stored with the key.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_global_wildcard(pattern: String, value) -> void: + for global_key in _globals.keys(): + if global_key.match(pattern): + self.set_global(global_key, value) + + +## Replaces any globals whose names are specified in braces with their respective values (i.e. performs string interpolation). values.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |string|`String`|The text in which globals in braces are to be substituted.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `String` value. (`String`) +func replace_globals(string: String) -> String: + for result in _globals_regex.search_all(string): + var globresult = escoria.globals_manager.get_global( + str(result.get_string()) + ) + string = string.replace( + "{" + result.get_string() + "}", str(globresult) + ) + return string + + +## Saves the state of globals in the savegame.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_savegame|`ESCSaveGame`|`ESCSaveGame` resource that holds all save data.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func save_game(p_savegame: ESCSaveGame) -> void: + p_savegame.globals = {} + for g in _globals: + if not g.begins_with("i/"): + p_savegame.globals[g] = _globals[g] diff --git a/addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd.uid b/addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd.uid new file mode 100644 index 0000000..316a254 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd.uid @@ -0,0 +1 @@ +uid://dye7jhy2l2wbt diff --git a/addons/escoria-core/game/core-scripts/esc/esc_inventory_manager.gd b/addons/escoria-core/game/core-scripts/esc/esc_inventory_manager.gd new file mode 100644 index 0000000..944aead --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_inventory_manager.gd @@ -0,0 +1,93 @@ +## A manager for inventory objects. +## @MANAGER +extends Resource +class_name ESCInventoryManager + + +## Checks whether the player has an inventory item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`String`|The inventory item's id.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func inventory_has(item: String) -> bool: + if not escoria.globals_manager.has("i/%s" % item): + return false + return escoria.globals_manager.get_global("i/%s" % item) + + +## Retrieves all inventory items.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Array` value. (`Array`) +func items_in_inventory() -> Array: + var items = [] + var filtered = escoria.globals_manager.filter("i/*") + for glob in filtered.keys(): + if filtered[glob]: + items.append(glob.rsplit("i/", false)[0]) + return items + + +## Removes an item from the player's inventory.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`String`|The inventory item's id.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func remove_item(item: String) -> void: + if not inventory_has(item): + escoria.logger.error( + self, + "Error removing inventory item: " + + "Trying to remove non-existent item %s." % item + ) + else: + escoria.globals_manager.set_global("i/%s" % item, false) + + +## Adds an item to the player's inventory.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`String`|The inventory item's id.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func add_item(item: String) -> void: + escoria.globals_manager.set_global("i/%s" % item, true) + + +## Saves the inventory in the specified savegame.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_savegame|`ESCSaveGame`|ESCSaveGame resource that holds all save data.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func save_game(p_savegame: ESCSaveGame) -> void: + p_savegame.inventory = [] + for item in items_in_inventory(): + p_savegame.inventory.append(item) diff --git a/addons/escoria-core/game/core-scripts/esc/esc_inventory_manager.gd.uid b/addons/escoria-core/game/core-scripts/esc/esc_inventory_manager.gd.uid new file mode 100644 index 0000000..a158b4d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_inventory_manager.gd.uid @@ -0,0 +1 @@ +uid://trmctj4lnahh diff --git a/addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd b/addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd new file mode 100644 index 0000000..047fcab --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd @@ -0,0 +1,703 @@ +## A manager for ESC objects. +## @MANAGER +extends Resource +class_name ESCObjectManager + + +## Reserved camera object. +const CAMERA = "_camera" + +## Reserved music player object. +const MUSIC = "_music" + +## Reserved sound player object. +const SOUND = "_sound" + +## Reserved speech player object. +const SPEECH = "_speech" + +## Reserved ambient player object. +const AMBIENT = "_ambient" + +## Array of objects that are reserved and automatically created when Escoria starts up. +const RESERVED_OBJECTS = [ + MUSIC, + SOUND, + SPEECH, + AMBIENT, +] + + +## The array of registered objects (organized by room, so each entry is a structure +## representing a room and its registered objects). This also includes one +## "room" for reserved objects; that is, we use one entry of the array to +## hold all reserved objects. This entry can be identified by the "is_reserved" +## property being set to true.[br] +##[br] +## "Reserved objects" are those which are named in the RESERVED_OBJECTS const +## array and include objects that are used internally by Escoria in every room, +## e.g. a music player, a sound player, a speech player, the main camera.[br] +##[br] +## In almost all cases, the reserved objects' entry doesn't need updating once +## created.[br] +##[br] +## Example structure:[br] +##[br] +## [[br] +## {[br] +## is_reserved: true, # Indicates this is the "reserved objects" entry[br] +## room: "",[br] +## room_instance_id: "",[br] +## objects:[br] +## {[br] +## "_camera": camera[br] +## },[br] +## },[br] +## {[br] +## is_reserved: false, # Indicates this an entry for a room's objects[br] +## room_global_id: "",[br] +## room_instance_id: "",[br] +## objects:[br] +## {[br] +## "obj1": val1,[br] +## "obj2": val2[br] +## }[br] +## }[br] +## ] +var room_objects: Array = [] + +## Array containing the encountered terrains so they can be properly saved in savegames. +var room_terrains: Array = [] + +## We also store the current room's ids for retrieving the right objects. +var current_room_key: ESCRoomObjectsKey + +## To avoid having to look this up all the time, we hold a reference. +var reserved_objects_container: ESCRoomObjects + + +## Initializes the object manager and sets up the reserved objects container.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init() -> void: + reserved_objects_container = ESCRoomObjects.new() + reserved_objects_container.is_reserved = true + reserved_objects_container.objects = {} + room_objects.push_back(reserved_objects_container) + + current_room_key = ESCRoomObjectsKey.new() + + +## Updates which object manager room is to be treated as the currently active one.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room|`ESCRoom`|Room to register objects with in the object manager.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_current_room(room: ESCRoom) -> void: + if room == null: + escoria.logger.error( + self, + "Unable to set current room: No room was specified.\n" + + "Please pass in a valid ESCRoom as an argument to the method." + ) + + current_room_key.room_global_id = room.global_id + current_room_key.room_instance_id = room.get_instance_id() + + +## Registers the object in the manager.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`ESCObject`|The object to register.|yes|[br] +## |room|`ESCRoom`|(optional) Room to register the object with in the object manager; if not specified, the object manager will attempt to register the object with the current room if one has been specified.|no|[br] +## |force|`bool`|(optional) Register the object, even if it has already been registered (default: `false`).|no|[br] +## |auto_unregister|`bool`|(optional) Automatically unregister the object when its node exits the scene tree (default: `true`).|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_object(object: ESCObject, room: ESCRoom = null, force: bool = false, \ + auto_unregister: bool = true) -> void: + + if object.global_id.is_empty(): + object.global_id = str(object.node.get_path()).split("/root/", false)[0] + object.node.global_id = object.global_id + escoria.logger.warn( + self, + "Registering ESCObject %s with empty global_id." % object.node.name + + "Using node's full path as global_id: %s" + % object.node.global_id + ) + + # If this is a reserved object, let's make sure it's in the right place. + # Note that we also don't allow it to auto unregister and, as such, we need + # to make sure we clean these up when the application exits. + if object.global_id in RESERVED_OBJECTS: + reserved_objects_container.objects[object.global_id] = object + return + + var room_key: ESCRoomObjectsKey = ESCRoomObjectsKey.new() + + # If a room was passed in, then we're going to register the object with it; + # otherwise, we register the object with the "current room". + if room == null or room.global_id.is_empty(): + # We duplicate the key so as to not hold a reference when current_room_key + # changes. + if current_room_key.room_global_id.is_empty(): + escoria.logger.error( + self, + "The current room has no Global ID.\n" + + "Please set the ESCRoom's Global ID property." + ) + room_key.room_global_id = current_room_key.room_global_id + room_key.room_instance_id = current_room_key.room_instance_id + + if not room_key.is_valid(): + # This condition should very likely never happen. + escoria.logger.error( + self, + "No room was specified to register object with, and no current room is properly set.\n" + + "Please either pass in a valid ESCRoom to this method, or " + \ + "call set_current_room() with a valid ESCRoom first." + ) + else: + room_key.room_global_id = room.global_id + room_key.room_instance_id = room.get_instance_id() + + if not force and _object_exists_in_room(object, room_key) \ + and _object_state_in_room_is_default(object, room_key): + escoria.logger.warn( + self, + "Object with global id '%s' in room %s already registered from node path %s." + % [ + object.global_id, + room_key.room_global_id, + get_object(object.global_id, room).node.get_path() + ] + ) + return + # Object exists in room, set it to is last state (if different from + # "default") + elif object.node is ESCItem and _object_exists_in_room(object, room_key): + # Object is already known, set its state to last known state + object.set_state(get_object(object.global_id).state) + + # If the object is already connected, disconnect it for the case of + # forcing the registration, since we don't know if this object will be + # overwritten ("forced") in the future and, if it is, if it's set to + # auto-unregister or not. In most cases, objects are set to auto unregister. + if object.node.tree_exited.is_connected(unregister_object): + object.node.tree_exited.disconnect(unregister_object) + + if force: + # If this ID already exists and we're about to overwrite it, do the + # safe thing and unregister the old object first + unregister_object_by_global_id(object.global_id, room_key) + + if auto_unregister: + object.node.tree_exited.connect(unregister_object.bind(object, room_key)) + + if "is_interactive" in object.node and object.node.is_interactive: + object.interactive = true + + if "esc_script" in object.node and not object.node.esc_script.is_empty(): + var script = escoria.esc_compiler.load_esc_file( + object.node.esc_script, + object.global_id + ) + object.events = script.events + + var objects: Dictionary = _get_room_objects_objects(room_key) + objects[object.global_id] = object + + # If object state is not STATE_DEFAULT, save it in manager's object states + if object.state != ESCObject.STATE_DEFAULT: + if get_object(object.global_id) == null: + escoria.logger.error( + self, + "Object with global id %s in room (%s, %s) not found in Object Manager." + % [ + object.global_id, + room_key.room_global_id, + room_key.room_instance_id + ] + ) + else: + get_object(object.global_id).state = object.state + + # If this is the first object for the room, that means we have a brand new + # room and it needs to be setup and tracked. + if objects.size() == 1: + var room_container: ESCRoomObjects = ESCRoomObjects.new() + room_container.room_global_id = room_key.room_global_id + room_container.room_instance_id = room_key.room_instance_id + room_container.is_reserved = false + room_container.objects = objects + room_objects.push_back(room_container) + + +## Registers the terrain with the manager.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`ESCObject`|The object containing the terrain to register.|yes|[br] +## |room|`ESCRoom`|(optional) Room to register the object with in the object manager; if not specified, the object manager will attempt to register the terrain with the current room if one has been specified.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_terrain(object: ESCObject, room: ESCRoom = null) -> void: + var room_key: ESCRoomObjectsKey = ESCRoomObjectsKey.new() + + # If a room was passed in, then we're going to register the object with it; + # otherwise, we register the object with the "current room". + if not is_instance_valid(room) or room.global_id.is_empty(): + # We duplicate the key so as to not hold a reference when current_room_key + # changes. + room_key.room_global_id = current_room_key.room_global_id + room_key.room_instance_id = current_room_key.room_instance_id + + if not room_key.is_valid(): + # This condition should very likely never happen. + escoria.logger.error( + self, + "No room was specified to register terrain with, and no current room is properly set.\n" + + "Please either pass in a valid ESCRoom to this method, or " + \ + "call set_current_room() with a valid ESCRoom first." + ) + else: + room_key.room_global_id = room.global_id + room_key.room_instance_id = room.get_instance_id() + + var terrains: Dictionary = _get_room_terrain_navpolys(room_key) + if object.node is NavigationRegion2D: + terrains[object.global_id] = object + if terrains[object.global_id].node.enabled: + terrains[object.global_id].state = "enabled" + + if terrains.size() == 1: + var room_container: ESCRoomTerrains = ESCRoomTerrains.new() + room_container.room_global_id = room_key.room_global_id + room_container.room_instance_id = room_key.room_instance_id + room_container.terrains = terrains + if room_terrains.has(room_container): + room_terrains.erase(room_container) + room_terrains.push_back(room_container) + + +## Checks whether an object has been registered.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`String`|The global ID of the object.|yes|[br] +## |room|`ESCRoom`|(optional) `ESCRoom` instance the object is registered with; if not specified, the object manager will attempt to use the current room if one has been specified.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func has(global_id: String, room: ESCRoom = null) -> bool: + if global_id in RESERVED_OBJECTS: + if reserved_objects_container == null: + return false + + return reserved_objects_container.objects.has(global_id) + + var room_key: ESCRoomObjectsKey + + if room == null: + room_key = current_room_key + else: + room_key = ESCRoomObjectsKey.new() + room_key.room_global_id = room.global_id + room_key.room_instance_id = room.get_instance_id() + + if not _room_exists(room_key): + return false + + return _object_exists_in_room(ESCObject.new(global_id, null), room_key) + + +## Retrieves the object from the object registry.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`String`|The global id of the object to retrieve.|yes|[br] +## |room|`ESCRoom`|The `ESCRoom` instance the object is registered with; if not specified, the object manager will attempt to use the current room if one has been specified.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCObject` value. (`ESCObject`) +func get_object(global_id: String, room: ESCRoom = null) -> ESCObject: + if global_id in RESERVED_OBJECTS: + if reserved_objects_container.objects.has(global_id): + return reserved_objects_container.objects[global_id] + else: + escoria.logger.warn( + self, + "Reserved object with global id %s not found in object manager!" + % global_id + ) + return null + + var room_key: ESCRoomObjectsKey + + if room == null: + room_key = current_room_key + else: + room_key = ESCRoomObjectsKey.new() + room_key.room_global_id = room.global_id + room_key.room_instance_id = room.get_instance_id() + + if not _room_exists(room_key): + escoria.logger.warn( + self, + "Specified room is empty/not found.\n" + + "Object with global id %s in room instance (%s, %s) not found." + % [global_id, room_key.room_global_id, room_key.room_instance_id] + ) + return null + + var objects: Dictionary = _get_room_objects_objects(room_key) + + if objects.has(global_id): + return objects[global_id] + else: + escoria.logger.warn( + self, + "Object with global id %s in room instance (%s, %s) not found. This can be safely ignored if a room was being searched for." + % [global_id, room_key.room_global_id, room_key.room_instance_id] + ) + if escoria.inventory_manager.inventory_has(global_id): + # item is in the inventory and may be registered to a different room + for single_room in room_objects: + # these are arrays of the objects still registered for each room + if single_room.objects.has(global_id): + escoria.logger.info( + self, + "Object with global id %s found in room instance (%s, %s) through the inventory." + % [global_id, room_key.room_global_id, room_key.room_instance_id] + ) + return single_room.objects[global_id] + return null + + +## Removes an object from the registry.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`ESCObject`|The object to unregister.|yes|[br] +## |room_key|`ESCRoomObjectsKey`|The room under which the object should be unregistered.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func unregister_object(object: ESCObject, room_key: ESCRoomObjectsKey) -> void: + if not _object_exists_in_room(object, room_key): + # Report this as a warning and not an error since this method may be + # called as part of an objectd's forced registration and the object not + # yet being managed. + escoria.logger.debug( + self, + "Unable to unregister object. " + + "Object with global ID %s room (%s, %s) not found. If this was " + % [ + "?" if object == null else object.global_id, + room_key.room_global_id, + room_key.room_instance_id + ] + + "part of a 'forced' registration, ignore this warning." + ) + + return + + var room_objects = _get_room_objects_objects(room_key) + + if not escoria.is_quitting and escoria.inventory_manager.inventory_has(object.global_id): + # Re-instance the node if it is an item present in inventory; that is, + # re-register it with the new current room. + if object.node != null: + object.node = object.node.duplicate() + register_object(object, null, true) + + if object.state == ESCObject.STATE_DEFAULT: + room_objects.erase(object.global_id) + + # If this room is truly empty, it's time to do away with it. + if room_objects.size() == 0: + _erase_room(room_key) + + +## Removes an object from the registry by its `global_id`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`String`|The global id` of the object to unregister.|yes|[br] +## |room_key|`ESCRoomObjectsKey`|The room under which the object should be unregistered.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func unregister_object_by_global_id(global_id: String, room_key: ESCRoomObjectsKey) -> void: + unregister_object(ESCObject.new(global_id, null), room_key) + + +## Inserts data to save into savegame. For now, we only save the current room's objects.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_savegame|`ESCSaveGame`|The savegame resource.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func save_game(p_savegame: ESCSaveGame) -> void: + p_savegame.objects = {} + + for room_obj in room_objects: + if room_obj.room_global_id.is_empty(): + continue + + var room_objects_dict = {} + for obj_id in room_obj.objects: + var obj: ESCObject = room_obj.objects[obj_id] + var obj_json_to_save: Dictionary = obj.get_save_data() + if not obj_json_to_save.is_empty(): + room_objects_dict[obj_id] = obj_json_to_save + + p_savegame.objects[room_obj.room_global_id] = room_objects_dict + + # Add in reserved objects (music, speech, sound, ambient), too. + var reserved_objects: Dictionary = reserved_objects_container.objects + for obj_global_id in reserved_objects: + if not reserved_objects[obj_global_id] is ESCObject: + continue + p_savegame.objects[obj_global_id] = reserved_objects[obj_global_id].get_save_data() + + # Add ENABLED terrain navigationpolygons in, too. + p_savegame.terrain_navpolys = {} + + for room_terrain_container in room_terrains: + if room_terrain_container.room_global_id == current_room_key.room_global_id: + for terrain_name in room_terrain_container.terrains: + var terrain_escobj = room_terrain_container.terrains[terrain_name] + if terrain_escobj.node.enabled: + p_savegame.terrain_navpolys[room_terrain_container.room_global_id] = {} + p_savegame.terrain_navpolys[room_terrain_container.room_global_id][terrain_name] = \ + terrain_escobj.node.enabled + + +## The current room's starting location. If more than one exists, the first one encountered is returned. or `null` if no `ESCLocation` with `is_start_location` enabled can be found.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the current room's starting location. If more than one exists, the first one encountered is returned. or `null` if no `ESCLocation` with `is_start_location` enabled can be found. (`ESCLocation`) +func get_start_location() -> ESCLocation: + if _room_exists(current_room_key): + for object in _get_room_objects_objects(current_room_key).values(): + if is_instance_valid(object.node): + var loc := object.node as ESCLocation + if loc and loc.is_start_location: + return loc + + escoria.logger.warn( + self, + "Room has no ESCLocation node with 'is_start_location' enabled. " + + "Player will be set at position (0,0)." + ) + return null + + +## Determines whether 'container' represents the current room the player is in.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |container|`ESCRoomObjects`|The entry in the object manager array being checked.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _is_current_room(container: ESCRoomObjects) -> bool: + return _compare_container_to_key(container, current_room_key) + + +## Determines whether a container represents the specified room.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |container|`ESCRoomContainer`|The entry in the object manager array being checked|yes|[br] +## |room_key|`ESCRoomObjectsKey`|The key representing the desired room in the object manager array|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _compare_container_to_key(container: ESCRoomContainer, room_key: ESCRoomObjectsKey) -> bool: + return container.room_global_id == room_key.room_global_id + + +## Checks whether an entry in the object manager array corresponds to the given room key.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room_key|`ESCRoomObjectsKey`|The key representing the desired room in the object manager array|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _room_exists(room_key: ESCRoomObjectsKey) -> bool: + for room_container in room_objects: + if _compare_container_to_key(room_container, room_key): + return true + + return false + + +## Checks whether the specified object exists in the specified object manager entry.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`ESCObject`|The object to check for existence|yes|[br] +## |room_key|`ESCRoomObjectsKey`|The key representing the desired room in the object manager array|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _object_exists_in_room(object: ESCObject, room_key: ESCRoomObjectsKey) -> bool: + if object == null: + escoria.logger.warn( + self, + "Cannot check room for \"null\" objects." + ) + + return false + + for room_container in room_objects: + if _compare_container_to_key(room_container, room_key) \ + and room_container.objects.has(object.global_id): + return true + + return false + + +## Checks whether the specified object's state is "default" in the specified object manager entry.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`ESCObject`|The object to check for existence|yes|[br] +## |room_key|`ESCRoomObjectsKey`|The key representing the desired room in the object manager array|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _object_state_in_room_is_default(object: ESCObject, room_key: ESCRoomObjectsKey) -> bool: + if object == null: + escoria.logger.warn( + self, + "Cannot check room for \"null\" objects." + ) + + return false + + for room_container in room_objects: + if _compare_container_to_key(room_container, room_key) \ + and room_container.objects.has(object.global_id) \ + and room_container.objects.get(object.global_id).state == ESCObject.STATE_DEFAULT: + return true + + return false + + +## The objects currently being managed in the object manager entry specified by the given room key.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room_key|`ESCRoomObjectsKey`|The key representing the desired room in the object manager array|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the objects currently being managed in the object manager entry specified by the given room key. (`Dictionary`) +func _get_room_objects_objects(room_key: ESCRoomObjectsKey) -> Dictionary: + for room_container in room_objects: + if _compare_container_to_key(room_container, room_key): + return room_container.objects + + return {} + + +## The terrain navigation polygons for the specified room key.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room_key|`ESCRoomObjectsKey`|The key representing the desired room in the object manager array|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the terrain navigation polygons for the specified room key. (`Dictionary`) +func _get_room_terrain_navpolys(room_key: ESCRoomObjectsKey) -> Dictionary: + for room_container in room_terrains: + if _compare_container_to_key(room_container, room_key): + return room_container.terrains + + return {} + + +## Completely removes the entry in the object manager array specified by the room key.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room_key|`ESCRoomObjectsKey`|The key representing the desired room in the object manager array|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _erase_room(room_key: ESCRoomObjectsKey) -> void: + for room_container in room_objects: + if _compare_container_to_key(room_container, room_key): + room_objects.erase(room_container) + return diff --git a/addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd.uid b/addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd.uid new file mode 100644 index 0000000..cf50995 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd.uid @@ -0,0 +1 @@ +uid://bjis6cn1s41ij diff --git a/addons/escoria-core/game/core-scripts/esc/esc_room_manager.gd b/addons/escoria-core/game/core-scripts/esc/esc_room_manager.gd new file mode 100644 index 0000000..1a077a9 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_room_manager.gd @@ -0,0 +1,606 @@ +## A manager for rooms in a game. +## @MANAGER +extends Resource +class_name ESCRoomManager +## The room manager scene. +## Manages room loading and keeps in memory + + +## Reserved global containing the global_id of previous room. Cannot be overriden. +const GLOBAL_LAST_SCENE = "ESC_LAST_SCENE" + +## Reserved global name determining whether ESC_LAST_SCENE is considered for automatic transitions. +const GLOBAL_FORCE_LAST_SCENE_NULL = "FORCE_LAST_SCENE_NULL" + +## Reserved global name containing the animations +const GLOBAL_ANIMATION_RESOURCES = "ANIMATION_RESOURCES" + +## Reserved global name containing the global_id of the current room +const GLOBAL_CURRENT_SCENE = "ESC_CURRENT_SCENE" + +## Path to escoria-core camera scene +const CAMERA_SCENE_PATH = "res://addons/escoria-core/game/scenes/camera_player/camera.tscn" + +## Dictionary of the reserved globals to register and their initial values. +const RESERVED_GLOBALS = { + GLOBAL_LAST_SCENE: "", + GLOBAL_FORCE_LAST_SCENE_NULL: false, + GLOBAL_ANIMATION_RESOURCES: {}, + GLOBAL_CURRENT_SCENE: "" +} + + +# ESC commands kept around for references to their command names. +## Transition command +var _transition: TransitionCommand + +## Wait command +var _wait: WaitCommand + +## Accept input command +var _accept_input: AcceptInputCommand + + +## Constructor[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init() -> void: + _transition = TransitionCommand.new() + _wait = WaitCommand.new() + _accept_input = AcceptInputCommand.new() + + +## Registers all reserved global flags for use.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_reserved_globals() -> void: + for key in RESERVED_GLOBALS: + escoria.globals_manager.register_reserved_global( \ + key, + RESERVED_GLOBALS[key]) + + +## Performs the actions needed in order to change the current scene to the one specified by room_path.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room_path|`String`|Node path to the room that is to become the new current room.|yes|[br] +## |enable_automatic_transitions|`bool`|Whether to play the transition between rooms automatically or to leave the responsibility to the developer.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func change_scene_to_file(room_path: String, enable_automatic_transitions: bool) -> void: + _check_and_prepare_managers_for_room(room_path) + + # Load room scene + var res_room = escoria.resource_cache.get_resource(room_path) + if res_room == null: + escoria.logger.error( + self, + "Failed loading scene resource %s." % room_path + ) + return + + var room_scene = res_room.instantiate() + if room_scene: + if enable_automatic_transitions \ + and escoria.event_manager.get_running_event( + escoria.event_manager.CHANNEL_FRONT + ) != null: + room_scene.enabled_automatic_transitions = true + else: + room_scene.enabled_automatic_transitions = enable_automatic_transitions + + # If the game scene is already in the tree but not a child of the room + # we remove it + if escoria.game_scene.is_inside_tree() \ + and escoria.game_scene.get_parent() != room_scene: + var game_parent = escoria.game_scene.get_parent() + game_parent.remove_child(escoria.game_scene) + + room_scene.add_child(escoria.game_scene) + room_scene.move_child(escoria.game_scene, 0) + room_scene.game = escoria.game_scene + escoria.main.set_scene(room_scene) + + # We know the scene has been loaded. Make its global ID available for + # use by ESC script. + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_CURRENT_SCENE, + room_scene.global_id, + true + ) + + # Clear queued resources + escoria.resource_cache.clear() + + escoria.inputs_manager.hotspot_focused = "" + else: + escoria.logger.error( + self, + "Failed loading room scene %s." % room_path + ) + +## Changes the current scene to a NON-ESCORIA room scene. This allows to use full-Godot scenes in +## the game, but requires to call Escoria functions from GDScript to change back to an Escoria room. +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room_path|`String`|Node path to the scene to change to|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func change_scene_to_godot_file(room_path: String) -> void: + _check_and_prepare_managers_for_room(room_path) + + # Load room scene + var res_room = escoria.resource_cache.get_resource(room_path) + var room_scene = res_room.instantiate() + escoria.add_child(room_scene) + +## Perform some checks and prepare Escoria managers for new room instanciation. +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room_path|`String`|Node path to the scene to change to|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _check_and_prepare_managers_for_room(room_path: String) -> void: + if escoria.main \ + and escoria.main.current_scene \ + and escoria.main.current_scene.scene_file_path == room_path: + escoria.logger.info( + self, + "Attempting to change scene to same scene as the current scene. Aborting." + ) + if not Engine.is_editor_hint() \ + and (escoria.save_manager.is_loading_game or escoria.creating_new_game): + escoria.main.current_scene.enabled_automatic_transitions = false + escoria.room_manager.init_room(escoria.main.current_scene) + return + + # We're changing scenes, so users shouldn't be able to do stuff during. + escoria.inputs_manager.input_mode = escoria.inputs_manager.INPUT_NONE + + # Clear the event queue to remove other events (there could be duplicate + # events in there so we avoid running these multiple times). Also sets a + # flag indicating a changing scene and interrupts any other currently-running + # events. + escoria.event_manager.set_changing_scene(true) + + # If FORCE_LAST_SCENE_NULL is true, force ESC_LAST_SCENE to empty + if escoria.globals_manager.get_global(GLOBAL_FORCE_LAST_SCENE_NULL): + escoria.globals_manager.set_global( + GLOBAL_LAST_SCENE, + null, + true + ) + elif escoria.main.current_scene: + # If FORCE_LAST_SCENE_NULL is false, set ESC_LAST_SCENE = current roomid + escoria.globals_manager.set_global( + GLOBAL_LAST_SCENE, + escoria.main.current_scene.global_id, + true + ) + + if escoria.dialog_player: + escoria.dialog_player.interrupt() + + escoria.inputs_manager.hover_stack.clear() + + # Check if game scene was loaded + if not escoria.game_scene: + escoria.logger.error( + self, + "Failed loading game scene %s." % \ + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_SCENE + ) + ) + + if escoria.main.current_scene \ + and escoria.game_scene.get_parent() == escoria.main.current_scene: + escoria.main.current_scene.remove_child(escoria.game_scene) + + +## Sanitize camera limits, add player node and set the global id to the name of this node if it's not set manually.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room|`ESCRoom`|The ESCRoom to be initialized for use.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func init_room(room: ESCRoom) -> void: + if not is_instance_valid(room) || room == null: + escoria.logger.error( + self, + "No valid room was specified for initialization." + ) + + if room.camera_limits.is_empty(): + room.camera_limits.push_back(Rect2()) + + if room.camera_limits.size() == 1 and not room.camera_limits[0].has_area(): + for child in room.get_children(): + if child is ESCBackground: + room.camera_limits[0] = \ + Rect2(0, 0, child.size.x, child.size.y) + + if Engine.is_editor_hint(): + return + + if room.has_node("game"): + room.game = room.get_node("game") + + if room.game == null: + room.game = escoria.game_scene + room.add_child(room.game) + room.move_child(room.game, 0) + + if room.is_run_directly: + if escoria.main.current_scene == null: + escoria.main.set_scene(room) + + # Register all navigationpolygons in the terrain + if escoria.room_terrain != null and is_instance_valid(escoria.room_terrain): + for n in escoria.room_terrain.get_children_navpolys(): + escoria.object_manager.register_terrain( + ESCObject.new( + n.name, + n + ), + room + ) + + # Register all navigationpolygons in the terrain + if escoria.room_terrain != null: + for n in escoria.room_terrain.get_children_navpolys(): + escoria.object_manager.register_terrain( + ESCObject.new( + n.name, + n + ), + room + ) + + # If the room node isn't at (0,0), the walk_stop function will offset the + # player by the same number of pixels when they're at the terrain edge and + # move them when it shouldn't. + if room.position != Vector2(0,0): + escoria.logger.error( + self, + "The room node's coordinates must be (0,0) instead of %s." + % room.position + ) + _perform_script_events(room) + + +## Performs the ASHES script events `:setup` and `:ready`, in that order, if they are present. Also manages automatic transitions.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room|`ESCRoom`|The ESCRoom to be initialized for use.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func _perform_script_events(room: ESCRoom) -> int: + # Used to track whether any yields have been executed before the call to + # set_scene_finish. + var yielded: bool = false + + if room.enabled_automatic_transitions \ + and not room.is_run_directly: + + var script_transition_out: ESCScriptBuilder = ESCScriptBuilder.new() + script_transition_out \ + .add_event(escoria.event_manager.EVENT_TRANSITION_OUT, []) \ + .begin_block() \ + .add_command( + _transition.get_command_name(), + [ + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DEFAULT_TRANSITION + ), + "out" + ] + ) \ + .add_command(_wait.get_command_name(), [0.1]) \ + .end_block() + + var script_transition_out_compiled = escoria.esc_compiler.compile( + script_transition_out.build(), + get_class()) + + escoria.event_manager.queue_event( + script_transition_out_compiled.events[escoria.event_manager.EVENT_TRANSITION_OUT], + true + ) + +# var script_transition_out = escoria.esc_compiler.compile([ +# "%s%s" % [ESCEvent.PREFIX, escoria.event_manager.EVENT_TRANSITION_OUT], +# "%s %s out" % +# [ +# _transition.get_command_name(), +# ESCProjectSettingsManager.get_setting( +# ESCProjectSettingsManager.DEFAULT_TRANSITION +# ) +# ], +# "%s 0.1" % _wait.get_command_name() +# ], +# get_class() +# ) +# escoria.event_manager.queue_event( +# script_transition_out.events[escoria.event_manager.EVENT_TRANSITION_OUT], +# true +# ) + + # Unpause the game if it was + escoria.set_game_paused(false) + + # Wait for transition_out event to be done + var rc = await escoria.event_manager.event_finished + while rc[1] != escoria.event_manager.EVENT_TRANSITION_OUT: + rc = await escoria.event_manager.event_finished + if rc[0] != ESCExecution.RC_OK: + return rc[0] + + yielded = true + + # With the room transitioned out, finish any room prep and run :setup if + # it exists. + if room.player_scene: + if not is_instance_valid(room.player): + room.player = room.player_scene.instantiate() + room.add_child(room.player) + escoria.object_manager.register_object( + ESCObject.new( + room.player.global_id, + room.player + ), + room, + true + ) + + if escoria.globals_manager.has( + escoria.room_manager.GLOBAL_ANIMATION_RESOURCES + ): + var animations = escoria.globals_manager.get_global( + escoria.room_manager.GLOBAL_ANIMATION_RESOURCES + ) + + if room.player.global_id in animations and \ + ResourceLoader.exists(animations[room.player.global_id]): + room.player.animations = ResourceLoader.load( + animations[room.player.global_id] + ) + room.player.update_idle() + + #escoria.object_manager.get_object(escoria.object_manager.CAMERA).node.set_target(room.player) + + if room.global_id.is_empty(): + room.global_id = room.name + + + # Manage player location at room start + if room.player != null: + var startloc := escoria.object_manager.get_start_location() + if startloc != null \ + and not escoria.save_manager.is_loading_game: + room.player.teleport(startloc) + + # We make sure 'room' is set as the new current_scene, but without making + # it visible/the current scene tree. + if not yielded: + escoria.main.finish_current_scene_init(room) + + # Add new camera to scene being prepared. + if room.player_camera: + room.remove_child(room.player_camera) + room.player_camera.queue_free() + var new_player_camera: ESCCamera = escoria.resource_cache.get_resource( + CAMERA_SCENE_PATH + ).instantiate() + new_player_camera.register() + room.player_camera = new_player_camera + + # We must first set the camera limits, and then worry about subsequent + # player setup since it relies on this. + escoria.main.set_camera_limits(0, room) + + # Add the camera in to the scene tree but don't make it active just yet. + new_player_camera.enabled = false + room.add_child(new_player_camera) + room.move_child(new_player_camera, 0) + + if not escoria.save_manager.is_loading_game: + var setup_event_added: bool = false + + # Run the setup event, if there is one. + setup_event_added = _run_script_event(escoria.event_manager.EVENT_SETUP, room) + + if setup_event_added: + # Wait for setup event to be done + var rc = await escoria.event_manager.event_finished + while rc[1] != escoria.event_manager.EVENT_SETUP: + rc = await escoria.event_manager.event_finished + if rc[0] != ESCExecution.RC_OK: + return rc[0] + + yielded = true + + # As far as the event manager is concerned, we're done changing scenes and + # so should resume allowing events to be queued and processed. + escoria.event_manager.set_changing_scene(false) + + if room.player: + escoria.object_manager.get_object(escoria.object_manager.CAMERA).node.set_target(room.player) + + # Conclude the call to set_scene (thankyouverymuch, coroutines), including + # making the new room visible. + escoria.main.set_scene_finish() + + # Hide main and pause menus + escoria.game_scene.hide_main_menu() + escoria.game_scene.unpause_game() + + # Maybe this is ok to put in set_scene_finish() above? But it might be a bit + # confusing to not see the matching camera.current updates. + new_player_camera.enabled = true + new_player_camera.make_current() + + # We know the scene has been loaded. Make its global ID available for + # use by ESC script. + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_CURRENT_SCENE, + room.global_id, + true + ) + + # Clear queued resources + escoria.resource_cache.clear() + + escoria.inputs_manager.hotspot_focused = "" + + var transition_in_script: ESCScriptBuilder = ESCScriptBuilder.new() + + #var command_strings: PoolStringArray = [] + + #command_strings.append("%s%s" % [ESCEvent.PREFIX, escoria.event_manager.EVENT_TRANSITION_IN]) + transition_in_script.add_event(escoria.event_manager.EVENT_TRANSITION_IN, []) + transition_in_script.begin_block() + + if room.enabled_automatic_transitions \ + or ( + not room.enabled_automatic_transitions \ + and escoria.globals_manager.get_global( \ + escoria.room_manager.GLOBAL_FORCE_LAST_SCENE_NULL) + ): + + transition_in_script.add_command(_transition.get_command_name(), [ + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DEFAULT_TRANSITION + ), + "in" + ]) +# command_strings.append("%s %s in" % +# [ +# _transition.get_command_name(), +# ESCProjectSettingsManager.get_setting( +# ESCProjectSettingsManager.DEFAULT_TRANSITION +# ) +# ] +# ) + + transition_in_script.add_command(_wait.get_command_name(), [0.1]) + #command_strings.append("%s 0.1" % _wait.get_command_name()) + + transition_in_script.add_command(_accept_input.get_command_name(), ["ALL"]) + #command_strings.append("%s ALL" % _accept_input.get_command_name()) + + #var script_transition_in = escoria.esc_compiler.compile(command_strings, get_class()) + var script_transition_in = escoria.esc_compiler.compile(transition_in_script.build()) + + escoria.event_manager.queue_event( + script_transition_in.events[escoria.event_manager.EVENT_TRANSITION_IN] + ) + + if not escoria.current_state == escoria.GAME_STATE.LOADING: + var ready_event_added: bool = false + # Run the ready event, if there is one. + ready_event_added = _run_script_event(escoria.event_manager.EVENT_READY, room) + + if ready_event_added: + # Wait for ready event to be done + var rc = await escoria.event_manager.event_finished + while rc[1] != escoria.event_manager.EVENT_READY: + rc = await escoria.event_manager.event_finished + if rc[0] != ESCExecution.RC_OK: + return rc[0] + + # Now that :ready is finished, if FORCE_LAST_SCENE_NULL was true, reset it + # to false + if escoria.globals_manager.get_global( \ + escoria.room_manager.GLOBAL_FORCE_LAST_SCENE_NULL): + + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_FORCE_LAST_SCENE_NULL, + false, + true + ) + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_LAST_SCENE, + escoria.main.current_scene.global_id \ + if escoria.main.current_scene != null else "", + true + ) + + # Make the room's global ID available for use in ESC script. + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_CURRENT_SCENE, + escoria.main.current_scene.global_id \ + if escoria.main.current_scene != null else "", + true + ) + + return ESCExecution.RC_OK + + +## Runs the script event from the script attached, if any. does not exist in the script.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event_name|`String`|The name of the event to run|yes|[br] +## |room|`ESCRoom`|The ESCRoom to be initialized for use|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _run_script_event(event_name: String, room: ESCRoom): + if not room.esc_script: + return false + if room.compiled_script == null: + room.compiled_script = \ + escoria.esc_compiler.load_esc_file(room.esc_script) + + if room.compiled_script.events.has(event_name): + escoria.logger.debug( + self, + "Queuing room script event %s " % event_name + + "composed of %s statements." + % room.compiled_script.events[event_name].get_num_statements_in_block() + ) + escoria.event_manager.queue_event(room.compiled_script.events[event_name], true) + return true + else: + return false diff --git a/addons/escoria-core/game/core-scripts/esc/esc_room_manager.gd.uid b/addons/escoria-core/game/core-scripts/esc/esc_room_manager.gd.uid new file mode 100644 index 0000000..b510fa9 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/esc_room_manager.gd.uid @@ -0,0 +1 @@ +uid://dxc5sayadn8fp diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_base_command.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_base_command.gd new file mode 100644 index 0000000..c46714d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_base_command.gd @@ -0,0 +1,145 @@ +## Abstract base class for every ESC command. +## +## Extending classes have to override the configure and run function +extends Resource +class_name ESCBaseCommand + + +## The filename from which the relevant command is being called, if available. +var filename: String = "" + +## The line number from the file the relevant command is being called from. +var line_number: int = 0 + + +## A descriptor (contained in `ESCCommandArgumentDescriptor`) of the arguments of this command.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a descriptor (contained in `ESCCommandArgumentDescriptor`) of the arguments of this command. (`ESCCommandArgumentDescriptor`) +func configure() -> ESCCommandArgumentDescriptor: + escoria.logger.error( + self, + "Command %s did not override configure. Please implement a configure() function." % get_command_name() + ) + return ESCCommandArgumentDescriptor.new() + + +## Validates whether the given arguments match the command descriptor, returning `true` iff the command's descriptor can successfully validate the arguments. Should be overridden for each command's own needs, with this method called from the child class.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|an array containing the arguments to be passed to the command|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func validate(arguments: Array) -> bool: + var argument_descriptor = self.configure() + + argument_descriptor.filename = filename + argument_descriptor.line_number = line_number + + return argument_descriptor.validate(get_command_name(), arguments) + + +## Runs the command.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Array`|an array containing the arguments|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func run(command_params: Array) -> int: + raise_error(self, "Command %s did not override run. Please implement a run() function." % get_command_name()) + + return 0 + + +## The name of the command based on the script's filename.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the name of the command based on the script's filename. (`String`) +func get_command_name() -> String: + var path := get_script().get_path() as String + + return path.get_basename().get_file() + + +## Method to be called when the command is interrupted.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.debug( + self, + "Command %s did not override interrupt. Please implement an interrupt() function." % get_command_name() + ) + + +## Sends an error to the Escoria logger.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command|`Variant`|the command object causing the error; must extend `ESCBaseCommand`.|yes|[br] +## |message|`String`|the message the logger should print to describe the error|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func raise_error(command, message: String) -> void: + escoria.logger.error( + command, + "[%s]: %s %s" % [get_command_name(), message, _get_error_info()] + ) + + +## Raises an error specific to no object being found with `global_id` as its identifier. `command` is the actual command implementation generating the error.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command|`Variant`|the command object causing the error; must extend `ESCBaseCommand`.|yes|[br] +## |global_id|`String`|the `global_id` that could not be found|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func raise_invalid_object_error(command, global_id: String) -> void: + raise_error(command, "Invalid object. Object with global id %s not found." % global_id) + + +## A string with the file and line number for error reporting.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a string with the file and line number for error reporting. The error info string. (`String`) +func _get_error_info() -> String: + return "(File: \"%s\", line %s.)" % [filename, line_number] diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_base_command.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_base_command.gd.uid new file mode 100644 index 0000000..37a139f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_base_command.gd.uid @@ -0,0 +1 @@ +uid://1x1t3kqgxk22 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_camera_base_command.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_camera_base_command.gd new file mode 100644 index 0000000..54af190 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_camera_base_command.gd @@ -0,0 +1,43 @@ +## Abstract base class for those commands that implement camera-based functionality. +## +## [br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +extends ESCBaseCommand +class_name ESCCameraBaseCommand + + +## Generates a log entry when attempting to move the camera to an invalid position.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pos|`Vector2`|The offending position, represented by a `Vector2`.|yes|[br] +## |camera|`ESCCamera`|The camera object as an `ESCCamera` that `pos` was checked against.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func generate_viewport_warning(pos: Vector2, camera: ESCCamera) -> void: + var camera_limit: Rect2 = camera.get_camera_limit_rect() + var message: String = \ + """ + [%s]: Invalid camera position. Camera3D cannot be moved to %s as this is outside the viewport with current camera limit %s. + Current valid ranges for positions are: x = %s inclusive; y = %s inclusive. + """ + + escoria.logger.warn( + self, + message + % [ + get_command_name(), + pos.floor(), + camera_limit, + camera.get_current_valid_viewport_values_x(), + camera.get_current_valid_viewport_values_y() + ] + ) diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_camera_base_command.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_camera_base_command.gd.uid new file mode 100644 index 0000000..5d26110 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_camera_base_command.gd.uid @@ -0,0 +1 @@ +uid://gjk6efeq775j diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_command.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_command.gd new file mode 100644 index 0000000..4a5cb76 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_command.gd @@ -0,0 +1,160 @@ +## Used to properly setup and execute commands, i.e. those that extend `ESCBaseCommand`. +## +## Despite its name, `ESCCommand` is more of a container and orchestrator than an actual command. +extends ESCStatement +class_name ESCCommand + + +## The name of this command. +var name: String + +## Parameters for this command +var parameters: Array = [] + +## The token generated by the parser containing info for debugging (e.g. line number, +## script filename, etc.). +var parser_token: ESCToken = null + + +## A `Dictionary` containing the data needed when saving a game.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` containing the data needed when saving a game. (`Dictionary`) +func exported() -> Dictionary: + var export_dict: Dictionary = super.exported() + export_dict.class = "ESCCommand" + export_dict.name = name + export_dict.parameters = parameters + + return export_dict + + +## `true` iff the command is able to be run.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns `true` iff the command is able to be run. (`bool`) +func is_valid() -> bool: + if not _command_exists(): + escoria.logger.error( + self, + "Invalid command detected: %s - " % self.name + + "Command implementation not found in any command directory." + ) + return false + + return super.is_valid() + + +# Checks that the command exists +# +# *Returns* True if the command exists, else false. +func _command_exists() -> bool: + for base_path in ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.COMMAND_DIRECTORIES + ): + var command_path = "%s/%s.gd" % [ + base_path.trim_suffix("/"), + self.name + ] + if ResourceLoader.exists(command_path): + return true + + return false + + +## Runs the command contained by this `ESCCommand`.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_params|`Variant`|an array containing the arguments|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func run() -> int: + var command_object = escoria.command_registry.load_command(self.name) + + _decorate_for_debugging(command_object) + + if command_object == null: + return ESCExecution.RC_ERROR + else: + var argument_descriptor = command_object.configure() + + _decorate_for_debugging(argument_descriptor) + + var prepared_arguments = argument_descriptor.prepare_arguments( + self.parameters + ) + + if command_object.validate(prepared_arguments): + escoria.logger.debug( + self, + "Running command %s with parameters %s." + % [self.name, prepared_arguments] + ) + escoria.event_manager.add_running_command(self) + var rc = await command_object.run(prepared_arguments) + escoria.event_manager.running_command_finished(self) + escoria.logger.debug( + self, + "[%s] Return code: %d." % [self.name, rc] + ) + return rc + else: + return ESCExecution.RC_ERROR + + +## This function interrupts the command. If it was not started, it will not run. If it had already started, the execution will be considered as finished immediately and finish. If it had already finished, nothing will happen.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + _is_interrupted = true + var command = escoria.command_registry.is_command_or_control_pressed(self.name) + if command.has_method("interrupt"): + command.interrupt() + +## Override of built-in _to_string function to display the statement.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a string representation of the command. (`String`) +func _to_string() -> String: + return "Command %s with parameters: %s" % [name, str(parameters)] + +## Adds additional info to the command object, i.e. for debugging.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command_object|`Variant`|The command object to decorate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _decorate_for_debugging(command_object) -> void: + command_object.filename = parser_token.get_filename() + command_object.line_number = parser_token.get_line() diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_command.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_command.gd.uid new file mode 100644 index 0000000..19c9198 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_command.gd.uid @@ -0,0 +1 @@ +uid://ge0nds6ptkbq diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_command_argument_descriptor.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_command_argument_descriptor.gd new file mode 100644 index 0000000..44b3b77 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_command_argument_descriptor.gd @@ -0,0 +1,264 @@ +## Describes the arguments of commands that extend `ESCBaseCommand`. +extends RefCounted +class_name ESCCommandArgumentDescriptor + + +## As the get_type command was deprecated with Godot 2.x w we need a way to determine +## variable types. Ideally these wouldn't be hardcoded but there's no GDScript 3.x command to +## turn a type back to its name.[br] +##[br] +## TODO: With Escoria having been ported to Godot 4, this concept will need to be revisited. +const GODOT_TYPE_LIST = ["nil", "bool", "int", "real", "string", \ + "vector2", "rect2", "vector3", "matrix32", "plane", "quat", \ + "aabb", "matrix3", "transform", "color", "image", "node_path", \ + "rid", "object", "input_event", "dictionary", "array", \ + "raw_array", "int_array", "real_array", "string_array", \ + "vector2_array", "vector3_array", "color_array", "max"] + + +## Maximum number of total arugments the command can handle +var max_args: int = 0 + +## Number of required arguments the command expects +var min_args: int = 0 + +## The types the arguments as TYPE_ constants. If the command is called with +## more arguments than there are entries in the types array, the additional +## arguments will be checked against the last entry of the types array. +var types: Array = [] + +## The default values for the arguments +var defaults: Array = [] + +## Whether to strip quotes on specific arguments +var strip_quotes: Array = [] + +## Whether the final argument is a series of varargs +var has_varargs: bool = false + +## The filename from which the relevant command is being called, if available. +var filename: String = "" + +## The line number from the file the relevant command is being called from. +var line_number: int = 0 + +## Initializes the descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_min_args|`int`|Minimum number of required arguments.|no|[br] +## |p_types|`Array`|Array of argument types.|no|[br] +## |p_defaults|`Array`|Array of default values for arguments.|no|[br] +## |p_strip_quotes|`Array`|Array indicating whether to strip quotes.|no|[br] +## |p_has_varargs|`bool`|Whether the final argument is a series of varargs.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init( + p_min_args: int = 0, + p_types: Array = [], + p_defaults: Array = [], + p_strip_quotes: Array = [true], + p_has_varargs: bool = false +): + max_args = p_types.size() + min_args = p_min_args + types = p_types + defaults = p_defaults + strip_quotes = p_strip_quotes + has_varargs = p_has_varargs + + +## Combines the default argument values with the given arguments.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |arguments|`Array`|an array of arguments to pass in to the command, with the array's order corresponding to the order of the arguments the command expects. If the number of arguments passed in is fewer than the maximum number of arguments the command can handle, default values are used for those arguments not passed in.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Array` value. (`Array`) +func prepare_arguments(arguments: Array) -> Array: + var complete_arguments = defaults + var varargs = [] + + for index in range(arguments.size()): + # If we have too many arguments passed in, complete_arguments won't + # be able to match 1:1. This condition will be validated later but so + # to avoid duplicating validation code, just grow complete_arguments + # since the arguments won't be used anyway. + if index >= complete_arguments.size(): + if has_varargs: + varargs.append(arguments[index]) + else: + complete_arguments.append(arguments[index]) + elif index == complete_arguments.size() - 1 and has_varargs: + # Varargs are a special case and need to be gathered and added at + # the end as an array, untyped and unchecked. They should also only + # appear at the very end of a command's argument list. + varargs.append(arguments[index]) + else: +# complete_arguments[index] = ESCUtils.get_typed_value( +# arguments[index], +# types[index] +# ) + complete_arguments[index] = arguments[index] + var strip = strip_quotes[0] + if strip_quotes.size() == complete_arguments.size(): + strip = strip_quotes[index] + + if strip and typeof(complete_arguments[index]) == TYPE_STRING: + complete_arguments[index] = complete_arguments[index].replace( + '"', + '' + ) + + if has_varargs: + complete_arguments[complete_arguments.size() - 1] = varargs + + return complete_arguments + + +## Validates whether the given arguments match the command descriptor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |command|`String`|the name of the command; used for logging purposes.|yes|[br] +## |arguments|`Array`|the arguments to be passed to the command|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func validate(command: String, arguments: Array) -> bool: + var required_args_count: int = _count_leading_non_null_values(arguments, min_args) + + if required_args_count < min_args: + var verb = "was" if required_args_count == 1 else "were" + + escoria.logger.error( + self, + "Invalid arguments for command %s." % command + + " Arguments didn't match minimum size {num}: Only {args} {verb} found." \ + .format({"num":self.min_args,"args":required_args_count,"verb":verb}) + + " %s" % _get_error_info() + ) + return false + + if arguments.size() > self.max_args and not has_varargs: + escoria.logger.error( + self, + "Invalid arguments for command %s. " % command + + "Maximum number of arguments ({num}) exceeded: {args}.".format( + {"num":self.max_args,"args":arguments} + ) + + " %s" % _get_error_info() + ) + return false + + for index in range(arguments.size()): + if arguments[index] == null: + # No type checking for null values + continue + + if has_varargs and index == arguments.size() - 1: + # If we have varargs at the end, do not validate them. + continue + + var correct = false + var types_index = index + if types_index > types.size(): + types_index = types.size() - 1 + if not self.types[types_index] is Array: + self.types[types_index] = [self.types[index]] + for type in self.types[types_index]: + if not correct: + correct = self._is_type(arguments[index], type) + + if not correct: + var allowed_types = "[ " + for type in self.types[types_index]: + allowed_types += GODOT_TYPE_LIST[type] + " or " + allowed_types = allowed_types.substr(0, allowed_types.length() - 3) + "]" + escoria.logger.error( + self, + "Argument type did not match descriptor for command \"%s\". " + % command + + "Argument %d (\"%s\") is of type %s. Expected %s." + % [ + index, + arguments[index], + GODOT_TYPE_LIST[typeof(arguments[index])], + allowed_types + ] + + " %s" % _get_error_info() + ) + return false + return true + + +## A string with the file and line number for error reporting.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a string with the file and line number for error reporting. The error info string. (`String`) +func _get_error_info() -> String: + return "(File: \"%s\", line %s.)" % [filename, line_number] + + +## Checks whether the given argument is of the given type.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |argument|`Variant`|Argument to test.|yes|[br] +## |type|`int`|Type to check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns Whether the argument is of the given type. (`bool`) +func _is_type(argument, type: int) -> bool: + if typeof(argument) == TYPE_FLOAT: + if int(argument) == argument and type == TYPE_INT: + return true + elif typeof(argument) == TYPE_INT: + if int(argument) == argument and type == TYPE_FLOAT: + return true + + return typeof(argument) == type + + +## Counts the number of non-null values that exist at the beginning of the array up to a specified index.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |array_to_check|`Array`|Array to check for leading non-null values.|yes|[br] +## |max_index|`int`|Maximum (inclusive) index to check in array_to_check.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the total number of entries at the start of array_to_check that are not null. (`int`) +func _count_leading_non_null_values(array_to_check: Array, max_index: int) -> int: + if array_to_check == null or max_index < 0: + return 0 + + var leading_non_nulls_count: int = 0 + + for i in range(max_index): + if array_to_check[i] != null: + leading_non_nulls_count += 1 + + return leading_non_nulls_count diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_command_argument_descriptor.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_command_argument_descriptor.gd.uid new file mode 100644 index 0000000..e2e3a36 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_command_argument_descriptor.gd.uid @@ -0,0 +1 @@ +uid://sborpect3wyt diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_dialog.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_dialog.gd new file mode 100644 index 0000000..5d48a98 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_dialog.gd @@ -0,0 +1,80 @@ +## Represents a dialog in Escoria. +extends ESCStatement +class_name ESCDialog + + +## Avatar used in the dialog, if any. +var avatar: String = "-" + +## Timeout until the timeout_option option is selected. Use 0 for no timeout. +var timeout: int = 0 + +## The dialog option to select when timeout is reached. +var timeout_option: int = 0 + +## A list of `ESCDialogOption`s. +var options: Array + + +## True iff the dialog and its settings are valid.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns true iff the dialog and its settings are valid. (`bool`) +func is_valid() -> bool: + if self.avatar != "-" and not ResourceLoader.exists(self.avatar): + escoria.logger.error( + self, + "Avatar scene not found: %s." % self.avatar + ) + return false + if self.timeout_option > self.options.size() \ + or self.timeout_option < 0: + escoria.logger.error( + self, + "Invalid timeout_option parameter given: %d." % self.timeout_option + ) + return false + + return true + + +## Run this dialog. TODO: Although this method overrides its parent version, the return type here does NOT match the parent's signature. Consider either changing the parent's return type to be a `Variant`, or doing something to ensure greater consistency.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the `ESCDialogOption` chosen by the player. (`Variant`) +func run(): + escoria.logger.debug( + self, + "Starting dialog." + ) + + escoria.current_state = escoria.GAME_STATE.DIALOG + + if !escoria.dialog_player: + escoria.dialog_player = escoria.main.current_scene.get_node( + "game/ui/dialog_layer/dialog_player" + ) + + escoria.dialog_player.start_dialog_choices(self) + + var option = ( + await escoria.dialog_player.option_chosen + ) as ESCDialogOption + + # If no valid option was returned, it means this level of dialog is done. + # If this is the case and the current level of dialog has a parent, it means + # it is still yielding and so will be shown again. + + escoria.current_state = escoria.GAME_STATE.DEFAULT + + return option diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_dialog.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_dialog.gd.uid new file mode 100644 index 0000000..a8f895f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_dialog.gd.uid @@ -0,0 +1 @@ +uid://p7sj3uddbdey diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_dialog_option.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_dialog_option.gd new file mode 100644 index 0000000..e3701e5 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_dialog_option.gd @@ -0,0 +1,71 @@ +## A single option used as part of a dialog. +## +## `ESCDialog` makes use of these when assembling an actual dialog in Escoria. +extends ESCStatement +class_name ESCDialogOption + + +## Option text displayed in the HUD. +var option: String: + get = get_translated_option + +## Maps back to the parsed source option. +var source_option + +## Whether this option is valid. +var _is_valid: bool: + set = set_is_valid, + get = is_valid + + +## The translated version of the option, if one exists; otherwise, the default text is returned.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the translated version of the option, if one exists; otherwise, the default text is returned. (`String`) +func get_translated_option() -> String: + # Check if text has a key + if ":" in option: + var splitted_text = option.split(":") + var key = splitted_text[0] + var translated_text = tr(key) + + # If no translation is found use default text + if key != translated_text: + return tr(key) + if splitted_text.size() > 1: + return splitted_text[1] + + return option + + +## Whether this dialog option is valid. Note: this value isn't currently used as part of any meaningful validation checks.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns whether this dialog option is valid. Note: this value isn't currently used as part of any meaningful validation checks. (`bool`) +func is_valid() -> bool: + return _is_valid + + +## Sets whether the option is valid, although this value isn't currently used as part of any useful checks.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`bool`|`true` to mark the option as valid; `false` to invalidate it.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_is_valid(value: bool) -> void: + _is_valid = value diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_dialog_option.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_dialog_option.gd.uid new file mode 100644 index 0000000..38f3c43 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_dialog_option.gd.uid @@ -0,0 +1 @@ +uid://vkyp6yn16fx1 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_event.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_event.gd new file mode 100644 index 0000000..19a0075 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_event.gd @@ -0,0 +1,148 @@ +## An event in Escoria. +## +## Events are triggered from various sources. Common events include:[br] +##[br] +## - `:setup` : This event is always the first to be called each time the room is visited. +## It allows elements in the room to be prepared *before* the room is displayed to the +## player (e.g. starting particle effects).[br] +## - `:ready` : This event is the second to be called each time the room is visited. +## It is run immediately after `:setup` finishes execution, if it exists. Otherwise, +## `:ready` will be the first event to run. Regardless, this event is run *after* +## the room is displayed to the player, allowing cutscenes or animations to be +## run once the room is visible.[br] +## - `:use ` : Called from the current item when it is used with the item +## with specified by ``. +extends ESCStatement +class_name ESCEvent + +## Regex identifying an ESC event. +const REGEX = \ + '^:(?[^|]+)( \\|\\s*(?( ' + \ + '(TK|NO_TT|NO_UI|NO_SAVE)' + \ + ')+))?$' + +## Prefix to identify this as an ESC event. +const PREFIX = ":" + + +## Valid event flags:[br] +## - `TK`: stands for "telekinetic". It means the player won't walk over to +## the item to say the line.[br] +## - `NO_TT`: stands for "No tooltip". It hides the tooltip for the duration of +## the event. Probably not very useful, because events having multiple +## say commands in them are automatically hidden.[br] +## - `NO_UI`: stands for "No User Inteface". It hides the UI for the duration of +##  the event. Useful when you want something to look like a cut scene but not +## disable input for skipping dialog.[br] +## - `NO_SAVE`: disables saving. Use this in cut scenes and anywhere a +## badly-timed autosave would leave your game in a messed-up state. +enum FLAGS { + TK = 1, + NO_TT = 2, + NO_UI = 4, + NO_SAVE = 8 +} + + +## Name of the event. +var name: String + +## Original name of the event (in case it is modified when resuming a loaded event). +var original_name: String + +## Flags set for this event. +var flags: int = 0 + + +## A Dictionary containing statements data for serialization, typically used as part of the savegame process.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a Dictionary containing statements data for serialization, typically used as part of the savegame process. (`Dictionary`) +func exported() -> Dictionary: + var exported_dict: Dictionary = super.exported() + exported_dict.class = "ESCEvent" + exported_dict.name = name + exported_dict.original_name = original_name + exported_dict.flags = flags + + return exported_dict + + +## Initializes the event with specified arguments.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event_name|`String`|the name of the event|yes|[br] +## |event_flags|`Array`|an array containing zero or more event flags as described in the enum contained in this class.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func init(event_name: String, event_flags: Array) -> void: + name = event_name + flags = get_flags_from_list(event_flags) + +## Converts a list of event flag strings to a bitmask.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event_flags|`Array[String]`|Array of flag strings.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the bitmask of flags as an int. (`int`) +static func get_flags_from_list(event_flags: Array[String]) -> int: + var computed_flags: int = 0 + for flag in event_flags: + match flag: + "TK": + computed_flags |= FLAGS.TK + "NO_TT": + computed_flags |= FLAGS.NO_TT + "NO_UI": + computed_flags |= FLAGS.NO_UI + "NO_SAVE": + computed_flags |= FLAGS.NO_SAVE + return computed_flags + + +## Executes this statement and returns a return code.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func run() -> int: + reset_interrupt() + escoria.logger.debug( + self, + "Event %s started." % name + ) + if name == "resume": + bypass_conditions = true + return await super() + + +## The event's name.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the event's name. (`String`) +func get_event_name() -> String: + return name diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_event.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_event.gd.uid new file mode 100644 index 0000000..16736c2 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_event.gd.uid @@ -0,0 +1 @@ +uid://clwvjxqfohhhi diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_execution.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_execution.gd new file mode 100644 index 0000000..21641e7 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_execution.gd @@ -0,0 +1,21 @@ +## Class with enum containing the return codes returned by ASHES script functions. +extends Resource +class_name ESCExecution +## Execution result values. + +## Return codes handled by events.[br] +## * RC_OK: Event ran okay[br] +## * RC_CANCEL: Cancel all scheduled and queued events. This return code tells the Event Manager +## that no execution is required for this command (such as "stop" and "repeat")[br] +## * RC_ERROR: Error running a command[br] +## * RC_REPEAT: Repeat the current scope from the beginning[br] +## * RC_INTERRUPTED: Event was interrupted[br] +## * RC_WONT_QUEUE: Event won't or can't be queued +enum { + RC_OK, + RC_CANCEL, + RC_ERROR, + RC_REPEAT, + RC_INTERRUPTED, + RC_WONT_QUEUE +} diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_execution.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_execution.gd.uid new file mode 100644 index 0000000..e42277e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_execution.gd.uid @@ -0,0 +1 @@ +uid://sljxq7gxcfqn diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_object.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_object.gd new file mode 100644 index 0000000..64d3596 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_object.gd @@ -0,0 +1,202 @@ +## Represents an object that is able to be handled by Escoria. +extends RefCounted +class_name ESCObject + + +## Default object state. +const STATE_DEFAULT: String = "default" + + +## The global id of the object. +var global_id: String + +## Whether the object is active (i.e. is actually visible to the player). +var active: bool = true: set = _set_active + +## Whether the object is interactive (i.e. is clickable by the player). +var interactive: bool = true: get = _get_interactive, set = _set_interactive + +## The state of the object. If the object has a respective animation, +## it will be played. +var state: String = STATE_DEFAULT: + get = get_state + +## The events registered with the object. +var events: Dictionary = {} + +## The node representing the object in the scene. +var node: Node + +## Initializes the ESCObject with a global id and node.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_global_id|`String`|The global id of the object.|yes|[br] +## |p_node|`Node`|The node in the scene.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(p_global_id: String, p_node: Node): + global_id = p_global_id + node = p_node + + +## Gets the current state.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `String` value. (`String`) +func get_state() -> String: + return state + + +## Set the state and start a possible animation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_state|`String`|The state in which this object should placed.|yes|[br] +## |immediate|`bool`|If true, skips any animation directly to its end.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_state(p_state: String, immediate: bool = false): + state = p_state + + if node.has_method("get_animation_player"): + var animation_node: ESCAnimationPlayer = node.get_animation_player() + + if animation_node != null and animation_node.is_valid(): + animation_node.stop() + var actual_animator + if animation_node.has_animation(p_state): + if immediate: + escoria.logger.debug( + self, + "State \"%s\" set. Matching immediate animation executing." + % p_state + ) + animation_node.seek_end(p_state) + else: + escoria.logger.debug( + self, + "State \"%s\" set. Matching non-immediate animation executing." + % p_state + ) + animation_node.play(p_state) + else: + escoria.logger.debug( + self, + "State \"%s\" set. No matching animation found." + % p_state + ) + + +## Sets the active value, thus hiding or showing the object.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`bool`|A boolean determining whether the object should become active or inactive.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_active(value: bool): + active = value + self.node.visible = value + + +## Whether the object is active.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns whether the object is active. (`bool`) +func is_active() -> bool: + return active + + +## Gets the interactive value from the node.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns Whether the node is interactive or not. (`bool`) +func _get_interactive() -> bool: + if is_instance_valid(self.node) and "is_interactive" in self.node: + return self.node.is_interactive + else: + return true + + +## Sets the interactive value in the node.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`bool`|Whether the object is interactive or not.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_interactive(value: bool): + if "is_interactive" in self.node: + self.node.is_interactive = value + if not value: + escoria.game_scene.clear_tooltip() + escoria.inputs_manager.on_item_non_interactive(self.node) + + +## Return the data of the object to be inserted in a savegame file.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` value. (`Dictionary`) +func get_save_data() -> Dictionary: + var save_data: Dictionary = {} + + if self.global_id == "player" and not is_instance_valid(self.node): + return save_data + + save_data["active"] = self.active + save_data["interactive"] = self.interactive + save_data["state"] = self.state + + if is_instance_valid(self.node): + if self.node.get("is_movable") and self.node.is_movable: + save_data["global_transform"] = self.node.global_transform + save_data["last_deg"] = wrapi(self.node._movable._get_angle() - 90 + 1, 0, 360) + save_data["last_dir"] = self.node._movable.last_dir + if self.node.has_method("get_custom_data"): + save_data["custom_data"] = self.node.get_custom_data() + + if self.global_id in ["_music", "_sound", "_ambient"] and self.node.get("state"): + save_data["state"] = self.node.get("state") + save_data["playback_position"] = self.node.get_playback_position() + + if self.global_id == "_camera": + save_data["target"] = self.node.get("_follow_target").global_id + + return save_data diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_object.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_object.gd.uid new file mode 100644 index 0000000..8cdbea3 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_object.gd.uid @@ -0,0 +1 @@ +uid://bbn763m60m033 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_room_container.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_room_container.gd new file mode 100644 index 0000000..591d2b5 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_room_container.gd @@ -0,0 +1,16 @@ +## Abstract base class for a container of Escoria entities specific to a room +## to be stored in and used by the Escoria object manager. +extends RefCounted +class_name ESCRoomContainer + + +## Designates whether the objects contained in this container are all reserved objects. +var is_reserved: bool = false + +## Global ID of the room in which the objects in this container are registered. +var room_global_id: String = "" + +## Instance ID of the room in which the objects in this container are registered. +## This is used to disambiguate in cases where more than one of the same room +## exist in the object manager. +var room_instance_id: int = -1 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_room_container.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_room_container.gd.uid new file mode 100644 index 0000000..148b228 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_room_container.gd.uid @@ -0,0 +1 @@ +uid://dup01ieep5jj1 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects.gd new file mode 100644 index 0000000..4a617ce --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects.gd @@ -0,0 +1,7 @@ +## Container for a room's `ESCObject`s stored in the object manager. +extends ESCRoomContainer +class_name ESCRoomObjects + + +## The hash of objects registered to the room. +var objects: Dictionary = {} diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects.gd.uid new file mode 100644 index 0000000..f834346 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects.gd.uid @@ -0,0 +1 @@ +uid://ck7e2dif7pi2h diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects_key.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects_key.gd new file mode 100644 index 0000000..b6f5ce4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects_key.gd @@ -0,0 +1,24 @@ +## Simple pair container to store a room's identifying information for use in +## the object manager. +extends RefCounted +class_name ESCRoomObjectsKey + + +## Contains the `global_id` of the room being represented by this key. +var room_global_id: String = "" + +## Contains the instance ID of the room being represented by this key. +var room_instance_id: int = -1 + + +## Checks whether this key is valid and represents an actual room.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func is_valid() -> bool: + return not room_global_id.is_empty() and room_instance_id > -1 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects_key.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects_key.gd.uid new file mode 100644 index 0000000..60bb3c7 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_room_objects_key.gd.uid @@ -0,0 +1 @@ +uid://cexl54srbt1q3 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_room_terrains.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_room_terrains.gd new file mode 100644 index 0000000..153a49c --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_room_terrains.gd @@ -0,0 +1,7 @@ +## Container for `NavigationPolygon`s stored in the object manager. +extends ESCRoomContainer +class_name ESCRoomTerrains + + +## The hash of terrains registered to the room. +var terrains: Dictionary = {} diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_room_terrains.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_room_terrains.gd.uid new file mode 100644 index 0000000..872d2ee --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_room_terrains.gd.uid @@ -0,0 +1 @@ +uid://c6b4dm4nvf0n3 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_scheduled_event.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_scheduled_event.gd new file mode 100644 index 0000000..736f241 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_scheduled_event.gd @@ -0,0 +1,63 @@ +## Represents an event that is scheduled to run sometime in the future. +extends RefCounted +class_name ESCScheduledEvent + + +## The event to run when timeout is reached. +var event + +## The number of seconds until the event is to be run. +var timeout: float + +## The target object. +var object: String + +## Creates a new scheduled event.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_event|`ESCGrammarStmts.Event`|The event to schedule.|yes|[br] +## |p_timeout|`float`|The number of seconds until the event is run.|yes|[br] +## |p_object|`String`|The target object.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(p_event: ESCGrammarStmts.Event, p_timeout: float, p_object: String): + self.event = p_event + self.timeout = p_timeout + self.object = p_object + + +## A `Dictionary` containing relevant data for serialization.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` containing relevant data for serialization. (`Dictionary`) +func exported() -> Dictionary: + var exported_dict: Dictionary = {} + exported_dict.class = "ESCScheduledEvent" + exported_dict.event_name = event.get_name().get_lexeme() + exported_dict.event_filename = event.get_name().get_filename() + exported_dict.timeout = timeout + exported_dict.object = object + return exported_dict + + +## TODO: THIS IS NO LONGER USED AS EVENT IS NOW RUN BY ASHES INTERPRETER. THIS MAY BE REMOVED IN THE FUTURE. Runs the event.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func run() -> int: + return await event.run() diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_scheduled_event.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_scheduled_event.gd.uid new file mode 100644 index 0000000..48fbdb8 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_scheduled_event.gd.uid @@ -0,0 +1 @@ +uid://bloxweo7xtn5p diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_script.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_script.gd new file mode 100644 index 0000000..efca0f8 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_script.gd @@ -0,0 +1,11 @@ +## Represents a compiled ASHES script. +extends Resource +class_name ESCScript + + +## A dictionary of `ESCEvent`s contained in this script. +var events: Dictionary + +## The name of the ASHES file parsed for this script, if this script was in fact +## loaded from a file (as opposed to being from an internal string). +var filename: String = "" diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_script.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_script.gd.uid new file mode 100644 index 0000000..87aa982 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_script.gd.uid @@ -0,0 +1 @@ +uid://bspmgkqu38886 diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd b/addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd new file mode 100644 index 0000000..345dd28 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd @@ -0,0 +1,184 @@ +## Abstract base class representing a "statement" that has been interpreted from +## an ASHES script file and is used to carry out its execution in Escoria. +## +## NOTE: This class is legacy code related to the ESCScript language that precedes the +## newer ASHES scripting language used by Escoria. Although this class is extended +## to facilitate the execution of actual commands and dialogs, the name may be +## a bit misleading and may eventually be renamed or refactored. +extends RefCounted +class_name ESCStatement + + +## Emitted when the event has finished running.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`Variant`|`ESCStatement` representing the event whose execution completed.|yes|[br] +## |statement|`Variant`|`ESCStatement` that was running when the signal fired.|yes|[br] +## |return_code|`Variant`|Execution result code produced by the statement.|yes|[br] +## [br] +signal finished(event: ESCStatement, statement: ESCStatement, return_code: int) + +## Emitted if the event has been interrupted[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`Variant`|`ESCStatement` representing the event whose execution was interrupted.|yes|[br] +## |statement|`Variant`|`ESCStatement` that was executing when the interruption occurred.|yes|[br] +## |return_code|`Variant`|Execution result code describing the interruption outcome.|yes|[br] +## [br] +signal interrupted(event: ESCStatement, statement: ESCStatement, return_code: int) + + +## The list of command to be executed. +var statements: Array = [] + +## The source of this statement, e.g. an ASHES script or a class. +var source: String = "" + +## Indictates whether this statement has completed. +var is_completed: bool = false + +## The currently running statement. +var current_statement: ESCStatement = null + +## Value from which to start assigning the IDs of statements to be executed. Defaults to 0. +var from_statement_id = 0 + +## Determines whether conditions will be checked during execution or skipped. +var bypass_conditions = false + +## TODO: Can probably delete. +var parsed_statements = [] + +# Indicates whether this event was interrupted. +var _is_interrupted: bool = false + + +## A `Dictionary` containing relevant data for serialization.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` containing relevant data for serialization. (`Dictionary`) +func exported() -> Dictionary: + var export_dict: Dictionary = {} + export_dict.class = "ESCStatement" + export_dict.source = source + export_dict._is_interrupted = _is_interrupted + export_dict.is_completed = is_completed + export_dict.from_statement_id = from_statement_id + export_dict.current_statement = current_statement.exported() if current_statement != null else null + export_dict.statements = [] + for s in statements: + export_dict.statements.push_back(s.exported()) + return export_dict + + + +## `true` iff the statement is valid.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns `true` iff the statement is valid. (`bool`) +func is_valid() -> bool: + return true + + +## Execute this statement and return its return code.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func run() -> int: + if parsed_statements.size() > 0: + var interpreter = ESCInterpreterFactory.create_interpreter() + var resolver: ESCResolver = ESCResolver.new(interpreter) + resolver.resolve(parsed_statements) + + interpreter.interpret(parsed_statements) + return 0 + + var final_rc = ESCExecution.RC_OK + + var statement_id = 0 + for statement in statements: + from_statement_id = statement_id + statement_id = statement_id + 1 + current_statement = statement + + if _is_interrupted: + final_rc = ESCExecution.RC_INTERRUPTED + statement.interrupt() + interrupted.emit(self, statement, final_rc) + return final_rc + + if statement.is_valid(): + var rc = await statement.run() + escoria.logger.debug( + self, + "Statement (%s) was completed." % statement + ) + if rc == ESCExecution.RC_REPEAT: + return await self.run() + elif rc != ESCExecution.RC_OK: + final_rc = rc + current_statement.is_completed = true + break + elif rc == ESCExecution.RC_OK: + current_statement.is_completed = true + + finished.emit(self, current_statement, final_rc) + is_completed = true + return final_rc + + +## Interrupts the statement in the middle of its execution.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + escoria.logger.info( + self, + "Interrupting event %s (%s)." + % [self.name if "name" in self else "group", str(self)] + ) + _is_interrupted = true + for statement in statements: + if statement.has_method("interrupt"): + statement.interrupt() + + +## Resets all affected statements' interrupts.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func reset_interrupt(): + _is_interrupted = false + for statement in statements: + statement.reset_interrupt() diff --git a/addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd.uid b/addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd.uid new file mode 100644 index 0000000..1f0929b --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd.uid @@ -0,0 +1 @@ +uid://nv51xk8g8h1r diff --git a/addons/escoria-core/game/core-scripts/esc_animation_player.gd b/addons/escoria-core/game/core-scripts/esc_animation_player.gd new file mode 100644 index 0000000..66b908e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_animation_player.gd @@ -0,0 +1,276 @@ +## An abstraction class to expose the same animation methods for both +## AnimatedSprite and AnimationPlayer. +extends Node +class_name ESCAnimationPlayer + + +## Signal emitted when the animation finished playing.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`Variant`|Name of the animation that completed playback.|yes|[br] +## [br] +signal animation_finished(name) + + +## The actual player node. +var _player_node: Node + +## An AnimationPlayer typed reference to the player node (for intellisense). +var _animation_player: AnimationPlayer + +## An AnimatedSprite2D typed reference to the player node (for intellisense). +var _animated_sprite: AnimatedSprite2D + +## Whether the player node is of type AnimationPlayer (just for convenience). +var _is_animation_player: bool = false + +## Currently running animation. +var _current_animation: String = "" + + +## Create a new animation player.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |node|`Node`|The actual player node.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(node: Node): + _player_node = node + if node is AnimationPlayer: + _is_animation_player = true + _animation_player = node + else: + _animated_sprite = node + node.add_child(self) + + +## Ready function. Connects animation signals.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready() -> void: + if _is_animation_player: + _player_node.animation_finished.connect(_on_animation_finished) + else: + _player_node.animation_finished.connect(_on_animation_finished_animated_sprite) + + +## Return the currently playing animation.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `String` value. (`String`) +func get_animation() -> String: + if _is_animation_player: + return _animation_player.assigned_animation + else: + return _animated_sprite.animation + + +## A list of all animation names.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a list of all animation names. (`PackedStringArray`) +func get_animations() -> PackedStringArray: + if _is_animation_player: + return _animation_player.get_animation_list() + else: + return _animated_sprite.sprite_frames.get_animation_names() + + +## Whether the animation is playing.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func is_playing() -> bool: + return _player_node.is_playing() + + +## Stop the animation.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func stop(): + _player_node.stop() + + +## Play the animation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Animation name to play.|yes|[br] +## |backwards|`bool`|Play backwards.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func play(name: String, backwards: bool = false): + if _is_animation_player and _animation_player.current_animation != "": + _animation_player.seek(0) + elif not _is_animation_player: + _animated_sprite.frame = 0 + + _current_animation = name + + if backwards and _is_animation_player: + _animation_player.play_backwards(name) + elif backwards: + _animated_sprite.play(name, true) + else: + _player_node.play(name) + + # Instead of waiting for the next frame, start the animation now. + if _is_animation_player: + _player_node.advance(0) + + +## Play the given animation backwards.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Animation name to play in reverse.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func play_backwards(name: String): + self.play(name, true) + + +## Check if the given animation exists.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Animation name to test for availability.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func has_animation(name: String) -> bool: + if _is_animation_player: + return _animation_player.has_animation(name) + else: + return _animated_sprite.sprite_frames.has_animation(name) + + +## Play an animation and directly skip to the end.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Animation name to jump to the last frame of.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func seek_end(name: String): + if _is_animation_player: + _animation_player.current_animation = name + _animation_player.seek(_animation_player.get_animation(name).length, true) + else: + _animated_sprite.animation = name + _animated_sprite.frame = _animated_sprite.sprite_frames.get_frame_count(name) + + +## Get the length of the specified animation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Animation name whose duration should be returned.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `float` value. (`float`) +func get_length(name: String) -> float: + if _is_animation_player: + return _animation_player.get_animation(name).length + else: + return _animated_sprite.sprite_frames.get_frame_count(name) - 1 * \ + _animated_sprite.sprite_frames.get_animation_speed(name) + + +## Return true if the ESCAnimationPlayer node is valid, ie. it has a valid player node.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func is_valid() -> bool: + return _player_node != null and _player_node is Node + + +## Transport the animation_finished signal.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Name of the animation that triggered the callback.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_animation_finished(name: String): + if _is_animation_player and not _animation_player.get_animation(name).loop_mode != Animation.LOOP_NONE: + _animation_player.stop(true) # param here is to keep current state and + # avoid resetting animation position to 0 + elif not _animated_sprite.sprite_frames.get_animation_loop(name): + _animated_sprite.stop() + animation_finished.emit(name) + + +## Special signal handler for animated sprites.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_animation_finished_animated_sprite(): + _on_animation_finished(_current_animation) diff --git a/addons/escoria-core/game/core-scripts/esc_animation_player.gd.uid b/addons/escoria-core/game/core-scripts/esc_animation_player.gd.uid new file mode 100644 index 0000000..2c253f6 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_animation_player.gd.uid @@ -0,0 +1 @@ +uid://c7s1pi5d3fpkv diff --git a/addons/escoria-core/game/core-scripts/esc_background.gd b/addons/escoria-core/game/core-scripts/esc_background.gd new file mode 100644 index 0000000..fff871d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_background.gd @@ -0,0 +1,191 @@ +@tool +@icon("res://addons/escoria-core/design/esc_background.svg") +## ESCBackground's purpose is to display a background image and receive input +## events on the background. More precisely, the TextureRect under ESCBackground +## does not receive events itself - if it did, it would also eat all events like +## hotspot focusing and such. Instead, we set the TextureRect mouse filter to +## MOUSE_FILTER_IGNORE, and we use an Area2D node to receive the input events.[br] +## [br] +## If ESCBackground doesn't contain a texture, it is important that its rect_size +## is set over the whole scene, because its rect_size is then used to create the +## Area2D node under it. If the rect_size is wrongly set, the background may +## receive no input. +extends TextureRect +class_name ESCBackground + + +## The background was double clicked[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Variant`|The position where the player clicked|yes|[br] +## [br] +signal double_left_click_on_bg(position) + +## The background was left clicked[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Variant`|The position where the player clicked|yes|[br] +## [br] +signal left_click_on_bg(position) + +## The background was right clicked[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Variant`|The position where the player clicked|yes|[br] +## [br] +signal right_click_on_bg(position) + +## Emitted when the mouse wheel was turned up[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal mouse_wheel_up + +## Emitted when the mouse wheel was turned down[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal mouse_wheel_down + +## Emitted when the background is hovered.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal hovered_bg + +## The ESC/ASH script connected to this background +@export_file("*.esc", "*.ash") var esc_script: String = "" + + +## Create the underlying Area2D as an input device[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _enter_tree(): + name = "escbackground" + var size + if get_texture(): + size = get_texture().get_size() + else: + size = self.size + + var area = Area2D.new() + var shape = RectangleShape2D.new() + + var sid = area.create_shape_owner(area) + + # Move origin of Area2D to center of Sprite + var transform = area.shape_owner_get_transform(sid) + transform.origin = size / 2 + area.shape_owner_set_transform(sid, transform) + + # Set extents of RectangleShape2D to cover entire TextureRect + shape.size = size / 2 + area.shape_owner_add_shape(sid, shape) + + add_child(area) + +## Disable mouse filter events and connect our own events to the ESC input manager[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + mouse_filter = MOUSE_FILTER_IGNORE + + # If background has no texture, set its rect size to viewport size + if texture == null and size == Vector2.ZERO: + size = escoria.game_size + + if !Engine.is_editor_hint(): + escoria.inputs_manager.register_background(self) + + +## Manage inputs reaching the Area2D and emit the events to the input manager[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`InputEvent`|Event received|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _unhandled_input(event: InputEvent) -> void: + var is_default_state = escoria.current_state == escoria.GAME_STATE.DEFAULT + if escoria.inputs_manager.try_custom_input_handler(event, is_default_state): + return + if not is_default_state: + return + if event is InputEventMouseMotion: + hovered_bg.emit() + if event is InputEventMouseButton and event.is_pressed(): + if event.button_index == MOUSE_BUTTON_WHEEL_UP: + mouse_wheel_up.emit() + elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: + mouse_wheel_down.emit() + var p = get_global_mouse_position() + var size + if get_texture(): + size = get_texture().get_size() + else: + size = self.size + if Rect2(position, size).has_point(p): + if event.double_click and event.button_index == MOUSE_BUTTON_LEFT: + double_left_click_on_bg.emit(p) + elif event.button_index == MOUSE_BUTTON_LEFT: + left_click_on_bg.emit(p) + elif event.button_index == MOUSE_BUTTON_RIGHT: + right_click_on_bg.emit(p) + + +## Calculate the actual area taken by this background depending on its Texture or set size[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Rect2` value. (`Rect2`) +func get_full_area_rect2() -> Rect2: + var area_rect2: Rect2 = Rect2() + var pos = get_global_position() + var size: Vector2 + if get_texture(): + size = get_texture().get_size() + else: + size = size + + if scale.x != 1 or scale.y != 1: + size.x *= scale.x + size.y *= scale.y + + area_rect2 = area_rect2.expand(pos) + area_rect2 = area_rect2.expand(pos + size) + return area_rect2 diff --git a/addons/escoria-core/game/core-scripts/esc_background.gd.uid b/addons/escoria-core/game/core-scripts/esc_background.gd.uid new file mode 100644 index 0000000..e57cf94 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_background.gd.uid @@ -0,0 +1 @@ +uid://fnwnfflaoeqp diff --git a/addons/escoria-core/game/core-scripts/esc_dialog_location.gd b/addons/escoria-core/game/core-scripts/esc_dialog_location.gd new file mode 100644 index 0000000..0d5c7c1 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_dialog_location.gd @@ -0,0 +1,24 @@ +@tool +@icon("res://addons/escoria-core/design/esc_location.svg") +## A simple node extending Position2D with a global ID so that it can be +## referenced in ESC Scripts. Movement-based commands like `walk_to_pos` will +## automatically use an `ESCLocation` that is a child of the destination node. +## Commands like `turn_to`--which are not movement-based--will ignore child +## `ESCLocation`s and refer to the parent node. +extends ESCLocation +class_name ESCDialogLocation + + +## Whether this object's class is the same as given string class name.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_classname|`String`|String class to compare against|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func is_class(p_classname: String) -> bool: + return p_classname == "ESCDialogLocation" diff --git a/addons/escoria-core/game/core-scripts/esc_dialog_location.gd.uid b/addons/escoria-core/game/core-scripts/esc_dialog_location.gd.uid new file mode 100644 index 0000000..e2b6cda --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_dialog_location.gd.uid @@ -0,0 +1 @@ +uid://85bledusisk1 diff --git a/addons/escoria-core/game/core-scripts/esc_exit.gd b/addons/escoria-core/game/core-scripts/esc_exit.gd new file mode 100644 index 0000000..79db99a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_exit.gd @@ -0,0 +1,100 @@ +@icon("res://addons/escoria-core/design/esc_exit.svg") +## An ESCExit is a minimal feature node that provides an exit to a room.[br] +## [br] +## For exits that don't require scripts, the ``ESCExit`` node is provided. +## The only things you will need to configure on the node are the +## "target_scene" to change to, and optionally, a "switch +## sound" (the sound to play when changing rooms).[br] +## [br] +## If you want to attach the exit to a script to perform additional actions - +## a cutscene for example - use an ``ESCItem`` with "Is Exit" selected instead.[br] +## [br] +## The game character will automatically walk to an ``ESCLocation`` created as a +## child of an ``ESCExit`` node. +extends ESCItem +class_name ESCExit + + +## Path to the target scene to change to +@export_file("*.tscn", "*.scn") var target_scene: String = "" + +## Sound effect to play when changing the scene +@export var switch_sound: Resource # (String, FILE, "*.ogg,*.mp3,*.wav") + +## String value to be used as UI hint, for example to determine the mouse cursor to use. +@export var exit_direction = "" + +## ESC PlaySnd command kept around for reference +var _play_snd: PlaySndCommand + +## ESC ChangeScene command kept around for reference +var _change_scene: ChangeSceneCommand + + +## Called on entering scene tree. Force is_exit to true.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _enter_tree(): + is_exit = true + + +## Called when node is ready.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + super._ready() + _play_snd = PlaySndCommand.new() + _change_scene = ChangeSceneCommand.new() + + call_deferred("_register_event") + + +## Registers the exit_scene event based on the properties[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _register_event(): + if escoria.object_manager.has(self.global_id) and\ + not escoria.event_manager.EVENT_EXIT_SCENE in escoria.object_manager.get_object( + self.global_id + ).events: + + var exit_scene_script_builder: ESCScriptBuilder = ESCScriptBuilder.new() + + exit_scene_script_builder.add_event(escoria.event_manager.EVENT_EXIT_SCENE, []) + exit_scene_script_builder.begin_block() + + if switch_sound != null: + exit_scene_script_builder.add_command( + _play_snd.get_command_name(), + switch_sound.resource_path + ) + + exit_scene_script_builder.add_command( + _change_scene.get_command_name(), + target_scene + ) + + var exit_scene_event = \ + escoria.esc_compiler.compile(exit_scene_script_builder.build(), get_class()) \ + .events[escoria.event_manager.EVENT_EXIT_SCENE] + + escoria.object_manager.get_object(self.global_id) \ + .events[escoria.event_manager.EVENT_EXIT_SCENE] = exit_scene_event diff --git a/addons/escoria-core/game/core-scripts/esc_exit.gd.uid b/addons/escoria-core/game/core-scripts/esc_exit.gd.uid new file mode 100644 index 0000000..bba2048 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_exit.gd.uid @@ -0,0 +1 @@ +uid://btt4cprco53ec diff --git a/addons/escoria-core/game/core-scripts/esc_game.gd b/addons/escoria-core/game/core-scripts/esc_game.gd new file mode 100644 index 0000000..9a424ad --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_game.gd @@ -0,0 +1,774 @@ +## Base class for ESC game scenes +## An extending class can be used in the project settings and is responsible +## for managing very basic game features and controls +extends Node2D +class_name ESCGame + + +## Signal emitted when the user has confirmed the crash popup[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal crash_popup_confirmed + +## Signal emitted when pause menu has to be displayed[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal request_pause_menu + + +## Editor debug modes[br] +## NONE - No debugging[br] +## MOUSE_TOOLTIP_LIMITS - Visualize the tooltip limits +enum EDITOR_GAME_DEBUG_DISPLAY { + NONE, # No debugging + MOUSE_TOOLTIP_LIMITS # Visualize the tooltip limits +} + + +## Main menu node +@export var main_menu: NodePath + +## Pause menu node +@export var pause_menu: NodePath + +## The safe margin around tooltips +@export var mouse_tooltip_margin: float = 50.0 + +## Debug mode for the editor (None, Mouse Tooltips Limit) +@export var editor_debug_mode: EDITOR_GAME_DEBUG_DISPLAY = EDITOR_GAME_DEBUG_DISPLAY.NONE: + set = _set_editor_debug_mode + +## The Control node underneath which all UI must be placed. +## This should be a Control node and NOT a CanvasLayer (or any other type of) node. +@export var ui_parent_control_node: NodePath + +## Reference to the node handling tooltips +var tooltip_node: Object + +## Boolean indicating whether the game scene is ready to accept inputs +## from the player. This enables using escoria.is_ready_for_inputs() in _input() +## function of game.gd script. +var room_ready_for_inputs: bool = false + +## Displayer node for hover stack debugging +var hover_stack_displayer: Node + + +## Function called when ESCGame enters the scene tree.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _enter_tree(): + initialize_esc_game() + + +## Method initializing the Escoria game: connects some signals from managers, setups some UI nodes.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func initialize_esc_game() -> void: + if not escoria.event_manager.event_finished.is_connected(_on_event_done): + escoria.event_manager.event_finished.connect(_on_event_done) + if not escoria.action_manager.action_finished.is_connected(_on_action_finished): + escoria.action_manager.action_finished.connect(_on_action_finished) + if not escoria.main.room_ready.is_connected(_on_room_ready): + escoria.main.room_ready.connect(_on_room_ready) + + get_node(main_menu).process_mode = ProcessMode.PROCESS_MODE_ALWAYS + get_node(pause_menu).process_mode = ProcessMode.PROCESS_MODE_ALWAYS + + # Debug display for hover stack + if ProjectSettings.get_setting(ESCProjectSettingsManager.ENABLE_HOVER_STACK_VIEWER) \ + and get_node_or_null("hover_stack_layer") == null: + hover_stack_displayer = preload( + "res://addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn" + ).instantiate() + add_child(hover_stack_displayer) + escoria.inputs_manager.hover_stack.connect("hover_stack_changed", Callable(hover_stack_displayer, "update")) + + +## Function called when ESCGame exits the scene tree.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _exit_tree(): + escoria.event_manager.event_finished.disconnect(_on_event_done) + escoria.action_manager.action_finished.disconnect(_on_action_finished) + escoria.main.room_ready.disconnect(_on_room_ready) + + +## Ready function[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + escoria.settings_manager.apply_settings() + crash_popup_confirmed.connect(escoria.quit) + +## Handle debugging visualizations[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _draw(): + if not Engine.is_editor_hint(): + return + if editor_debug_mode == EDITOR_GAME_DEBUG_DISPLAY.NONE: + return + + if editor_debug_mode == EDITOR_GAME_DEBUG_DISPLAY.MOUSE_TOOLTIP_LIMITS: + var mouse_limits: Rect2 = get_viewport_rect().grow( + -mouse_tooltip_margin + ) + print("ESC {0}".format([mouse_limits])) + + # Draw lines for tooltip limits + draw_rect(mouse_limits, Color.RED, false, 10.0) + + +## Clears the tooltip content (if an ESCTooltip node exists in UI)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func clear_tooltip(): + if tooltip_node != null: + (tooltip_node as ESCTooltip).clear() + + +## Sets up and performs default walking action.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |destination|`Variant`|Destination to walk to|yes|[br] +## |params|`Array`|Parameters for the action|no|[br] +## |can_interrupt|`bool`|if true, this command will interrupt any ongoing event|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func do_walk(destination, params: Array = [], can_interrupt: bool = false) -> void: + if can_interrupt: + escoria.event_manager.interrupt() + + escoria.action_manager.clear_current_action() + + var walk_fast = false + + if params.size() > 1: + walk_fast = true if params[1] else false + + # Check moving object. + if not escoria.object_manager.has(params[0]): + escoria.logger.error( + self, + "Walk action requested on nonexisting object: %s." % params[0] + ) + return + + var moving_obj = escoria.object_manager.get_object(params[0]) + var target + + if destination is String: + if not escoria.object_manager.has(destination): + escoria.logger.error( + self, + "Walk action requested to nonexisting object: %s." % destination + ) + return + + target = escoria.object_manager.get_object(destination) + elif destination is Vector2: + target = destination + + escoria.action_manager.perform_walk(moving_obj, target, walk_fast) + + +## Called when a mouse motion happens on the background.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func hovered_bg() -> void: + pass + + +## Called when the player left clicks on the background. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|clicked position|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func left_click_on_bg(position: Vector2) -> void: + if escoria.main.current_scene.player: + do_walk( + position, + [escoria.main.current_scene.player.global_id], + true + ) + else: + escoria.logger.trace( + self, + "No player loaded for current scene. Ignoring left click on background." + ) + + +## Called when the player right clicks on the background. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|clicked position|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func right_click_on_bg(position: Vector2) -> void: + if escoria.main.current_scene.player: + do_walk( + position, + [escoria.main.current_scene.player.global_id], + true + ) + else: + escoria.logger.trace( + self, + "No player loaded for current scene. Ignoring right click on background." + ) + + +## Called when the player double clicks on the background. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|clicked position|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func left_double_click_on_bg(position: Vector2) -> void: + if escoria.main.current_scene.player: + do_walk( + position, + [escoria.main.current_scene.player.global_id, true], + true + ) + else: + escoria.logger.trace( + self, + "No player loaded for current scene. Ignoring left double-click on background." + ) + + +## Called when an element in the scene was focused. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |element_id|`String`|Global id of the focused element|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func element_focused(element_id: String) -> void: + pass + + +## Called when no element is focused anymore (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func element_unfocused() -> void: + pass + + +## Called when an item was left clicked. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_global_id|`String`|Global id of the item that was clicked|yes|[br] +## |event|`InputEvent`|The received input event|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func left_click_on_item(item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_LEFT_CLICK, + [item_global_id, event], + true + ) + + +## Called when an item was right clicked. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_global_id|`String`|Global id of the item that was clicked|yes|[br] +## |event|`InputEvent`|The received input event|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func right_click_on_item(item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_RIGHT_CLICK, + [item_global_id, event], + true + ) + + +## Called when an item was double clicked (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_global_id|`String`|Global id of the item that was clicked|yes|[br] +## |event|`InputEvent`|The received input event|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func left_double_click_on_item( + item_global_id: String, + event: InputEvent +) -> void: + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_LEFT_CLICK, + [item_global_id, event], + true + ) + + +## Called when an inventory item was left clicked (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|Global id of the inventory item was clicked|yes|[br] +## |event|`InputEvent`|The received input event|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func left_click_on_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + pass + + +## Called when an inventory item was right clicked. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|Global id of the inventory item was clicked|yes|[br] +## |event|`InputEvent`|The received input event|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func right_click_on_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + pass + + +## Called when an inventory item was double clicked. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|Global id of the inventory item was clicked|yes|[br] +## |event|`InputEvent`|The received input event|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func left_double_click_on_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + pass + + +## Called when an inventory item was focused. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|Global id of the inventory item that was focused|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func inventory_item_focused(inventory_item_global_id: String) -> void: + pass + + +## Called when no inventory item is focused anymore. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func inventory_item_unfocused() -> void: + pass + + +## Called when the inventory was opened. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func open_inventory(): + pass + + +## Called when the inventory was closed. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func close_inventory(): + pass + + +## Called when the mousewheel was used. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |direction|`int`|The direction in which the mouse wheel was rotated|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func mousewheel_action(direction: int): + pass + + +## Called when the UI should be hidden. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func hide_ui(): + pass + + +## Called when the UI should be shown. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func show_ui(): + pass + + +## Set the Editor debug mode.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_editor_debug_mode|`int`|EDITOR_GAME_DEBUG_DISPLAY enum (int) value corresponding to the desired editor debug mode|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_editor_debug_mode(p_editor_debug_mode: int) -> void: + editor_debug_mode = p_editor_debug_mode + queue_redraw() + + +## Automatically called whenever an event is finished. Can be used to reset some UI elements to their default/empty state. This function can be called before _on_action_finished() if the player input started an event. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |_return_code|`int`|return code of the event (type ESCExecution)|yes|[br] +## |_event_name|`String`|name of the event that was just done (can be unused)|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_event_done(_return_code: int, _event_name: String) -> void: + pass + + +## Automatically called whenever an action initiated by the player is finished. Can be used to reset some UI elements to their default/empty state. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_action_finished() -> void: + pass + + +## Pauses the game. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func pause_game(): + escoria.set_game_paused(true) + + +## Unpauses the game. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func unpause_game(): + escoria.set_game_paused(false) + + +## Shows the main menu. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func show_main_menu(): + pass + + +## Hides the main menu. (Needs to be overridden, if supported)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func hide_main_menu(): + pass + + +## Custom function that is meant to apply custom settings. Called right after Escoria settings file was loaded.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |custom_settings|`Dictionary`|dictionary containing the custom settings.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func apply_custom_settings(custom_settings: Dictionary): + pass + + +## Custom function automatically called when save game is created. game file.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` value. (`Dictionary`) +func get_custom_data() -> Dictionary: + return {} + + +## Shows the crash popup when a crash occurs.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |files|`Array`|Array of strings containing the paths to the files generated on crash|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func show_crash_popup(files: Array = []) -> void: + var crash_popup = AcceptDialog.new() + crash_popup.exclusive = true + crash_popup.process_mode = Node.PROCESS_MODE_ALWAYS + add_child(crash_popup) + var files_to_send: String = "" + for file in files: + files_to_send += "- %s\n" % file + crash_popup.dialog_text = tr(ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.CRASH_MESSAGE) + ) % files_to_send + crash_popup.popup_centered() + escoria.set_game_paused(true) + await crash_popup.confirmed + crash_popup_confirmed.emit() + + +## *** FOR USE BY ESCORIA CORE ONLY *** Hides everything under the UI Control node.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func escoria_hide_ui(): + if ui_parent_control_node != null and not ui_parent_control_node.is_empty(): + (get_node(ui_parent_control_node) as Control).visible = false + else: + escoria.logger.warn( + self, + "UI parent Control node not defined!" + ) + + +## *** FOR USE BY ESCORIA CORE ONLY *** Show everything under the UI Control node.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func escoria_show_ui(): + if ui_parent_control_node != null and not ui_parent_control_node.is_empty(): + (get_node(ui_parent_control_node) as Control).visible = true + else: + escoria.logger.warn( + self, + "UI parent Control node not defined!" + ) + + +## Manage signal room_ready from main.gd.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_room_ready(): + room_ready_for_inputs = true + + +## Input function to manage specific input keys. Note that if any child of this class wishes to override _input, the overriding method MUST call its parent's version (i.e. this method).[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`InputEvent`|input event to manage|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _input(event: InputEvent): + if escoria.inputs_manager.input_mode == escoria.inputs_manager.INPUT_NONE: + return + + if event.is_action_pressed("ui_cancel"): + request_pause_menu.emit() diff --git a/addons/escoria-core/game/core-scripts/esc_game.gd.uid b/addons/escoria-core/game/core-scripts/esc_game.gd.uid new file mode 100644 index 0000000..6a84970 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_game.gd.uid @@ -0,0 +1 @@ +uid://cve0wqkwjq5xf diff --git a/addons/escoria-core/game/core-scripts/esc_interaction_location.gd b/addons/escoria-core/game/core-scripts/esc_interaction_location.gd new file mode 100644 index 0000000..4fe84fb --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_interaction_location.gd @@ -0,0 +1,23 @@ +@tool +@icon("res://addons/escoria-core/design/esc_location.svg") +## A simple node extending Position2D with a global ID so that it can be +## referenced in ESC Scripts. Movement-based commands like `walk_to_pos` will +## automatically use an `ESCLocation` that is a child of the destination node.[br] +## Commands like `turn_to`--which are not movement-based--will ignore child +## `ESCLocation`s and refer to the parent node. +extends ESCLocation +class_name ESCInteractionLocation + +## Whether this object's class is the same as given string class name.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_classname|`String`|String class to compare against|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func is_class(p_classname: String) -> bool: + return p_classname == "ESCInteractionLocation" diff --git a/addons/escoria-core/game/core-scripts/esc_interaction_location.gd.uid b/addons/escoria-core/game/core-scripts/esc_interaction_location.gd.uid new file mode 100644 index 0000000..cdfed8d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_interaction_location.gd.uid @@ -0,0 +1 @@ +uid://chu4bvqgwxuts diff --git a/addons/escoria-core/game/core-scripts/esc_inventory_item.gd b/addons/escoria-core/game/core-scripts/esc_inventory_item.gd new file mode 100644 index 0000000..560c6c1 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_inventory_item.gd @@ -0,0 +1,29 @@ +## Container class containing basic information about an inventory item. +class_name ESCInventoryItem + + +## Global ID of the ESCItem that uses this ESCInventoryItem +var global_id: String = "" + +## The texture for the item +var texture_normal: Texture2D = null + +## The texture for the item when hovered +var texture_hovered: Texture2D = null + + +## Constructs the inventory item from an existing ESCItem.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_item|`ESCItem`|ESCItem instance to create the inventory item object from|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(p_item: ESCItem) -> void: + global_id = p_item.global_id + texture_normal = p_item._get_inventory_texture() + texture_hovered = p_item._get_inventory_texture_hovered() diff --git a/addons/escoria-core/game/core-scripts/esc_inventory_item.gd.uid b/addons/escoria-core/game/core-scripts/esc_inventory_item.gd.uid new file mode 100644 index 0000000..88a6e6c --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_inventory_item.gd.uid @@ -0,0 +1 @@ +uid://jusovdeiq64s diff --git a/addons/escoria-core/game/core-scripts/esc_item.gd b/addons/escoria-core/game/core-scripts/esc_item.gd new file mode 100644 index 0000000..5f58b33 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_item.gd @@ -0,0 +1,1407 @@ +@tool +@icon("res://addons/escoria-core/design/esc_item.svg") +## An ``ESCItem`` defines a (usually interactive) item in the game. +## +## When interacting with an ``ESCItem``, the game character will automatically +## walk to an ``ESCLocation`` that is created as a child of an ``ESCItem``.[br] +##[br] +## By selecting the "Is Exit" checkbox when you create an ``ESCItem`` +## node, Escoria will look for an ``:exit_scene`` event in the attached script file. +## Any commands you place in the ``:exit_scene`` event will be run when the player +## chooses to "use" the exit - for example, saying a goodbye, or running a +## cutscene. Place a ``change_scene`` command inside this event to move the +## character to the next room. +extends Area2D +class_name ESCItem + + +## List of forbidden characters in global_ids +const FORBIDDEN_CHARACTERS: String = "['\"]" + + +## Emitted when the mouse has entered this item[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`Variant`|Item that emitted the signal.|yes|[br] +## [br] +signal mouse_entered_item(item) + +## Emitted when the mouse has exited this item[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`Variant`|Item that emitted the signal.|yes|[br] +## [br] +signal mouse_exited_item(item) + +## Emitted when the item was left clicked[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`Variant`|ID of this item|yes|[br] +## [br] +signal mouse_left_clicked_item(global_id) + +## Emitted when the item was double clicked[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`Variant`|ID of this item|yes|[br] +## [br] +signal mouse_double_left_clicked_item(global_id) + +## Emitted when the item was right clicked[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global_id|`Variant`|ID of this item|yes|[br] +## [br] +signal mouse_right_clicked_item(global_id) + +## Emitted when the item walked to a destination[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |walk_context|`Variant`|The walk context of the command|yes|[br] +## [br] +signal arrived(walk_context) + + +## The global ID of this item +@export var global_id: String + +## The ASHES script for this item +@export_file("*.esc", "*.ash") var esc_script: String + +## The node that references the camera position and zoom if this item is used +## as a camera target +@export var camera_node: NodePath + +@export_group("Display") + +## The name for the tooltip of this item. +@export var tooltip_name: String + +@export_group("","") +@export_group("Item Behavior") + +## Whether this item is movable. A movable item will be scaled with the terrain +## and be moved with commands like ``teleport`` and ``turn_to``. +@export var is_movable: bool = false + +## Color used for dialogs if that item talks. +@export var dialog_color: Color = Color(1,1,1,1) + +## Default action to use if object is not in the inventory. +@export var default_action: String + +## If action used by player is in this list, the game will wait for a second +## click on another item to combine objects together (typically +## `USE WITH `, `GIVE TO `) +@export var combine_when_selected_action_is_in: PackedStringArray = [] + +## If enabled, the ASHES script may have an :exit_scene event to manage scene changes. +## For simple exits that do not require scripted actions, the ESCExit node may be +## preferred. +@export var is_exit: bool + +## Defines this item as acting as a trigger if enabled.[br] +## Allows using specific events (defined in trigger_in_verb and trigger_out_verb +## properties) in ASHES scripts. +@export var is_trigger: bool + +## Event name that is activated when another item enters the area defined by the +## trigger item. By default, "trigger_in". +@export var trigger_in_verb: String = "trigger_in" + +## Event name that is activated when another item exits the area defined by the +## trigger item. By default, "trigger_out". +@export var trigger_out_verb: String = "trigger_out" + +## Defines whether the player can interact with this item. If false, the item +## will not react to inputs and mouse hovers. +@export var is_interactive: bool = true + + +@export_subgroup("Hover Behavior") + +## Defines whether Escoria will manage a specific hover behavior when the item +## is focused. All options below can be used together.[br] +## This can also be expanded or overriden in your ESCGame implementation +## (in methods ``element_focused()`` and ``element_unfocused()``). +@export var hover_enabled: bool = false + +## If hover is enabled, applies this color modulation on the ESCItem sprite. +@export var hover_modulate: Color = Color.WHITE +var _previous_color_modulate: Color + +## If hover is enabled, replaces this ESCItem sprite texture by this one. +@export var hover_texture: Texture2D = null +var _previous_texture: Texture2D = null + +## If hover is enabled, applies this shader to the ESCItem sprite. +@export var hover_shader: ShaderMaterial = null + +@export_subgroup("","") + +@export_group("","") +@export_group("Player behavior on arrival") + +## Whether player character orients towards 'interaction_angle' as it arrives at +## the item's interaction position. +@export var player_orients_on_arrival: bool = true + +## If 'player_orients_on_arrival' is enabled, let the player character turn to +## this angle when it arrives at the item's interaction position. +@export var interaction_angle: int + +@export_group("","") +@export_group("Inventory") + +## Default action to use if object is in the inventory +@export var default_action_inventory: String + +## If enabled, combination must be done in the way it is written in ASHES script +## ie. :use ON_ITEM[br] +## If disabled, combination will be tried in the other way. +@export var combine_is_one_way: bool = false + +## If enabled, then the object must have been picked up before using it. +## Keep disabled for items in the background, such as buttons. +@export var use_from_inventory_only: bool = false + +## The visual representation for this item when it's in the inventory +@export var inventory_texture: Texture2D = null: + get = _get_inventory_texture + +## The visual representation for this item when it's in the inventory and hovered +@export var inventory_texture_hovered: Texture2D = null: + get = _get_inventory_texture_hovered + +@export_group("","") +@export_group("Movement") + +## If enabled, terrain scaling will not be applied and +## node will remain at the scale set in the scene. +@export var dont_apply_terrain_scaling: bool = false + +## Speed of this item if movable +@export var speed: int = 300 + +## Speed damp of this item if movable +@export var v_speed_damp: float = 1.0 + +@export_group("","") +@export_group("Animations") + +## Animations resource for the item (walking, idling...) +@export var animations: ESCAnimationResource: + set = set_animations + +## The node used to play animations +@export var animation_player_node: NodePath = "": + set = _set_animation_player_node + +@export_group("","") +@export_group("Custom actions") + +## Custom data dictionary to ease customization and custom command creation. +## Avoid name collision using proper key names. +@export var custom_data: Dictionary = {}: + get = get_custom_data, + set = set_custom_data + + +@export_group("","") + +## Reference to the animation node (null if none was found) +var animation_sprite = null + +## Reference to the current terrain +var terrain: ESCTerrain + +## Reference to this items collision shape node +var collision: Node + + +## Reference to the sprite node +var _sprite_node: Node = null + +## The movable subnode +var _movable: ESCMovable = null + +## The identified animation player +var _animation_player: ESCAnimationPlayer = null + +## Whether to force regsitration with the object manager. Defaults to false. +var _force_registration: bool = false + +## Warnings for scene. +var _scene_warnings: PackedStringArray = [] + + +# Add the movable node, connect signals, detect child nodes +# and register this item +func _ready(): + self.process_mode = Node.PROCESS_MODE_PAUSABLE + + _detect_children() + + # We add ourselves to this group so we can easily get a reference to all + # items in a scene tree. + if not Engine.is_editor_hint(): + add_to_group(escoria.GROUP_ITEM_CAN_COLLIDE) + if is_trigger: + add_to_group(escoria.GROUP_ITEM_TRIGGERS) + + validate_animations(animations) + validate_exported_parameters() + + if not input_event.is_connected(_on_input_event): + input_event.connect(_on_input_event) + if not mouse_exited.is_connected(_on_mouse_exited): + mouse_exited.connect(_on_mouse_exited) + + # Register and connect all elements to Escoria backoffice. + if not Engine.is_editor_hint(): + + if is_movable: + _movable = ESCMovable.new() + add_child(_movable) + + if not escoria.event_manager.event_finished.is_connected(_update_terrain): + escoria.event_manager.event_finished.connect(_update_terrain) + + escoria.object_manager.register_object( + ESCObject.new( + global_id, + self + ), + null, + _force_registration + ) + + terrain = escoria.room_terrain if is_instance_valid(escoria.room_terrain) else null + + if escoria.object_manager.get_object(global_id).state == ESCObject.STATE_DEFAULT \ + and get_animation_player() != null: + escoria.object_manager.get_object(global_id) \ + .set_state(get_animation_player().get_animation()) + if is_movable: + escoria.object_manager.get_object(global_id).node \ + ._movable.last_dir = animations.get_direction_id_from_animation_name( + get_animation_player().get_animation() + ) + + + if escoria.object_manager.get_object(global_id).state == ESCObject.STATE_DEFAULT \ + and escoria.object_manager.get_object(global_id).node.get_animation_player() != null: + escoria.object_manager.get_object(global_id) \ + .set_state(get_animation_player().get_animation()) + if is_movable: + escoria.object_manager.get_object(global_id).node._movable.last_dir = 1 + #animations.get_direction_id_from_animation_name( + #.get_animation_player().get_animation() + #) + + if !is_trigger: + if not self.is_connected( + "mouse_entered_item", + escoria.inputs_manager._on_mouse_entered_item + ): + mouse_entered_item.connect( + escoria.inputs_manager._on_mouse_entered_item + ) + if not self.is_connected( + "mouse_exited_item", + escoria.inputs_manager._on_mouse_exited_item + ): + mouse_exited_item.connect( + escoria.inputs_manager._on_mouse_exited_item + ) + if not self.is_connected( + "mouse_left_clicked_item", + escoria.inputs_manager._on_mouse_left_clicked_item + ): + mouse_left_clicked_item.connect( + escoria.inputs_manager._on_mouse_left_clicked_item + ) + if not self.is_connected( + "mouse_double_left_clicked_item", + escoria.inputs_manager._on_mouse_left_double_clicked_item + ): + mouse_double_left_clicked_item.connect( + escoria.inputs_manager._on_mouse_left_double_clicked_item + ) + if not self.is_connected( + "mouse_right_clicked_item", + escoria.inputs_manager._on_mouse_right_clicked_item + ): + mouse_right_clicked_item.connect( + escoria.inputs_manager._on_mouse_right_clicked_item + ) + else: # Item is a trigger + if not self.is_connected("area_entered", _element_entered): + area_entered.connect(_element_entered) + if not self.is_connected("area_exited", _element_exited): + area_exited.connect(_element_exited) + if not self.is_connected("body_entered", _element_entered): + body_entered.connect(_element_entered) + if not self.is_connected("body_exited", _element_exited): + body_exited.connect(_element_exited) + + # If object can be in the inventory, set default_action_inventory to same as + # default_action, if default_action_inventory is not set + if use_from_inventory_only and default_action_inventory.is_empty(): + default_action_inventory = default_action + + # Perform a first terrain scaling if we have to. + if (not is_exit or dont_apply_terrain_scaling) and is_movable: + _movable.last_scale = scale + _movable.update_terrain() + +## For trigger items, enable monitoring to detect collisions.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func connect_trigger_events(): + assert(is_trigger) + self.monitoring = true + + +## Validates the various exported parameters so we get immediate crash.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func validate_exported_parameters() -> void: + var regex = RegEx.new() + regex.compile(FORBIDDEN_CHARACTERS) + var result = regex.search(global_id) + if result: + ESCSafeLogging.log_error( + self, + "Forbidden character in global_id %s (path: %s)" + % [global_id, get_path()] + ) + if global_id.is_empty(): + ESCSafeLogging.log_error( + self, + "global_id of item is empty (node name : %s, path: %s)" + % [name, get_path()] + ) + +## For trigger items, disable monitoring to disable collisions detection.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func disconnect_trigger_events(): + assert(is_trigger) + self.monitoring = false + + +## Mouse exited happens on any item that mouse cursor exited, even those UNDER the top level of overlapping stack.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_exited(): + if escoria.inputs_manager.hover_stack.has(self): + escoria.inputs_manager.hover_stack.erase_item(self) + escoria.inputs_manager.unset_hovered_node(self) + _apply_unhover_behavior() + +## Special helper class used to sort stacked items by z-index. +class HoverStackSorter: + static func sort_ascending_z_index(a, b): + if a.z_index < b.z_index: + return true + return false + + +## Manage input events on the item[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |_viewport|`Object`|the viewport node the event entered|yes|[br] +## |event|`InputEvent`|Input event delivered to the item.|yes|[br] +## |_shape_idx|`int`|Index of the collision shape within the item that received the event.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_input_event(_viewport: Object, event: InputEvent, _shape_idx: int): + if event is InputEventMouseMotion: + var physics2d_dss: PhysicsDirectSpaceState2D = get_world_2d().direct_space_state + var params := PhysicsPointQueryParameters2D.new() + params.position = get_global_mouse_position() + params.collision_mask = 0x7FFFFFFF + params.collide_with_areas = true + var colliding: Array = physics2d_dss.intersect_point(params, 32) + var colliding_nodes = [] + for c in colliding: + if c.collider.get("global_id") \ + and escoria.action_manager.is_object_actionable(c.collider.global_id): + colliding_nodes.push_back(c.collider) + + if colliding_nodes.is_empty(): + return + colliding_nodes.sort_custom(Callable(HoverStackSorter, "sort_ascending_z_index")) + escoria.inputs_manager.hover_stack.clear() + escoria.inputs_manager.hover_stack.add_items(colliding_nodes) + escoria.inputs_manager.set_hovered_node(colliding_nodes.back()) + +## Manage mouse button clicks on this item by sending out signals[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |input_event|`InputEvent`|Triggered event|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _unhandled_input(input_event: InputEvent) -> void: + # If this is a trigger, then escoria.inputs_manager is not wired up to + # receive the signals this function might dispatch. In particular, + # calling get_tree().set_input_as_handled() unnecessarily will prevent + # the ESCBackground from being able to process the event. + # See https://github.com/godot-escoria/escoria-issues/issues/147. + if is_trigger: + return + + var event = input_event + # Note that event could be InputEventMouseButton, InputEventJoypadButton, + # or something else. As such, the value of the `button_index` property + # must be read in the context of the type of input event. + if input_event is InputEventJoypadButton: + if not input_event.is_action_pressed(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION): + return + + # For now, rather than refactor input handling to be more generic + # to accommodate gamepad support, we create a synthetic mouse event + # based on the InputEventJoypadButton. + event = InputEventMouseButton.new() + event.button_index = MOUSE_BUTTON_LEFT + event.double_click = false + event.button_pressed = true + # ESCActionManager expects to read the position off of the event. + event.position = get_global_mouse_position() + + if event is InputEventMouseButton and event.is_pressed(): + if not escoria.current_state == escoria.GAME_STATE.DEFAULT: + ESCSafeLogging.log_info( + self, + "Current game state doesn't accept interactions." + ) + return + var p = get_global_mouse_position() + if _is_in_shape(p) and escoria.action_manager.is_object_actionable(global_id): + if event.double_click and event.button_index == MOUSE_BUTTON_LEFT: + mouse_double_left_clicked_item.emit(self, event) + get_viewport().set_input_as_handled() + elif event.button_index == MOUSE_BUTTON_LEFT: + mouse_left_clicked_item.emit(self, event) + get_viewport().set_input_as_handled() + elif event.button_index == MOUSE_BUTTON_RIGHT: + mouse_right_clicked_item.emit(self, event) + get_viewport().set_input_as_handled() + + +## To display warnings in the scene tree should there be any.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `String` value containing configuration warnings joined by newlines. (`String`) +func _get_configuration_warnings(): + validate_animations(animations) + return "\n".join(_scene_warnings) + +## Tests whether given point is inside a shape.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|point coordinates to test|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _is_in_shape(position: Vector2) -> bool: + var params := PhysicsPointQueryParameters2D.new() + params.position = position + params.collision_mask = 2147483647 + params.collide_with_areas = true + var colliders = get_world_2d().direct_space_state.intersect_point( + params, + 32 + ) + for _owner in get_shape_owners(): + for _shape_id in range(0, shape_owner_get_shape_count(_owner)): + for _collider in colliders: + if _collider.collider == self and _collider.shape == _shape_id: + return true + return false + + +## Validates the ESCAnimationResource if it exists. Note that we pass in the ESCAnimationResource as an argument so that it can also be used to validate an ESCAnimationResource prior to being set.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |animations_resource|`ESCAnimationResource`|Animation resource to validate before using it on this item.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func validate_animations(animations_resource: ESCAnimationResource) -> void: + if not is_instance_valid(animations_resource): + return + + # This initialization must always be here since this is a tool script. + _scene_warnings = [] + + if is_instance_valid(animations_resource): + _validate_animations_property_all_not_null(animations_resource.dir_angles, "dir_angles") + + var num_dir_angles = animations_resource.dir_angles.size() + + if animations_resource.directions.size() != num_dir_angles: + _scene_warnings.append("%s animation angles specified but %s 'directions' animation(s) given. [%s]" \ + % [num_dir_angles, animations_resource.directions.size(), _get_identifier_as_key_value()]) + else: + _validate_animations_property_all_not_null(animations_resource.directions, "directions") + + if animations_resource.idles.size() != num_dir_angles: + _scene_warnings.append("%s animation angles specified but %s 'idles' animation(s) given. [%s]" \ + % [num_dir_angles, animations_resource.idles.size(), _get_identifier_as_key_value()]) + else: + _validate_animations_property_all_not_null(animations_resource.idles, "idles") + + if animations_resource.speaks.size() != num_dir_angles: + _scene_warnings.append("%s animation angles specified but %s 'speaks' animation(s) given. [%s]" \ + % [num_dir_angles, animations_resource.speaks.size(), _get_identifier_as_key_value()]) + else: + _validate_animations_property_all_not_null(animations_resource.speaks, "speaks") + + if Engine.is_editor_hint(): + update_configuration_warnings() + elif _scene_warnings.size() > 0: + ESCSafeLogging.log_error( + self, + ", ".join(_scene_warnings) + ) + + +## Setter for the animations property.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_animations|`ESCAnimationResource`|the ESCAnimationResource to set.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_animations(p_animations: ESCAnimationResource) -> void: + if p_animations == null: + return + + animations = p_animations + + if not animations.is_connected("changed", Callable(self, "_validate_animations")): + animations.connect("changed", Callable(self, "_validate_animations")) + + +## The animation player node[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the animation player node. (`Node`) +func get_animation_player() -> Node: + if _animation_player == null: + var player_node_path = animation_player_node + if player_node_path.is_empty(): + for child in self.get_children(): + if child is AnimatedSprite2D or \ + child is AnimationPlayer: + player_node_path = child.get_path() + if player_node_path.is_empty(): + ESCSafeLogging.log_debug( + self, + "Can not find animation_player or animated sprite for %s." % global_id + ) + elif not has_node(player_node_path): + ESCSafeLogging.log_debug( + self, + "Can not find animation_player node at path %s for %s." % [player_node_path, global_id] + ) + else: + _animation_player = ESCAnimationPlayer.new( + get_node(player_node_path) + ) + return _animation_player + + +## Return the position the player needs to walk to to interact with this item. That can either be a direct Position2D child or a collision shape[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Vector2` value. (`Vector2`) +func get_interact_position() -> Vector2: + var pos_2d_count: int = 0 + var pos_2d_position = null + + var esclocation_count = 0 + var esclocation_position = null + + var interact_count = 0 + var interact_position = null + + for c in get_children(): + if c is Marker2D: + # Identify any Postion2D nodes + if c is ESCLocation: + esclocation_count += 1 + esclocation_position = c.global_position + elif c is ESCInteractionLocation: + interact_count += 1 + interact_position = c.global_position + else: + # This will catch all other Position2D related nodes + # including dialog locations and native Position2D nodes. + pos_2d_count += 1 + pos_2d_position = c.global_position + + if interact_position == null and \ + esclocation_position == null and is_instance_valid(collision): + ESCSafeLogging.log_warn( + self, + "No ESCLocation found to walk to for object " + + "%s. Middle of collision shape will be used." % global_id) + return collision.global_position + + if interact_count > 0: + if interact_count > 1: + ESCSafeLogging.log_warn( + self, + "Multiple ESCInteractionLocations found to walk to for " + + "object %s. Last one will be used." % global_id) + return interact_position + + if esclocation_count > 1: + ESCSafeLogging.log_warn( + self, + "Multiple ESClocations found to walk to for object " + + "%s. Last one will be used." % global_id) + return esclocation_position + + +## React to the mouse entering the item by emitting the respective signal[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func mouse_entered(): + if escoria.action_manager.is_object_actionable(global_id): + mouse_entered_item.emit(self) + _apply_hover_behavior() + + + +## React to the mouse exiting the item by emitting the respective signal[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func do_mouse_exited(): + mouse_exited_item.emit(self) + + +# Another item (e.g. the player) has entered this item +# +# #### Parameters +# +# - body: Other object that has entered the item +func _element_entered(body): + if body is ESCBackground or body.get_parent() is ESCBackground: + return + escoria.action_manager.do( + escoria.action_manager.ACTION.TRIGGER_IN, + [global_id, body.global_id, trigger_in_verb] + ) + + +# Another item (e.g. the player) has exited this element +# +# #### Parameters +# +# - body: Other object that has exited the item +func _element_exited(body): + if body is ESCBackground or body.get_parent() is ESCBackground: + return + escoria.action_manager.do( + escoria.action_manager.ACTION.TRIGGER_OUT, + [global_id, body.global_id, trigger_out_verb] + ) + + +## Use the movable node to teleport this item to the target item[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target|`Node`|Target node to teleport to|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func teleport(target: Node) -> void: + if is_movable: + _movable.teleport(target) + else: + ESCSafeLogging.log_warn( + self, + "Node %s cannot \"teleport\". Its \"is_movable\" parameter is false." %self + ) + + +## Use the movable node to teleport this item to the target position[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target|`Vector2`|Vector2 position to teleport to|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func teleport_to(target: Vector2) -> void: + if is_movable: + _movable.teleport_to(target) + else: + ESCSafeLogging.log_warn( + self, + "Node %s cannot \"teleport_to\". Its \"is_movable\" parameter is false." %self + ) + + +## Use the movable node to make the item walk to the given position[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pos|`Vector2`|Position to walk to|yes|[br] +## |p_walk_context|`ESCWalkContext`|Walk context to use|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func walk_to(pos: Vector2, p_walk_context: ESCWalkContext = null) -> void: + if is_movable: + _movable.walk_to(pos, p_walk_context) + else: + ESCSafeLogging.log_warn( + self, + "Node %s cannot use \"walk_to\". Its \"is_movable\" parameter is false." %self + ) + + +## Stop the movable node immediately and remain where it is at this moment, or teleport it directly at destination position if 'to_target' is true.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |to_target|`bool`|if true, the movable node is teleport directly at its target destination|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func stop_walking_now(to_target: bool = false) -> void: + if is_movable: + var where: Vector2 = position + if to_target: + where = _movable.walk_destination + _movable.walk_stop(where) + else: + ESCSafeLogging.log_warn( + self, + "Node %s cannot use \"stop_walking_now\". Its \"is_movable\" parameter is false." %self + ) + + +## Set the moving speed[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |speed_value|`int`|Set the new speed|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_velocity(speed_value: int) -> void: + speed = speed_value + + +## Checks whether this item moved[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func has_moved() -> bool: + return _movable.moved if is_movable else false + + +## Checks whether this item has a sprite.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func has_sprite() -> bool: + if _sprite_node != null: + return true + else: # confirm + for child in self.get_children(): + if child is AnimatedSprite2D or child is Sprite2D: + return true + return false + +## Return the sprite node.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Node` value. (`Node`) +func get_sprite() -> Node: + if _sprite_node == null: + for child in self.get_children(): + if child is AnimatedSprite2D or child is Sprite2D: + _sprite_node = child + if _sprite_node == null: + ESCSafeLogging.log_error( + self, + "No sprite node found in the scene %s." % get_path() + ) + return _sprite_node + + +## Set the angle[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |deg|`int`|The angle degree to set|yes|[br] +## |wait|`float`|Wait this amount of seconds until continuing with turning around|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_angle(deg: int, wait: float = 0.0): + if is_movable: + _movable.set_angle(deg, wait) + else: + ESCSafeLogging.log_warn( + self, + "Node %s cannot use \"set_angle\". Its \"is_movable\" parameter is false." % self + ) + + +## Set the direction id[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |direction_id|`int`|The direction id|yes|[br] +## |wait|`float`|Wait this amount of seconds until continuing with turning around|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_direction(direction_id: int, wait: float = 0.0): + if is_movable: + _movable.set_direction(direction_id, wait) + else: + ESCSafeLogging.log_warn( + self, + "Node %s cannot use \"set_direction\". Its \"is_movable\" parameter is false." % self + ) + + +## Turn to face another object[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`Node`|Node whose position the item should face.|yes|[br] +## |wait|`float`|Optional delay in seconds before execution continues after turning.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func turn_to(object: Node, wait: float = 0.0): + if is_movable: + _movable.turn_to(object, wait) + else: + ESCSafeLogging.log_warn( + self, + "Node %s cannot use \"turn_to\". Its \"is_movable\" parameter is false." % self + ) + + +## Check everything is in place to play talk animations. A warning indicating the problem is printed in console, if necessary.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func check_talk_possible() -> bool: + if is_movable and (_movable.last_dir < 0 \ + or _movable.last_dir >= animations.speaks.size()): + ESCSafeLogging.log_warn( + self, + "Node %s cannot talk. Its \"last_dir\" parameter is invalid: %s." \ + % [self, _movable.last_dir] + ) + return false + if not is_instance_valid(animations): + ESCSafeLogging.log_warn( + self, + "Node %s cannot talk. Its \"animations\" parameter is empty." \ + % self + ) + return false + if animations.speaks.size() == 0: + ESCSafeLogging.log_warn( + self, + "Node %s cannot talk. Its \"animations.speaks\" array is empty." \ + % self + ) + return false + if not get_animation_player(): + ESCSafeLogging.log_warn( + self, + "Node %s cannot talk. Its animation player can't be found." \ + % self + ) + return false + return true + + +## Play the talking animation[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func start_talking(): + if not check_talk_possible(): + return + + var animation_player = get_animation_player() + + if animation_player.is_playing(): + animation_player.stop() + + if is_movable and animations.speaks[_movable.last_dir].mirrored \ + and not _movable.is_mirrored: + _sprite_node.scale.x *= -1 + + var animation_direction = _movable.last_dir if is_movable else 0 + animation_player.play( + animations.speaks[animation_direction].animation + ) + + +## Stop playing the talking animation[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func stop_talking(): + if not check_talk_possible(): + return + + var animation_player = get_animation_player() + + if animation_player.is_playing(): + animation_player.stop() + + if is_movable: + if animations.speaks[_movable.last_dir].mirrored \ + and not _movable.is_mirrored: + # Allow this function to be called multiple times without setting + # the direction incorrectly + if _sprite_node.scale.x < 1: + _sprite_node.scale.x *= -1 + + animation_player.play( + animations.idles[_movable.last_dir].animation + ) + else: + animation_player.play( + animations.idles[0].animation + ) + + +## Replay the last idle animation[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func update_idle(): + get_animation_player().play( + animations.idles[_movable.last_dir].animation + ) + + +## Return the camera position if a camera_position_node exists or the global position of the player[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Node` value. (`Node`) +func get_camera_node() -> Node: + if has_node(camera_node): + ESCSafeLogging.log_debug( + self, + "Camera3D node found - directing camera to the camera_node on %s." + % global_id + ) + return get_node(camera_node) + return self + + +## Detect the child nodes and set respective references.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _detect_children() -> void: + # Initialize collision variable. + for c in get_children(): + if c is CollisionShape2D or c is CollisionPolygon2D: + collision = c + + +## Update the terrain when an event finished[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |rc|`int`|Return code of the event that just finished|yes|[br] +## |event_name|`String`|Event that just finished to provide to update terrain call.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _update_terrain(rc: int, event_name: String) -> void: + if is_movable: + _movable.update_terrain(event_name) + +## Godot method override to provide more information about this object.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _get_property_list(): + var properties = [] + properties.append({ + "name": "animations", + "type": TYPE_OBJECT, + "hint": PROPERTY_HINT_RESOURCE_TYPE, + "hint_string": "ESCAnimationResource" + }) + return properties + + +## Set the node path to the animation player[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |node_path|`NodePath`|Path to the player node|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_animation_player_node(node_path: NodePath): + if not Engine.is_editor_hint(): + return + + if node_path.is_empty(): + animation_player_node = node_path + return + + assert(has_node(node_path), "Node with path %s not found" % node_path) + assert( + get_node(node_path) is AnimatedSprite2D or \ + get_node(node_path) is AnimationPlayer, + "Selected node has to be an AnimatedSprite2D or AnimationPlayer node" + ) + + animation_player_node = node_path + + +## Either the set inventory texture or the texture of a TextureRect found as a child if it is null[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns either the set inventory texture or the texture of a TextureRect found as a child if it is null. (`Texture2D`) +func _get_inventory_texture() -> Texture2D: + if inventory_texture == null: + for c in get_children(): + if c is TextureRect or c is Sprite2D: + return c.texture + return null + else: + return inventory_texture + + +func _get_inventory_texture_hovered() -> Texture2D: + if inventory_texture_hovered == null: + return _get_inventory_texture() + else: + return inventory_texture_hovered + +## Checks whether the given ESCAnimationResource property array has all non-null entries, and adds to the scene's warnings if not[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |property|`Array`|ESCAnimationResource property. Must be an array.|yes|[br] +## |property_name|`String`|the name of the property being passed in.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _validate_animations_property_all_not_null(property: Array, property_name: String) -> void: + var has_empty_entry: bool = false + + for item in property: + if item == null: + has_empty_entry = true + break + + if has_empty_entry: + _scene_warnings.append("At least one entry in '%s' is empty. [%s]" % [property_name, _get_identifier_as_key_value()]) + + +## The global ID as a key/value pair. If none is specified, use the node name. Used to tag messages.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the global ID as a key/value pair. If none is specified, use the node name. Used to tag messages. (`String`) +func _get_identifier_as_key_value() -> String: + if self.global_id: + return "global_id: %s" % self.global_id + else: + return "node: %s" % get_name() + +## Applies hover behavior on this ESCItem: if hover is enabled, applies the defined hover color modulate and/or hover texture and/or hover shader on the Sprite2D node, if any.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _apply_hover_behavior() -> void: + if not hover_enabled: + return + if has_sprite(): + var sprite = get_sprite() + _previous_color_modulate = modulate + sprite.modulate = hover_modulate + if sprite is Sprite2D: + if hover_texture != null: + _previous_texture = sprite.texture + sprite.texture = hover_texture + if hover_shader != null: + sprite.material = hover_shader + +## Applies unhover behavior on this ESCItem: if hover is enabled, reverts back to normal the defined hover color modulate and/or hover texture and/or hover shader on the Sprite2D node, if any.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _apply_unhover_behavior() -> void: + if not hover_enabled: + return + if has_sprite(): + var sprite = get_sprite() + sprite.modulate = _previous_color_modulate + if sprite is Sprite2D: + if hover_texture != null: + sprite.texture = _previous_texture + if hover_shader != null: + sprite.material = null + + +## Whether the item is currently moving.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns true if the player is currently moving, false otherwise. (`bool`) +func is_moving() -> bool: + return _movable.task != ESCMovable.MovableTask.NONE if is_movable else false + +## The number of defined animation directions for this ESCItem.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the number of defined animation directions for this ESCItem. (`int`) +func get_directions_quantity() -> int: + return animations.dir_angles.size() + + +## A Dictionary containing custom data defined for this ESCItem.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a Dictionary containing custom data defined for this ESCItem. (`Dictionary`) +func get_custom_data() -> Dictionary: + return custom_data + +## Sets the `custom_data` Dictionary containing custom data defined for this ESCItem.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |data|`Dictionary`|Custom data dictionary to assign to this item (an empty dictionary is used when `null`).|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_custom_data(data: Dictionary) -> void: + custom_data = data if (data != null) else {} diff --git a/addons/escoria-core/game/core-scripts/esc_item.gd.uid b/addons/escoria-core/game/core-scripts/esc_item.gd.uid new file mode 100644 index 0000000..06b792d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_item.gd.uid @@ -0,0 +1 @@ +uid://dshelmw7y44qg diff --git a/addons/escoria-core/game/core-scripts/esc_location.gd b/addons/escoria-core/game/core-scripts/esc_location.gd new file mode 100644 index 0000000..47e9f09 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_location.gd @@ -0,0 +1,142 @@ +@tool +@icon("res://addons/escoria-core/design/esc_location.svg") +## A simple node extending Position2D with a global ID so that it can be +## referenced in ESC Scripts. Movement-based commands like `walk_to_pos` will +## automatically use an `ESCLocation` that is a child of the destination node. +## Commands like `turn_to`--which are not movement-based--will ignore child +## `ESCLocation`s and refer to the parent node. +extends Marker2D +class_name ESCLocation + + +## Escoria Plugin signal emitted to the `ESCRoom` when a start location is set in the `ESCLocation` node in order to check whether multiple start locations are set.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |node_to_ignore|`ESCLocation`| `ESCLocation` that should be ignored while validating start locations. Defaults to `null`.|no|[br] +## [br] +signal editor_is_start_location_set(node_to_ignore: ESCLocation) + +## Warning message: multiple start locations set in the room.. +const MULTIPLE_START_LOCATIONS_WARNING = \ + "Only 1 ESCLocation should have is_start_location set to true in an ESCRoom" + + +## The global ID of this ESCLocation +@export var global_id: String + +## If enabled, this `ESCLocation` is considered as a player start location +## for this room. +@export var is_start_location: bool = false: + set = set_is_start_location + + +@export_group("Player behavior on arrival") + +## Whether player character orients towards 'interaction_angle' as it arrives at +## the item's interaction position. +@export var player_orients_on_arrival: bool = true + +## If 'player_orients_on_arrival' is enabled, let the player character turn to +## this angle when it arrives at the item's interaction position. +@export var interaction_angle: int + +@export_group("","") + +## Escoria plugin variable to check the existence of multiple start locations. +var _multiple_start_locations_exist: bool = false: + set = set_multiple_start_locations_exist + + +## Used by "is" keyword to check whether a node's class_name is the same as p_classname. ## Parameters p_classname: String class to compare against[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_classname|`String`|Class name to compare against this location.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func is_class(p_classname: String) -> bool: + return p_classname == "ESCLocation" + + +## Ready function. Registers the ESCLocation to Object Manager.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + if not Engine.is_editor_hint(): + if not self.global_id.is_empty(): + escoria.object_manager.register_object( + ESCObject.new( + self.global_id, + self + ) + ) + +## Escoria editor plugin: on tree exit (ie. this node was removed), notify ESCRoom to update the list of start locations.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _exit_tree(): + if Engine.is_editor_hint() and is_start_location: + editor_is_start_location_set.emit(self) + +## Escoria editor plugin: overriden method that returns the list of warnings for these nodes.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `PackedStringArray` value. (`PackedStringArray`) +func _get_configuration_warnings() -> PackedStringArray: + return [MULTIPLE_START_LOCATIONS_WARNING] \ + if _multiple_start_locations_exist else [] + +## Escoria editor plugin: Setter for _multiple_start_locations_exist member. Updates the warnings displayed in the editor's scene tree.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`bool`|true whether multiple start locations exist in the room.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_multiple_start_locations_exist(value: bool) -> void: + _multiple_start_locations_exist = value + update_configuration_warnings() + +## Escoria editor plugin: Setter for is_start_location member. Notifies the ESCRoom of the change.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`bool`|true whether the ESCLocation node was set as start location.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_is_start_location(value: bool) -> void: + is_start_location = value + if Engine.is_editor_hint() and is_instance_valid(get_owner()): + editor_is_start_location_set.emit() diff --git a/addons/escoria-core/game/core-scripts/esc_location.gd.uid b/addons/escoria-core/game/core-scripts/esc_location.gd.uid new file mode 100644 index 0000000..0d8c5e0 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_location.gd.uid @@ -0,0 +1 @@ +uid://beh3ds4eapldu diff --git a/addons/escoria-core/game/core-scripts/esc_player.gd b/addons/escoria-core/game/core-scripts/esc_player.gd new file mode 100644 index 0000000..2ad2f15 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_player.gd @@ -0,0 +1,37 @@ +@tool +@icon("res://addons/escoria-core/design/esc_player.svg") +## An Escoria playable character. +extends ESCItem +class_name ESCPlayer + + +## Whether the player can be hovered over and actions performed on like an item. +## This allows the user to perform actions or use items on the player character. +@export var selectable: bool = false + +## Constructor method.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(): + # A player is always movable + is_movable = true + _force_registration = true + + +## Ready method.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + super() diff --git a/addons/escoria-core/game/core-scripts/esc_player.gd.uid b/addons/escoria-core/game/core-scripts/esc_player.gd.uid new file mode 100644 index 0000000..8d01418 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_player.gd.uid @@ -0,0 +1 @@ +uid://btrr8e33hu0i4 diff --git a/addons/escoria-core/game/core-scripts/esc_resource_cache.gd b/addons/escoria-core/game/core-scripts/esc_resource_cache.gd new file mode 100644 index 0000000..b096cca --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_resource_cache.gd @@ -0,0 +1,146 @@ +## A cache for resources +extends Node +class_name ESCResourceCache + + +signal resource_loading_progress(path, progress) +signal resource_loading_done(path) +signal resource_queue_progress(queue_size) + + +var queue: Array = [] +var pending: Dictionary = {} + + +func _ready(): + name = "resource_cacher" + resource_loading_progress.connect(print_progress) + resource_loading_done.connect(res_loaded) + resource_queue_progress.connect(print_queue_progress) + + +func queue_resource(path: String, p_in_front: bool = false, p_permanent: bool = false): + if path in pending: + return + + elif ResourceLoader.has_cached(path): + var res = ResourceLoader.load(path) + pending[path] = ESCResourceDescriptor.new(res, p_permanent) + else: + var res = ResourceLoader.load_threaded_request(path) + res.resource_path = path + if p_in_front: + queue.insert(0, res) + else: + queue.push_back(res) + pending[path] = ESCResourceDescriptor.new(res, p_permanent) + + +func cancel_resource(path): + if path in pending: + if pending[path].res is ResourceLoader: + queue.erase(pending[path].res) + pending.erase(path) + + +func clear(): + for p in pending.keys(): + if pending[p].permanent: + continue + cancel_resource(p) + #queue = [] + #pending = {} + + +func get_progress(path): + var ret = -1 + if path in pending: + if pending[path].res is ResourceLoader: + ret = float(pending[path].res.get_stage()) / float(pending[path].res.get_stage_count()) + else: + ret = 1.0 + resource_loading_done.emit(path) + resource_loading_progress.emit(path, ret) + + return ret + + +func is_ready(path): + var ret + + if path in pending: + ret = !(pending[path].res is ResourceLoader) + else: + ret = false + + return ret + + +func _wait_for_resource(res, path): + while true: + #VisualServer.call("sync") # workaround because sync is a keyword + RenderingServer.force_sync() + OS.delay_usec(16000) # wait 1 frame + + if queue.size() == 0 || queue[0] != res: + return pending[path].res + + +func get_resource(path): + if path in pending: + if pending[path].res is ResourceLoader: + var res = pending[path].res + if res != queue[0]: + var pos = queue.find(res) + queue.remove_at(pos) + queue.insert(0, res) + + res = _wait_for_resource(res, path) + + if !pending[path].permanent: + pending.erase(path) + + return res + + else: + var res = pending[path].res + if !pending[path].permanent: + pending.erase(path) + + return res + else: + # We can't use ESCProjectSettingsManager here since this method + # can be called from escoria._init() + if not ProjectSettings.get_setting("escoria/platform/skip_cache"): + var res = ResourceLoader.load(path) + pending[path] = ESCResourceDescriptor.new(res, true) + return res + return ResourceLoader.load(path) + + +func print_progress(p_path, p_progress): + escoria.logger.info(self, "{0} loading: {1}%".format([p_path, round(p_progress * 100)])) + + +func res_loaded(p_path): + escoria.logger.info(self, "loaded resource: {0}%".format([p_path])) + + +func print_queue_progress(p_queue_size): + escoria.logger.info(self, "queue size: {0}%".format([p_queue_size])) + + +func _process(_delta) -> void: + while queue.size() > 0: + var res = queue[0] + + var ret = res.poll() + + var path = res.get_meta("path") + if ret == ERR_FILE_EOF || ret != OK: + escoria.logger.info(self, "Finished loading %s" % path) + if path in pending: # else it was already retrieved + pending[res.get_meta("path")].res = res.get_resource() + + queue.erase(res) # something might have been put at the front of the queue while we polled, so use erase instead of remove + resource_queue_progress.emit(queue.size()) diff --git a/addons/escoria-core/game/core-scripts/esc_resource_cache.gd.uid b/addons/escoria-core/game/core-scripts/esc_resource_cache.gd.uid new file mode 100644 index 0000000..619ba3e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_resource_cache.gd.uid @@ -0,0 +1 @@ +uid://4vvytsg2pws3 diff --git a/addons/escoria-core/game/core-scripts/esc_room.gd b/addons/escoria-core/game/core-scripts/esc_room.gd new file mode 100644 index 0000000..d86780d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_room.gd @@ -0,0 +1,255 @@ +@tool +@icon("res://addons/escoria-core/design/esc_room.svg") +## A room in an Escoria based game. +extends Node2D +class_name ESCRoom + + +## Debugging displays for a room. +enum EditorRoomDebugDisplay { + NONE, # No debug display + CAMERA_LIMITS # Display the camera limits +} + +## The global id of this room +@export var global_id: String = "" + +## The ASHES script of this room +@export_file("*.esc", "*.ash") var esc_script: String = "" + +## The player scene to use inside this room +@export var player_scene: PackedScene + +## The camera limits available in this room +@export var camera_limits: Array = [Rect2()]: # (Array, Rect2) + set = set_camera_limits + +## The room's debug display mode.[br] +## Camera Limits: show a colored frame for each camera limit of the room.[br] +## None: no debug display +@export var editor_debug_mode: EditorRoomDebugDisplay = EditorRoomDebugDisplay.NONE: + set = set_editor_debug_mode + + +## Container of the player scene instance. +var player + +## Container of player camera +var player_camera: ESCCamera + +## Container of game scene instance +var game + +## Container of compiled ESCScript +var compiled_script: ESCScript + +## Whether automatic transition are enabled or not. This is modified by +## the Room Manager. +var enabled_automatic_transitions = true + +# Whether this room was run directly with Play Scene (F6) +var is_run_directly = false + +###### @Tool properties ###### +# Default font for tool display in the editor +var _tool_default_font: Font + +# Start the random number generator when the camera limits should be displayed +func _enter_tree(): + if editor_debug_mode == EditorRoomDebugDisplay.CAMERA_LIMITS: + randomize() + + +# Sanitize camera limits, add player node and set the global id to the +# name of this node if it's not set manually +func _ready(): + # Might as well just check here. + if get_parent() == get_tree().root \ + and ESCProjectSettingsManager.get_setting( + "application/run/main_scene" + ) != self.scene_file_path: + is_run_directly = true + + var temp_control: Control = Control.new() + _tool_default_font = temp_control.get_theme_default_font() + temp_control.queue_free() + + child_entered_tree.connect(_on_child_entered_tree) + + if Engine.is_editor_hint(): + _connect_location_nodes() + _validate_start_locations() + return + + # If room has no ESCBackground child, add one + var found_escbackground: bool = false + for child in get_children(): + if child is ESCBackground: + found_escbackground = true + move_child(child, 0) + if not found_escbackground: + var esc_bg = ESCBackground.new() + if not camera_limits.is_empty(): + esc_bg.set_size(camera_limits.front().size) + add_child(esc_bg) + move_child(esc_bg, 0) + + escoria.room_manager.init_room(self) + + +# Draw the camera limits visualization if enabled +func _draw(): + if not Engine.is_editor_hint(): + return + if editor_debug_mode == EditorRoomDebugDisplay.NONE: + return + + var camera_limits_colors: Array = [ + Color("red"), Color("blue"), Color("green") + ] + + # If there are more camera limits than colors defined for them, add more. + if camera_limits.size() > camera_limits_colors.size(): + for i in camera_limits.size() - camera_limits_colors.size(): + camera_limits_colors.push_back(Color(randf(), randf(), randf(), 1.0)) + + # Draw lines for camera limits + for i in camera_limits.size(): + draw_rect(camera_limits[i], camera_limits_colors[i], false, 10.0) + draw_string( + _tool_default_font, + Vector2(camera_limits[i].position.x + 30, camera_limits[i].position.y + 30), + str(i), + HORIZONTAL_ALIGNMENT_LEFT, + -1, + 16, + camera_limits_colors[i]) + +## Escoria editor plugin:[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |new_node|`Node`|Child node that entered the room's scene tree.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_child_entered_tree(new_node: Node): + if Engine.is_editor_hint() and new_node is ESCLocation: + _connect_location_nodes() + +## Escoria editor plugin: Listen for any signals from ESCLocation indicating that the is_start_location attribute has been set/unset in order to update start location validation.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _connect_location_nodes() -> void: + if not Engine.is_editor_hint(): + return + _connect_location_nodes_in_tree(self) + +## Escoria editor plugin:[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |node|`Node`|Node whose descendants should be scanned for `ESCLocation` children.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _connect_location_nodes_in_tree(node: Node) -> void: + for n in node.get_children(): + if n is ESCLocation: + if not n.is_connected("editor_is_start_location_set",_validate_start_locations): + n.connect("editor_is_start_location_set", _validate_start_locations) + + if n.get_child_count() > 0: + _connect_location_nodes_in_tree(n) + + +## Validate that we only have one start location for this scene. If we don't, call it out in the scene tree via configuration warnings. We may have to ignore a node if it's being removed/deleted from the scene tree.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |to_ignore|`ESCLocation`|Location node that should be excluded from validation (usually the one being removed).|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _validate_start_locations(to_ignore: ESCLocation = null): + var esc_locations: Array = _find_esc_locations(self) + var num_start_locations: int = 0 + + for n in esc_locations: + if n == to_ignore: + continue + + num_start_locations += 1 if n.is_start_location else 0 + + for n in esc_locations: + if n == to_ignore: + continue + + n.set_multiple_start_locations_exist(n.is_start_location and num_start_locations > 1) + + +## Escoria plugin for editor: get the list of ESCLocation nodes in the room. ####[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |node|`Node`|Root node to inspect when searching for `ESCLocation` instances.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Array` value. (`Array`) +func _find_esc_locations(node: Node) -> Array: + var esc_locations: Array = [] + + for n in node.get_children(): + if n is ESCLocation: + esc_locations.append(n) + + if n.get_child_count() > 0: + esc_locations.append_array(_find_esc_locations(n)) + + return esc_locations + + +# Set the camera limits +# +# #### Parameters +# +# - p_camera_limits: An array of Rect2Ds as camera limits +func set_camera_limits(p_camera_limits: Array) -> void: + var fixed_camera_limits: Array = [] + var viewport_height = ProjectSettings.get_setting("display/window/size/viewport_height") + for cam_limit in p_camera_limits: + var fixed_camera_limit = Rect2(cam_limit) + if fixed_camera_limit.size.y < viewport_height: + fixed_camera_limit.size.y = viewport_height + fixed_camera_limits.append(fixed_camera_limit) + camera_limits = fixed_camera_limits + + queue_redraw() + + +# Set the editor debug mode +# +# #### Parameters +# +# - p_editor_debug_mode: The debug mode to set for the room +func set_editor_debug_mode(p_editor_debug_mode: int) -> void: + editor_debug_mode = p_editor_debug_mode + queue_redraw() diff --git a/addons/escoria-core/game/core-scripts/esc_room.gd.uid b/addons/escoria-core/game/core-scripts/esc_room.gd.uid new file mode 100644 index 0000000..a1ba365 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_room.gd.uid @@ -0,0 +1 @@ +uid://c7fcpp72w3dnj diff --git a/addons/escoria-core/game/core-scripts/esc_terrain.gd b/addons/escoria-core/game/core-scripts/esc_terrain.gd new file mode 100644 index 0000000..a9279f8 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_terrain.gd @@ -0,0 +1,417 @@ +@tool +@icon("res://addons/escoria-core/design/esc_terrain.svg") +## A walkable Terrain for Escoria rooms. +extends Node2D +class_name ESCTerrain + + +## Logger class reference +const EscLogger = preload("res://addons/escoria-core/tools/logging/esc_logger.gd") + + +## Visualize scales or lightmap for debugging purposes if the editor +enum DebugMode { + NONE, + SCALES, + LIGHTMAP, +} + + + +@export_group("Scales") +## Scaling texture. If empty, items scale will always be 1.0. +## This is a greyscale image defining how close to the camera the character will +## appear at each pixel position. White is closer, black is further. +@export var scales: Texture2D: set = _set_scales + +## Minimum scaling. Corresponds to the scale used for characters when they are +## located at the blackest pixel of scaling texture, at which they will appear +## smallest. +## Unused if scaling texture is not set. +@export var scale_min: float = 0.3 + +## Maximum scaling. Corresponds the scale used for characters when they are +## located at the whitest pixel of scaling texture, at which they will appear +## biggest. +## Unused if scaling texture is not set. +@export var scale_max: float = 1.0 + +@export_group("","") +@export_group("Lightmap") + +## Lightmap texture. +@export var lightmap: Texture2D: set = _set_lightmap + +## Additional modulator to the lightmap texture. +@export var lightmap_modulate: Color = Color(1, 1, 1, 1) + +@export_group("","") +@export_group("Bitmaps scale") + + +## Scaling factor for the scale and light maps. +@export var bitmaps_scale: Vector2 = Vector2(1,1): set = _set_bm_scale + +@export_group("","") +@export_group("Movement speed") + +## Multiplier applied to the player speed on this terrain. +@export var player_speed_multiplier: float = 1.0 + +## Multiplier that determines how much faster the player will walk when fast +## mode is on (double clicked) +@export var player_doubleclick_speed_multiplier: float = 1.5 + +@export_group("","") +@export_group("Debug display") + + +## Debug display mode.[br] +## None: no debug display[br] +## Scales: displays the scales map[br] +## Lightmap: displays the lightmap +@export var editor_debug_mode = DebugMode.NONE: # (int, "None", "Scales", "Lightmap") + set = _set_editor_debug_mode + +@export_group("","") + +## The currently active navigation polygon +var current_active_navigation_instance: NavigationRegion2D = null + +## Currently visualized texture for debug mode +var _texture = null + +## The image from the lightmap texture +var _lightmap_data + +## Prohibits multiple calls to update_texture +var _texture_in_update = false + +## Logger instance +@onready var logger = EscLogger.ESCLoggerFile.new() + +## Ready method. Sets a reference to the active navigation polygon, register to Escoria and update the texture[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + connect("child_entered_tree", Callable(self, "_check_multiple_enabled_navpolys")) + connect("child_exiting_tree", Callable(self, "_check_multiple_enabled_navpolys").bind(true)) + + _check_multiple_enabled_navpolys() + if !Engine.is_editor_hint(): + escoria.room_terrain = self + _update_texture() + + +## All NavigationPolygonInstances defined as children of ESCTerrain in an Array.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns all NavigationPolygonInstances defined as children of ESCTerrain in an Array. (`Array`) +func get_children_navpolys() -> Array: + var navpolys: Array = [] + for n in get_children(): + if n is NavigationRegion2D: + navpolys.push_back(n) + return navpolys + + +## Checks whether multiple navigation polygons are enabled. +## Shows a warning in the terminal if this happens.[br] +# TODO: change this "simple" console log for an editor warning +# by overriding Node._get_configuration_warning() after we get rid of +# deprecated Navigation2D. +## Validates that only one navigation polygon is enabled at a time, logging a warning if multiple are active.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |node|`Node`|if this method is triggered by child_entered_tree or child_exited_tree signals, parameter is the added node.|no|[br] +## |is_exiting|`bool`|`true` when the node is leaving the scene tree and should be ignored.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _check_multiple_enabled_navpolys(node: Node = null, is_exiting: bool = false) -> void: + var navigation_enabled_found = false + if (node != null + and not is_exiting + and node is NavigationRegion2D + and node.enabled): + navigation_enabled_found = true + + for n in get_children(): + if is_exiting and n == node: + continue + if n is NavigationRegion2D and n.enabled: + if navigation_enabled_found: + if Engine.is_editor_hint(): + logger.warn( + self, + "Multiple NavigationPolygonInstances enabled " + \ + "at the same time." + ) + else: + logger.error( + self, + "Multiple NavigationPolygonInstances enabled " + \ + "at the same time." + ) + return + else: + navigation_enabled_found = true + current_active_navigation_instance = n + + +## Return the Color of the lightmap pixel for the specified position.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pos|`Vector2`|Position to calculate lightmap for.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Color` value. (`Color`) +func get_light(pos: Vector2) -> Color: + if lightmap == null or lightmap.get_image().is_empty(): + return Color(1, 1, 1, 1) + return _get_color(lightmap.get_image(), pos) * lightmap_modulate + + +## Calculate the scale inside the scale range for a given scale factor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |factor|`float`|The factor for the scaling according to the scale map|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Vector2` value. (`Vector2`) +func get_scale_range(factor: float) -> Vector2: + factor = scale_min + (scale_max - scale_min) * factor + return Vector2(factor, factor) + + +## Get the terrain scale factor for a given position[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pos|`Vector2`|The position to calculate for|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `float` value. (`float`) +func get_terrain(pos: Vector2) -> float: + if scales == null || scales.get_image().is_empty(): + return 1.0 + return _get_color(scales.get_image(), pos).v + + +## Small helper to get the color of an image at a position[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |image|`Image`|image to search the position in|yes|[br] +## |pos|`Vector2`|The position to calculate for|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Color` value. (`Color`) +func _get_color(image: Image, pos: Vector2) -> Color: + return image.get_pixel(pos.x, pos.y) + + +## Set the bitmap scaling[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_scale|`Vector2`|Scale to set|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_bm_scale(p_scale: Vector2): + bitmaps_scale = p_scale + _update_texture() + + +## Set the lightmap texture[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_lightmap|`Texture2D`|Lightmap texture to set|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_lightmap(p_lightmap: Texture2D): + var need_init = (lightmap != p_lightmap) or (lightmap and not _lightmap_data) + + lightmap = p_lightmap + + # It's bad enough a new copy is created when reading a pixel, we don't + # also need to get the data for every read to make yet another copy + if need_init: + if _lightmap_data == null: + _lightmap_data = lightmap.get_image().get_data() + + _update_texture() + + +## Set the scales texture[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_scales|`Texture2D`|Scale texture to set|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_scales(p_scales: Texture2D): + scales = p_scales + _update_texture() + + +## Set the debug mode[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_mode|`int`|Debug mode to set|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_editor_debug_mode(p_mode: int): + editor_debug_mode = p_mode + _update_texture() + + +## Update the debug texture, if it is dirty.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _update_texture(): + if _texture_in_update: + return + _texture_in_update = true + call_deferred("_do_update_texture") + + +## Update the texture and optionally set the debug texture[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _do_update_texture(): + _texture_in_update = false + if !is_inside_tree() or !Engine.is_editor_hint(): + return + + if editor_debug_mode == DebugMode.NONE: + queue_redraw() + return + + _texture = ImageTexture.new() + if editor_debug_mode == DebugMode.SCALES: + if scales != null: + _texture = scales + elif editor_debug_mode == DebugMode.LIGHTMAP: + if lightmap != null: + _texture = lightmap + + queue_redraw() + + +## Draw debugging visualizations[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _draw(): + if _texture == null or \ + not Engine.is_editor_hint() or \ + editor_debug_mode == DebugMode.NONE: + if current_active_navigation_instance: + current_active_navigation_instance.visible = true + return + + var scale_vect = bitmaps_scale + + if current_active_navigation_instance: + current_active_navigation_instance.visible = false + + var src = Rect2(0, 0, _texture.get_width(), _texture.get_height()) + var dst = Rect2( + 0, + 0, + _texture.get_width() * scale_vect.x, + _texture.get_height() * scale_vect.y + ) + + draw_texture_rect_region(_texture, dst, src) + +## Calculate a path from a given point to another. The path consists in a list of points.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |from|`Vector2`|Origin point|yes|[br] +## |to|`Vector2`|Destination point|yes|[br] +## |optimize|`bool`|(default is true) enabled optimisation|no|[br] +## |layers|`int`|bitmask of all region navigation layers that are allowed to be in the path.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `PackedVector2Array` value. (`PackedVector2Array`) +func get_simple_path( + from: Vector2, + to: Vector2, + optimize: bool = true, + layers: int = 1) -> PackedVector2Array: + return NavigationServer2D.map_get_path( + get_world_2d().navigation_map, + from, + to, + optimize, + layers) diff --git a/addons/escoria-core/game/core-scripts/esc_terrain.gd.uid b/addons/escoria-core/game/core-scripts/esc_terrain.gd.uid new file mode 100644 index 0000000..d0dd371 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_terrain.gd.uid @@ -0,0 +1 @@ +uid://5pbcieq7gpdo diff --git a/addons/escoria-core/game/core-scripts/esc_tooltip.gd b/addons/escoria-core/game/core-scripts/esc_tooltip.gd new file mode 100644 index 0000000..937856a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_tooltip.gd @@ -0,0 +1,311 @@ +@tool +## Dynamically controlled tooltip +extends RichTextLabel +class_name ESCTooltip + + +## Maximum width of the label +const MAX_WIDTH = 200 + +## Minimum height of the label +const MIN_HEIGHT = 30 + +## Maximum height of the label +const MAX_HEIGHT = 500 + +## Height of one line in the label +const ONE_LINE_HEIGHT = 16 + + +## Color of the label +@export var color: Color: set = set_color + +## Vector2 defining the offset from the cursor +@export var offset_from_cursor: Vector2 = Vector2(10,0) + +## Activates debug mode. If enabled, shows the label with a white background. +@export var debug_mode: bool = false: set = set_debug_mode + + +## Infinitive verb +var current_action: String + +## Target item/hotspot +var current_target: String + +## Preposition: on, with... +var current_prep: String = "with " + +## Target 2 item/hotspot +var current_target2: String + +## True if tooltip is waiting for a click on second target (use x with y) +var waiting_for_target2 = false + +## Node containing the debug white background +var debug_texturerect_node: TextureRect + +## Indicates whether the current room is loaded and ready +var _room_is_ready: bool = false + + +## Ready function.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + escoria.main.room_ready.connect(_on_room_ready) + escoria.action_manager.action_changed.connect(_on_action_selected) + + +## Set the color of the label[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_color|`Color`|the color to set the label|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_color(p_color: Color): + color = p_color + if _room_is_ready: + update_tooltip_text() + + +## Enable/disable debug mode of the label. If enabled, the label is displayed with a white background.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_debug_mode|`bool`|if true, enable debug mode. False to disable|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_debug_mode(p_debug_mode: bool): + debug_mode = p_debug_mode + if debug_mode: + # Add a white TextureRect behind the RTL to see its actual size + debug_texturerect_node = TextureRect.new() + add_child(debug_texturerect_node) + debug_texturerect_node.texture = load("res://addons/escoria-core/game/assets/images/white.png") + debug_texturerect_node.expand = true + debug_texturerect_node.stretch_mode = TextureRect.STRETCH_TILE + debug_texturerect_node.size_flags_horizontal = SIZE_EXPAND_FILL + debug_texturerect_node.size_flags_vertical = SIZE_EXPAND_FILL + debug_texturerect_node.show_behind_parent = true + debug_texturerect_node.anchor_right = 1.0 + debug_texturerect_node.anchor_bottom = 1.0 + debug_texturerect_node.mouse_filter = Control.MOUSE_FILTER_IGNORE + move_child(debug_texturerect_node, 2) + else: + if debug_texturerect_node: + remove_child(debug_texturerect_node) + debug_texturerect_node.queue_free() + + +## Set the first target of the label. #### Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target|`String`|String the target to add to the label|yes|[br] +## |needs_second_target|`bool`|if true, the label will prepare for a second target|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_target(target: String, needs_second_target: bool = false) -> void: + current_target = target + waiting_for_target2 = needs_second_target + if _room_is_ready: + update_tooltip_text() + + +## Set the second target of the label #### Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |target2|`String`|String the second target to add to the label|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_target2(target2: String) -> void: + current_target2 = target2 + if _room_is_ready: + update_tooltip_text() + + +## Update the tooltip text.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func update_tooltip_text(): + """ + Overriden method. Should not be called directly. + """ + pass + + +## Update the tooltip size according to the text.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func update_size(): + if not get_tree(): + # We're not in the tree anymore. Return + return + size = get_theme_font("normal_font").get_string_size( + current_target, + HORIZONTAL_ALIGNMENT_CENTER, + -1, + get("theme_override_font_sizes/normal_font_size") + ) + + +## Calculate the offset of the label depending on its position. #### Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|the position to test|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Vector2` value. (`Vector2`) +func _offset(position: Vector2) -> Vector2: + var center_offset_x = size.x / 2 + var offset_y = 5 + + position.x -= center_offset_x + position.y += offset_y + + return position + + +## Return the tooltip distance to top edge. #### Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|the position to test|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func tooltip_distance_to_edge_top(position: Vector2): + return position.y + + +## Return the tooltip distance to bottom edge. #### Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|the position to test|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func tooltip_distance_to_edge_bottom(position: Vector2): + return escoria.game_size.y - position.y + + +## Return the tooltip distance to left edge. #### Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|the position to test|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func tooltip_distance_to_edge_left(position: Vector2): + return position.x + + +## Return the tooltip distance to right edge. #### Parameters[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|the position to test Return* The distance to the edge.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func tooltip_distance_to_edge_right(position: Vector2): + return escoria.game_size.x - position.x + + +## Clear the tooltip targets texts[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func clear(): + waiting_for_target2 = false + set_target("") + set_target2("") + + +## Called when the room is loaded to setup the label.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_room_ready(): + escoria.main.current_scene.game.tooltip_node = self + _room_is_ready = true + + +## Called when an action is selected[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_action_selected() -> void: + current_action = escoria.action_manager.current_action + if _room_is_ready: + update_tooltip_text() diff --git a/addons/escoria-core/game/core-scripts/esc_tooltip.gd.uid b/addons/escoria-core/game/core-scripts/esc_tooltip.gd.uid new file mode 100644 index 0000000..c16a14d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_tooltip.gd.uid @@ -0,0 +1 @@ +uid://767x2bbqs8wr diff --git a/addons/escoria-core/game/core-scripts/esc_walk_context.gd b/addons/escoria-core/game/core-scripts/esc_walk_context.gd new file mode 100644 index 0000000..c0b7c86 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_walk_context.gd @@ -0,0 +1,42 @@ +## The walk context describes the target of a walk command and if that command +## should be executed fast. +extends RefCounted +class_name ESCWalkContext + + +## Target object that the walk command tries to reach. +var target_object: ESCObject = null + +## The target position +var target_position: Vector2 = Vector2() + +## Whether to move fast +var fast: bool + +## Whether an interaction should NOT happen after walk reaches destination +var dont_interact_on_arrival: bool + +## Constructor method.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_target_object|`ESCObject`|Escoria object the walker should approach (or `null` when walking to a position).|yes|[br] +## |p_target_position|`Vector2`|World position the walker should reach when no target object is provided.|yes|[br] +## |p_fast|`bool`|Whether the walk should use the actor's fast speed.|yes|[br] +## |p_dont_interact_on_arrival|`bool`|If `true`, skip the usual interaction when the destination is reached.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init( + p_target_object: ESCObject, + p_target_position: Vector2, + p_fast: bool, + p_dont_interact_on_arrival: bool +): + target_object = p_target_object + target_position = p_target_position + fast = p_fast + dont_interact_on_arrival = p_dont_interact_on_arrival diff --git a/addons/escoria-core/game/core-scripts/esc_walk_context.gd.uid b/addons/escoria-core/game/core-scripts/esc_walk_context.gd.uid new file mode 100644 index 0000000..08017f4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_walk_context.gd.uid @@ -0,0 +1 @@ +uid://ovss515ew3g1 diff --git a/addons/escoria-core/game/core-scripts/migrations/esc_migration.gd b/addons/escoria-core/game/core-scripts/migrations/esc_migration.gd new file mode 100644 index 0000000..1592f27 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/migrations/esc_migration.gd @@ -0,0 +1,49 @@ +extends RefCounted +class_name ESCMigration +## Base class for all migration version scripts. Extending scripts should be +## named like the version they migrate the savegame to. (e.g. 1.0.0.gd, 1.0.1.gd) + +## The savegame object to migrate +var _savegame: ESCSaveGame: + get = get_savegame + + +## Set the savegame[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |savegame|`ESCSaveGame`|Savegame to modify|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_savegame(savegame: ESCSaveGame): + _savegame = savegame + + +## Get the savegame.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCSaveGame` value. (`ESCSaveGame`) +func get_savegame() -> ESCSaveGame: + return _savegame + + +## Override this function in the version script with the things that need to be applied to the savegame[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func migrate(): + pass diff --git a/addons/escoria-core/game/core-scripts/migrations/esc_migration.gd.uid b/addons/escoria-core/game/core-scripts/migrations/esc_migration.gd.uid new file mode 100644 index 0000000..3e4edb0 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/migrations/esc_migration.gd.uid @@ -0,0 +1 @@ +uid://c6qkc3u4x23g4 diff --git a/addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd b/addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd new file mode 100644 index 0000000..8db1a0d --- /dev/null +++ b/addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd @@ -0,0 +1,208 @@ +## Class that handles migrations between different game or escoria versions +## @MANAGER +extends RefCounted +class_name ESCMigrationManager + + +## Regular expression that matches a simple semver version string +const VERSION_REGEX = "^(?\\d+)\\.(?\\d+)\\.(?\\d+)$" + + +## Compiled regex +var version_regex: RegEx + +## Constructor of the migration manager.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init() -> void: + version_regex = RegEx.new() + version_regex.compile(VERSION_REGEX) + + +## Migrates the specified savegame from a specified version to another version based on a directory of migration scripts. The migration manager searches for scripts from after the given version up to the target version in this directory, loads them and applies the version. Each migration will return a modified version of the given savegame. The migrated savegame.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |savegame|`ESCSaveGame`|The `ESCSaveGame` object to migrate.|yes|[br] +## |from|`String`|The source version string (e.g., "1.0.0").|yes|[br] +## |to|`String`|The target version string (e.g., "1.1.0").|yes|[br] +## |versions_directory|`String`|Directory path containing version migration scripts.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCSaveGame` value. (`ESCSaveGame`) +func migrate( + savegame: ESCSaveGame, + from: String, + to: String, + versions_directory: String +) -> ESCSaveGame: + escoria.logger.info( + self, + "Migrating savegame from version %s to version %s." + % [from, to] + ) + + var from_info = version_regex.search(from) + var to_info = version_regex.search(to) + + var wrong_version: bool = false + if from_info.get_string("major") > to_info.get_string("major"): + wrong_version = true + elif from_info.get_string("major") == to_info.get_string("major") and\ + from_info.get_string("minor") > to_info.get_string("minor"): + wrong_version = true + elif from_info.get_string("major") == to_info.get_string("major") and\ + from_info.get_string("minor") == to_info.get_string("minor") and\ + from_info.get_string("patch") > to_info.get_string("patch"): + wrong_version = true + + if wrong_version: + escoria.logger.error( + self, + "Can not migrate savegame from version %s to version %s." + + " \"To\" version must be later than \"from\" version." + % [from, to] + ) + + var versions = _find_versions(versions_directory, from, to) + versions.sort_custom(Callable(self, "_compare_version")) + if versions[0].get_file().get_basename() == from: + versions.pop_front() + + for version in versions: + var migration_script = load(version).new() + if not migration_script is ESCMigration: + escoria.logger.error( + self, + "File %s is not a valid migration script." % version + ) + escoria.logger.debug( + self, + "Migrating savegame to version %s." % version + ) + (migration_script as ESCMigration).set_savegame(savegame) + (migration_script as ESCMigration).migrate() + savegame = (migration_script as ESCMigration).get_savegame() + + return savegame + + +## Find all fitting version scripts between the given versions in a directory and all its subdirectories.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |directory|`String`|Directory to search in|yes|[br] +## |from|`String`|Start version to check|yes|[br] +## |to|`String`|End version to check|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Array` value. (`Array`) +func _find_versions(directory: String, from: String, to: String) -> Array: + escoria.logger.trace( + self, + "Searching directory %s." % directory + ) + var versions = [] + var dir = DirAccess.open(directory) + dir.list_dir_begin() + var file_name = dir.get_next() + while file_name != "": + var version = file_name.get_basename() + var regex_result = version_regex.search(version) + if dir.current_is_dir(): + versions += _find_versions( + directory.path_join(file_name), + from, + to + ) + elif regex_result and _version_between(version, from, to): + escoria.logger.trace( + self, + "Found compatible savegame migration script for version %s." % version + ) + versions.append( + directory.path_join(file_name) + ) + file_name = dir.get_next() + return versions + + +## Check, whether the given version is >= from and <= to[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |version|`String`|Version to check|yes|[br] +## |from|`String`|Start version|yes|[br] +## |to|`String`|End version|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _version_between(version: String, from: String, to: String) -> bool: + var version_info = version_regex.search(version) + var from_info = version_regex.search(from) + var to_info = version_regex.search(to) + + if from_info.get_string("major") < version_info.get_string("major") and \ + version_info.get_string("major") < to_info.get_string("major"): + return true + elif from_info.get_string("major") == version_info.get_string("major") and \ + from_info.get_string("minor") < version_info.get_string("minor"): + return true + elif from_info.get_string("major") == version_info.get_string("major") and \ + from_info.get_string("minor") == \ + version_info.get_string("minor") and \ + from_info.get_string("patch") < version_info.get_string("patch"): + return true + elif to_info.get_string("major") == version_info.get_string("major") and \ + to_info.get_string("minor") > version_info.get_string("minor"): + return true + elif to_info.get_string("major") == version_info.get_string("major") and \ + to_info.get_string("minor") == version_info.get_string("minor") and\ + to_info.get_string("patch") > version_info.get_string("patch"): + return true + + return false + + +## Comparator function for version strings.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |version_a|`String`|First version to compare|yes|[br] +## |version_b|`String`|Second version to compare|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func _compare_version(version_a: String, version_b: String) -> bool: + var a_info = version_regex.search(version_a.get_file().get_basename()) + var b_info = version_regex.search(version_b.get_file().get_basename()) + + if a_info.get_string("major") < b_info.get_string("major"): + return true + elif a_info.get_string("major") == b_info.get_string("major") and \ + a_info.get_string("minor") < b_info.get_string("minor"): + return true + elif a_info.get_string("major") == b_info.get_string("major") and \ + a_info.get_string("minor") == b_info.get_string("minor") and \ + a_info.get_string("patch") < b_info.get_string("patch"): + return true + + return false diff --git a/addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd.uid b/addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd.uid new file mode 100644 index 0000000..714e2fc --- /dev/null +++ b/addons/escoria-core/game/core-scripts/migrations/esc_migration_manager.gd.uid @@ -0,0 +1 @@ +uid://bkcimcp3qsfvl diff --git a/addons/escoria-core/game/core-scripts/plugins/escoria_plugin.gd b/addons/escoria-core/game/core-scripts/plugins/escoria_plugin.gd new file mode 100644 index 0000000..ec02f03 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/plugins/escoria_plugin.gd @@ -0,0 +1,126 @@ +## Base class for Escoria plugins. +class_name EscoriaPlugin + +## Register a user interface. This should be called in a deferred way from the addon's _enter_tree.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |plugin|`EditorPlugin`|the plugin that registers|yes|[br] +## |game_scene|`String`|Path to the game scene extending ESCGame|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +static func register_ui(plugin: EditorPlugin, game_scene: String) -> bool: + if not plugin.get_editor_interface().is_plugin_enabled( + Escoria.ESCORIA_CORE_PLUGIN_NAME + ): + push_error("Escoria Core must be enabled.") + return false + + var game_scene_setting_value = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_SCENE + ) + + if not game_scene_setting_value in [ + "", + game_scene + ]: + push_error("Can't register user interface because user interface %s is registered." + % game_scene_setting_value + ) + return false + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.GAME_SCENE, + game_scene + ) + return true + +## Deregister a user interface.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |game_scene|`String`|Path to the game scene extending ESCGame|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +static func deregister_ui(game_scene: String): + # If the currently configured game scene is not the one we're disabling, exit now. + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_SCENE + ) != game_scene: + return + + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.GAME_SCENE, + "" + ) + + +## Register a dialog manager addon. This should be called in a deferred way from the addon's _enter_tree.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |plugin|`EditorPlugin`|the plugin that registers|yes|[br] +## |manager_class|`String`|Path to the manager class script|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +static func register_dialog_manager(plugin: EditorPlugin, manager_class: String) -> bool: + if not plugin.get_editor_interface().is_plugin_enabled( + Escoria.ESCORIA_CORE_PLUGIN_NAME + ): + push_error("Escoria Core must be enabled.") + return false + + var dialog_managers: Array = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DIALOG_MANAGERS + ) + + if manager_class in dialog_managers: + return true + + dialog_managers.push_back(manager_class) + + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.DIALOG_MANAGERS, + dialog_managers + ) + + return true + + +## Deregister a dialog manager addon.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |manager_class|`String`|Path to the manager class script|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +static func deregister_dialog_manager(manager_class: String): + var dialog_managers: Array = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DIALOG_MANAGERS + ) + + # If the dialog manager we're removing in not in the registered list, return quietly. + if not manager_class in dialog_managers: + return + + dialog_managers.erase(manager_class) + + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.DIALOG_MANAGERS, + dialog_managers + ) diff --git a/addons/escoria-core/game/core-scripts/plugins/escoria_plugin.gd.uid b/addons/escoria-core/game/core-scripts/plugins/escoria_plugin.gd.uid new file mode 100644 index 0000000..c31ee66 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/plugins/escoria_plugin.gd.uid @@ -0,0 +1 @@ +uid://byo0q0nmvlq0g diff --git a/addons/escoria-core/game/core-scripts/resources/esc_animationname.gd b/addons/escoria-core/game/core-scripts/resources/esc_animationname.gd new file mode 100644 index 0000000..482695f --- /dev/null +++ b/addons/escoria-core/game/core-scripts/resources/esc_animationname.gd @@ -0,0 +1,10 @@ +@tool +## Class defining an animation to use for an angle. +extends Resource +class_name ESCAnimationName + +## Name of the animation +@export var animation: String + +## Animation mirror (false is no mirror, true is mirrored) +@export var mirrored: bool diff --git a/addons/escoria-core/game/core-scripts/resources/esc_animationname.gd.uid b/addons/escoria-core/game/core-scripts/resources/esc_animationname.gd.uid new file mode 100644 index 0000000..96467ed --- /dev/null +++ b/addons/escoria-core/game/core-scripts/resources/esc_animationname.gd.uid @@ -0,0 +1 @@ +uid://clixil1u5exq7 diff --git a/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd b/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd new file mode 100644 index 0000000..337013e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd @@ -0,0 +1,120 @@ +@tool +## Resource containing all defined animations and angles for character +## movement.[br] +extends Resource +class_name ESCAnimationResource + +## Array containing the different angles available for animations.[br] +## Each angle is defined by an array [start_angle, angle_size]. +## start_angle must be between 0 and 360.[br] +## Angles 0 and 360 are the same and correspond to UP/NORTH, 90 is RIGHT/EAST, +## 180 is DOWN/SOUTH, 270 is LEFT/WEST etc. +@export var dir_angles: Array[ESCDirectionAngle] = []: set = set_dir_angles + +## Array of animations for each direction, from UP to RIGHT_UP clockwise.[br] +## Each entry is [animation_name, scale]. The scale parameter can be set to -1 to +## mirror the animation. +@export var directions: Array[ESCAnimationName] = []: set = set_directions + +## Array containing the idle animations for each direction (in the order defined +## by dir_angles).[br] +## The scale parameter can be set to -1 to mirror the animation. +@export var idles: Array[ESCAnimationName] = []: set = set_idles + +## Array containing the speak animations for each direction (in the order defined +## by dir_angles).[br] +## The scale parameter can be set to -1 to mirror the animation. +@export var speaks: Array[ESCAnimationName] = []: set = set_speaks + +## Sets the dir_angles property.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_dir_angles|`Array`|Array of direction angle resources to set.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_dir_angles(p_dir_angles: Array[ESCDirectionAngle]) -> void: + dir_angles = p_dir_angles + emit_changed() + +## Sets the directions property.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_set_directions|`Array`|Array of direction resources to set.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_directions(p_set_directions: Array[ESCAnimationName]) -> void: + directions = p_set_directions + emit_changed() + +## Sets the idles property.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_set_idles|`Array`|Array of idle resources to set.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_idles(p_set_idles: Array[ESCAnimationName]) -> void: + idles = p_set_idles + emit_changed() + +## Sets the speaks property.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_set_speaks|`Array`|Array of speak resources to set.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_speaks(p_set_speaks: Array[ESCAnimationName]) -> void: + speaks = p_set_speaks + emit_changed() + +## The direction id from an animation name. 1 if not found.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_animation_name|`String`|The animation name.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the direction id from an animation name. 1 if not found. (`int`) +func get_direction_id_from_animation_name(p_animation_name: String) -> int: + var founds_array = [] + + for directions_anim_resource in directions: + if (directions_anim_resource as ESCAnimationName).animation == p_animation_name: + founds_array.push_back(directions.find(directions_anim_resource)) + for idles_anim_resource in idles: + if (idles_anim_resource as ESCAnimationName).animation == p_animation_name: + founds_array.push_back(idles.find(idles_anim_resource)) + for speaks_anim_resource in speaks: + if (speaks_anim_resource as ESCAnimationName).animation == p_animation_name: + founds_array.push_back(speaks.find(speaks_anim_resource)) + + if founds_array.size() > 1: + escoria.logger.warn( + self, + "Multiple animations found for name %s. Returning first direction found." % p_animation_name + ) + elif founds_array.size() == 0: + return -1 + return founds_array[0] diff --git a/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd.uid b/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd.uid new file mode 100644 index 0000000..cfa2154 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd.uid @@ -0,0 +1 @@ +uid://sksvt0n8gle2 diff --git a/addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd b/addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd new file mode 100644 index 0000000..0050dc4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd @@ -0,0 +1,10 @@ +@tool +## Class defining an angle, with a start angle (between 0 and 360) and a size. +extends Resource +class_name ESCDirectionAngle + +## Start angle of the directional angle. +@export var angle_start: int + +## Size of the angle. +@export var angle_size: int diff --git a/addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd.uid b/addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd.uid new file mode 100644 index 0000000..c164811 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd.uid @@ -0,0 +1 @@ +uid://bxc3dll30x8tl diff --git a/addons/escoria-core/game/core-scripts/resources/esc_resource_descriptor.gd b/addons/escoria-core/game/core-scripts/resources/esc_resource_descriptor.gd new file mode 100644 index 0000000..72e2cce --- /dev/null +++ b/addons/escoria-core/game/core-scripts/resources/esc_resource_descriptor.gd @@ -0,0 +1,25 @@ +## Describes a resource for use in the resource cache. +extends Resource +class_name ESCResourceDescriptor + +## The resource being described. +var res + +## Whether the resource is permanent. +var permanent: bool + +## Constructor for ESCResourceDescriptor. Sets the resource and permanence flag.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |res_in|`Variant`|The resource to describe.|yes|[br] +## |permanent_in|`bool`|Whether the resource is permanent.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(res_in, permanent_in: bool) -> void: + res = res_in + permanent = permanent_in diff --git a/addons/escoria-core/game/core-scripts/resources/esc_resource_descriptor.gd.uid b/addons/escoria-core/game/core-scripts/resources/esc_resource_descriptor.gd.uid new file mode 100644 index 0000000..770f7e0 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/resources/esc_resource_descriptor.gd.uid @@ -0,0 +1 @@ +uid://20ixn175cd6c diff --git a/addons/escoria-core/game/core-scripts/save_data/esc_save_manager.gd b/addons/escoria-core/game/core-scripts/save_data/esc_save_manager.gd new file mode 100644 index 0000000..ec78fe4 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/save_data/esc_save_manager.gd @@ -0,0 +1,620 @@ +## Saves and loads savegame and settings files. +## @MANAGER +class_name ESCSaveManager + +## Emitted when the game is starting to load a savegame.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal game_is_loading + +## Emitted when the game has finished loading a savegame.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal game_finished_loading + +## Template for settings filename. +const SETTINGS_TEMPLATE: String = "settings.tres" + +## Template for savegames filenames. +const SAVE_NAME_TEMPLATE: String = "save_%03d.tres" + +## Template for crash savegames filenames. +const CRASH_SAVE_NAME_TEMPLATE: String = "crash_autosave_%s_%s.tres" + +## If true, saving a game is enabled. Else, saving is disabled. +var save_enabled: bool = true + +## Variable containing the saves folder obtained from Project Settings. +var save_folder: String + +## Filename of the latest crash savegame file. +var crash_savegame_filename: String + +## Variable containing the settings folder obtained from Project Settings. +var settings_folder: String + +## True if escoria is currently loading a savegame. This is used to avoid +## RoomManager to execute room's :setup and :ready events when loading a savegame. +var is_loading_game: bool + +## ESC commands kept around for references to their command names. +var _add_inventory: InventoryAddCommand +var _transition: TransitionCommand +var _hide_menu: HideMenuCommand +var _camera_set_target: CameraSetTargetCommand +var _change_scene: ChangeSceneCommand +var _enable_terrain: EnableTerrainCommand +var _set_active: SetActiveCommand +var _set_active_if_exists: SetActiveIfExistsCommand +var _set_interactive: SetInteractiveCommand +var _set_item_custom_data: SetItemCustomDataCommand +var _teleport_pos: TeleportPosCommand +var _set_direction: SetDirectionCommand +var _set_global: SetGlobalCommand +var _set_state: SetStateCommand +var _stop_snd: StopSndCommand +var _play_snd: PlaySndCommand +var _sched_event: SchedEventCommand + +## Constructor of ESCSaveManager object.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(): + # We leave the calls to ProjectSettings as-is since this constructor can be + # called from escoria.gd's own. + save_folder = ProjectSettings.get_setting("escoria/main/savegames_path") + settings_folder = ProjectSettings.get_setting("escoria/main/settings_path") + + _add_inventory = InventoryAddCommand.new() + _transition = TransitionCommand.new() + _hide_menu = HideMenuCommand.new() + _camera_set_target = CameraSetTargetCommand.new() + _change_scene = ChangeSceneCommand.new() + _enable_terrain = EnableTerrainCommand.new() + _set_active = SetActiveCommand.new() + _set_active_if_exists = SetActiveIfExistsCommand.new() + _set_interactive = SetInteractiveCommand.new() + _set_item_custom_data = SetItemCustomDataCommand.new() + _teleport_pos = TeleportPosCommand.new() + _set_direction = SetDirectionCommand.new() + _set_global = SetGlobalCommand.new() + _set_state = SetStateCommand.new() + _stop_snd = StopSndCommand.new() + _play_snd = PlaySndCommand.new() + _sched_event = SchedEventCommand.new() + is_loading_game = false + +## Return a list of savegames metadata (id, date, name and game version).[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` value. (`Dictionary`) +func get_saves_list() -> Dictionary: + var regex = RegEx.new() + regex.compile("save_(?[0-9]{3})\\.tres") + + var saves = {} + + _ensure_directory_exists(save_folder) + + var dirsave = DirAccess.open(save_folder) + + if dirsave != null: + dirsave.list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547 + var nextfile = dirsave.get_next() + while nextfile != "": + var save_path = save_folder.path_join(nextfile) + var save_game_res: Resource = load(save_path) + + if save_game_res == null: + escoria.logger.warn( + self, + "Savegame file %s is corrupted. Skipping." % save_path + ) + else: + var matches = regex.search(nextfile) + if matches != null and matches.get_string("slotnumber") != null: + var save_game_data = { + "date": save_game_res["date"], + "name": save_game_res["name"], + "game_version": save_game_res["game_version"], + "slotnumber": matches.get_string("slotnumber") + } + saves[int(matches.get_string("slotnumber"))] = save_game_data + else: + escoria.logger.warn( + self, + "Savegame file %s contains valid data but doesn't match filename format %s. Skipping." + % [save_path, regex.get_pattern()] + ) + nextfile = dirsave.get_next() + else: + escoria.logger.error( + self, + "Could not open savegame folder %s" % save_folder + ) + + return saves + +## True whether the savegame identified by id does exist.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |id|`int`|Integer suffix of the savegame file.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns true whether the savegame identified by id does exist. (`bool`) +func save_game_exists(id: int) -> bool: + var save_file_path: String = save_folder.path_join(SAVE_NAME_TEMPLATE % id) + return FileAccess.file_exists(save_file_path) + +## Save the current state of the game in a file suffixed with the id value. This id can help with slots development for the game developer.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |id|`int`|Integer suffix of the savegame file.|yes|[br] +## |p_savename|`String`|Name of the savegame.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func save_game(id: int, p_savename: String): + if not save_enabled: + escoria.logger.debug( + self, + "Saving is currently disabled. Save cancelled." + ) + return + + var save_game := _do_save_game(p_savename) + + if not DirAccess.dir_exists_absolute(save_folder): + DirAccess.make_dir_recursive_absolute(save_folder) + + var save_path = save_folder.path_join(SAVE_NAME_TEMPLATE % id) + var error: int = ResourceSaver.save(save_game, save_path) + if error != OK: + escoria.logger.error( + self, + "There was an issue writing savegame number %s to %s." % [id, save_path] + ) + +## Performs an emergency savegame in case of crash.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +func save_game_crash() -> int: + var datetime = Time.get_datetime_dict_from_system() + var datetime_string = "%02d/%02d/%02d %02d:%02d" % [ + datetime["day"], + datetime["month"], + datetime["year"], + datetime["hour"], + datetime["minute"], + ] + + var save_game := _do_save_game("Crash %s" % datetime_string) + + var save_file_path: String = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.LOG_FILE_PATH + ) + crash_savegame_filename = save_file_path.path_join( + CRASH_SAVE_NAME_TEMPLATE % [ + str(datetime["year"]) + str(datetime["month"]) + + str(datetime["day"]), + str(datetime["hour"]) + str(datetime["minute"]) + + str(datetime["second"]) + ] + ) + + var error: int = ResourceSaver.save(save_game, crash_savegame_filename) + if error != OK: + escoria.logger.error( + self, + "There was an issue writing the crash save to %s." + % crash_savegame_filename + ) + return error + +## Actual savegame function.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_savename|`String`|Name of the savegame.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCSaveGame` value. (`ESCSaveGame`) +func _do_save_game(p_savename: String) -> ESCSaveGame: + var save_game = ESCSaveGame.new() + + var plugin_config = ConfigFile.new() + plugin_config.load("res://addons/escoria-core/plugin.cfg") + save_game.escoria_version = plugin_config.get_value("plugin", "version") + + save_game.game_version = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_VERSION + ) + save_game.name = p_savename + + save_game.date = Time.get_datetime_dict_from_system() + + escoria.globals_manager.save_game(save_game) + escoria.inventory_manager.save_game(save_game) + escoria.object_manager.save_game(save_game) + escoria.main.save_game(save_game) + escoria.event_manager.save_game(save_game) + save_game.settings = escoria.settings_manager.get_settings_dict() + save_game.custom_data = escoria.game_scene.get_custom_data() + + return save_game + +## Load a savegame file from its id.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |id|`int`|Integer suffix of the savegame file.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func load_game(id: int): + var save_file_path: String = save_folder.path_join(SAVE_NAME_TEMPLATE % id) + if not FileAccess.file_exists(save_file_path): + escoria.logger.error( + self, + "Save file %s doesn't exist." % save_file_path + ) + return + + # Disconnect all trigger areas in the current room so that they don't + # trigger after room is loaded (eg: when player was in a trigger area, + # trigger_out won't fire after loading the game) + if (escoria.main.current_scene != null): + escoria.main.current_scene.get_tree().call_group(escoria.GROUP_ITEM_TRIGGERS, "disconnect_trigger_events") + + emit_signal("game_is_loading") + + # Disconnect all trigger areas in the current room so that they don't + # trigger after room is loaded (eg: when player was in a trigger area, + # trigger_out won't fire after loading the game) + if (escoria.main.current_scene != null): + escoria.main.current_scene.get_tree().call_group(escoria.GROUP_ITEM_TRIGGERS, "disconnect_trigger_events") + + game_is_loading.emit() + + escoria.logger.info( + self, + "Loading savegame %s" % str(id) + ) + is_loading_game = true + escoria.current_state = escoria.GAME_STATE.LOADING + + var save_game: ESCSaveGame = ResourceLoader.load(save_file_path) + + escoria.settings_manager.load_settings_from_dict(save_game.settings) + + escoria.settings_manager.load_settings_from_dict(save_game.settings) + + var plugin_config = ConfigFile.new() + plugin_config.load("res://addons/escoria-core/plugin.cfg") + var escoria_version = plugin_config.get_value("plugin", "version") + + # Migrate savegame through escoria versions + if escoria_version != save_game.escoria_version: + var migration_manager: ESCMigrationManager = ESCMigrationManager.new() + save_game = migration_manager.migrate( + save_game, + save_game.escoria_version, + escoria_version, + "res://addons/escoria-core/game/core-scripts/migrations/versions" + ) + + # Migrate savegame through game versions + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_VERSION + ) != save_game.game_version \ + and ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_MIGRATION_PATH + ) != "": + var migration_manager: ESCMigrationManager = ESCMigrationManager.new() + save_game = migration_manager.migrate( + save_game, + save_game.game_version, + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_VERSION + ), + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.GAME_MIGRATION_PATH + ) + ) + + escoria.event_manager.interrupt() + + # Now the actual savegame loading happens. + # Steps: + # 1. Hide main and pause menus + # 2. Change the scene to the next room. + # This does the out transition from the current scene. + # And resets objects lists in various managers so we don't have to reset + # them all manually. + # 3. Load objects for all rooms + # 4. Clear current globals (in case the loaded room automatically + # initializes some - they are loaded anyway) + # Load globals + # 5. Load inventory items + # 6. Load room's terrain navigation polygons + # 7. Load events (currently only scheduled events) + # === At this point we're done loading the saved game and ready to give + # back control to the player + # 8. Transition in + # 9. Unpause the game + # 10. Clear current action, tool + # Reset input mode to accept input + # Reset escoria state + # Set save manager "is_loading_game" var to false since we're now done + + # Hide main and pause menus + escoria.game_scene.hide_main_menu() + escoria.game_scene.unpause_game() + + escoria.room_manager.change_scene_to_file(save_game.main.current_scene_filename, false) + + _load_savegame_objects(save_game.objects) + + escoria.globals_manager.clear() + _load_savegame_globals(save_game.globals) + _load_savegame_inventory(save_game.inventory) + _load_savegame_terrain_navpolys(save_game.terrain_navpolys) + _load_savegame_events(save_game.events) + _transition.run(["", "in", 1.0]) + + escoria.set_game_paused(false) + + escoria.action_manager.clear_current_action() + escoria.action_manager.clear_current_tool() + escoria.inputs_manager.input_mode = escoria.inputs_manager.INPUT_ALL + is_loading_game = false + escoria.current_state = escoria.GAME_STATE.DEFAULT + + emit_signal("game_finished_loading") + + escoria.logger.info(self, "Finished loading savegame %s" % str(id)) + +## Load all objects saved in a savegame data.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |savegame_objects|`Dictionary`|Dictionary containing saved objects.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _load_savegame_objects(savegame_objects: Dictionary): + for object_id in savegame_objects: + var saved_object_data = savegame_objects[object_id] + if object_id in ESCObjectManager.RESERVED_OBJECTS: # Sound players only atm + if saved_object_data.has("state") \ + and saved_object_data["state"] in ["off", "default"]: + _stop_snd.run([object_id]) + else: + _play_snd.run([saved_object_data["state"], object_id, saved_object_data["playback_position"]]) + else: + if object_id == escoria.main.current_scene.global_id: + _load_room_objects(object_id, saved_object_data) + +## Load objects saved in a savegame data for a given room.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room_id|`String`|Id of the room.|yes|[br] +## |objects_dictionary|`Dictionary`|Dictionary containing the objects data.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _load_room_objects(room_id: String, objects_dictionary: Dictionary): + escoria.logger.info(self, "Loading room '%s'" % room_id) + + for object_id in objects_dictionary: + _load_object(object_id, objects_dictionary[object_id], room_id) + + escoria.logger.info(self, "Finished loading room '%s'" % room_id) + +## Load one object saved in a savegame data.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object_id|`String`|Id of the object.|yes|[br] +## |object_dictionary|`Dictionary`|Dictionary containing the object's data.|yes|[br] +## |room_id|`String`|Id of the room.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _load_object(object_id: String, object_dictionary: Dictionary, room_id: String): + escoria.logger.info(self, "Loading object '%s'" % object_id) + + if object_id == ESCObjectManager.CAMERA: + _camera_set_target.run([0, object_dictionary["target"]]) + else: + # Active + if object_dictionary.has("active"): + _set_active_if_exists.run([object_id, object_dictionary["active"]]) + + # Interactive + if object_dictionary.has("interactive") and _set_interactive.validate([object_id, object_dictionary["interactive"]]): + _set_interactive.run([object_id, object_dictionary["interactive"]]) + + # State + if object_dictionary.has("state") and _set_state.validate([object_id, object_dictionary["state"], true]): + _set_state.run([object_id, object_dictionary["state"], true]) + + # Position + if object_dictionary.has("global_transform"): + _teleport_pos.run([ + object_id, + object_dictionary["global_transform"].origin.x, + object_dictionary["global_transform"].origin.y + ]) + + # Orientation + if object_dictionary.has("last_dir"): + _set_direction.run([object_id, int(object_dictionary["last_dir"]), 0.0]) + + # Custom data + if object_dictionary.has("custom"): + var custom_data: Dictionary = object_dictionary["custom_data"] + if not custom_data.is_empty(): + _set_item_custom_data.run([object_id, custom_data]) + + escoria.logger.info(self, "Finished loading object '%s'" % object_id) + +## Load globals from a savegame data.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |savegame_globals|`Dictionary`|Dictionary containing saved globals.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _load_savegame_globals(savegame_globals: Dictionary): + escoria.logger.info(self, "Loading globals") + + for g in savegame_globals: + _set_global.run([g, savegame_globals[g], true]) + + escoria.logger.info(self, "Finished loading globals") + +## Load inventory from a savegame data.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |savegame_inventory|`Array`|Array containing saved inventory items.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _load_savegame_inventory(savegame_inventory: Array): + escoria.logger.info(self, "Loading inventory") + + for g in savegame_inventory: + _add_inventory.run([g]) + + escoria.logger.info(self, "Finished loading inventory") + +## Load terrain navpolys from a savegame data.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |savegame_terrain_navpolys|`Dictionary`|Dictionary containing saved terrain navpolys.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _load_savegame_terrain_navpolys(savegame_terrain_navpolys: Dictionary): + escoria.logger.info(self, "Loading terrain") + + for room_id in savegame_terrain_navpolys: + for terrain_id in savegame_terrain_navpolys[room_id]: + if savegame_terrain_navpolys[room_id][terrain_id]: + _enable_terrain.run([terrain_id]) + break + + escoria.logger.info(self, "Finished loading terrain") + +## Load events from a savegame data.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |savegame_events|`Dictionary`|Dictionary containing saved events.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _load_savegame_events(savegame_events: Dictionary): + escoria.logger.info(self, "Loading events") + + if savegame_events.has("sched_events") \ + and not savegame_events.sched_events.is_empty(): + escoria.logger.info(self, "Loading scheduled events") + for sched_event in savegame_events.sched_events: + var script: ESCScript = \ + escoria.esc_compiler.load_esc_file( + sched_event.event_filename, + sched_event.object + ) + escoria.event_manager.schedule_event( + script.events[sched_event.event_name], + sched_event.timeout, + sched_event.object + ) + escoria.logger.info(self, "Finished loading scheduled events") + + escoria.logger.info(self, "Finished loading events") + +## Ensures the given directory exists, creating it if necessary.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |dir|`String`|Path to the directory to check or create.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ensure_directory_exists(dir: String) -> void: + if not DirAccess.dir_exists_absolute(save_folder): + var return_code = DirAccess.make_dir_absolute(save_folder) + + if return_code != OK: + escoria.logger.error( + self, + "Could not create savegame folder %s" % save_folder + ) diff --git a/addons/escoria-core/game/core-scripts/save_data/esc_save_manager.gd.uid b/addons/escoria-core/game/core-scripts/save_data/esc_save_manager.gd.uid new file mode 100644 index 0000000..747f384 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/save_data/esc_save_manager.gd.uid @@ -0,0 +1 @@ +uid://bgs080lh0240j diff --git a/addons/escoria-core/game/core-scripts/save_data/esc_savegame.gd b/addons/escoria-core/game/core-scripts/save_data/esc_savegame.gd new file mode 100644 index 0000000..b49ea0e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/save_data/esc_savegame.gd @@ -0,0 +1,45 @@ +## Resource used for holding savegames data. +extends Resource +class_name ESCSaveGame + +## Access key for the main data last_scene_global_id. +const MAIN_LAST_SCENE_GLOBAL_ID_KEY = "last_scene_global_id" + +## Access key for the main data current_scene_filename. +const MAIN_CURRENT_SCENE_FILENAME_KEY = "current_scene_filename" + +## Escoria version which the savegame was created with. +@export var escoria_version: String + +## Game version which the savegame was created with. +@export var game_version: String = "" + +## Name of the savegame. Can be custom value, provided by the player. +@export var name: String = "" + +## Date of creation of the savegame. +@export var date: Dictionary = {} + +## Main data to be saved. +@export var main: Dictionary = {} + +## Escoria Global variables exported from ESCGlobalsManager. +@export var globals: Dictionary = {} + +## Inventory items. +@export var inventory: Array = [] + +## Escoria objects exported from ESCObjectsManager. +@export var objects: Dictionary = {} + +## Running event exported from ESCEventManager. +@export var events: Dictionary = {} + +## Enabled ESCTerrain navpolygons. +@export var terrain_navpolys: Dictionary = {} + +## Settings. +@export var settings: Dictionary = {} + +## Custom data. +@export var custom_data: Dictionary = {} diff --git a/addons/escoria-core/game/core-scripts/save_data/esc_savegame.gd.uid b/addons/escoria-core/game/core-scripts/save_data/esc_savegame.gd.uid new file mode 100644 index 0000000..0755f13 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/save_data/esc_savegame.gd.uid @@ -0,0 +1 @@ +uid://blcoeovuyvld2 diff --git a/addons/escoria-core/game/core-scripts/save_data/esc_savesettings.gd b/addons/escoria-core/game/core-scripts/save_data/esc_savesettings.gd new file mode 100644 index 0000000..ccc3d1e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/save_data/esc_savesettings.gd @@ -0,0 +1,50 @@ +## Resource holding game settings. Note that we call directly to ProjectSettings +## for instance variable initialization since this class is instantiated from +## escoria.gd. +extends Resource +class_name ESCSaveSettings + +## Version of ESCORIA Framework. +@export var escoria_version: String + +## Language of displayed text. +@export var text_lang: String = ProjectSettings.get_setting( + "escoria/main/text_lang" +) + +## Language of voice speech. +@export var voice_lang: String = ProjectSettings.get_setting( + "escoria/main/voice_lang" +) + +## Whether speech is enabled. +@export var speech_enabled: bool = ProjectSettings.get_setting( + "escoria/sound/speech_enabled") + +## Master volume (mix of music, voice and sfx). +@export var master_volume: float = ProjectSettings.get_setting( + "escoria/sound/master_volume") + +## Volume of music only. +@export var music_volume: float = ProjectSettings.get_setting( + "escoria/sound/music_volume") + +## Volume of SFX only. +@export var sfx_volume: float = ProjectSettings.get_setting( + "escoria/sound/sfx_volume" +) + +## Speech volume only. +@export var speech_volume: float = ProjectSettings.get_setting( + "escoria/sound/speech_volume") + +## Ambient volume only. +@export var ambient_volume: float = ProjectSettings.get_setting( + "escoria/sound/ambient_volume") + +## True if game has to be fullscreen. +@export var fullscreen: bool = ProjectSettings.get_setting( + "display/window/size/mode") == DisplayServer.WINDOW_MODE_FULLSCREEN + +## Dictionary containing all user-defined settings. +@export var custom_settings: Dictionary diff --git a/addons/escoria-core/game/core-scripts/save_data/esc_savesettings.gd.uid b/addons/escoria-core/game/core-scripts/save_data/esc_savesettings.gd.uid new file mode 100644 index 0000000..634f490 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/save_data/esc_savesettings.gd.uid @@ -0,0 +1 @@ +uid://phl6j4s60cx0 diff --git a/addons/escoria-core/game/core-scripts/save_data/esc_settings_manager.gd b/addons/escoria-core/game/core-scripts/save_data/esc_settings_manager.gd new file mode 100644 index 0000000..18d4247 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/save_data/esc_settings_manager.gd @@ -0,0 +1,295 @@ +## Escoria settings manager. +## @MANAGER +class_name ESCSettingsManager + +## Template for settings filename. +const SETTINGS_TEMPLATE: String = "settings.tres" + +## Variable containing the settings folder obtained from Project Settings. +var settings_folder: String + +## Dictionary containing specific settings that gamedev wants to save in settings. +## This variable is access-free. Getting its content is gamedev's duty. It is +## saved with other Escoria settings data when save_settings() is called. +var custom_settings: Dictionary + + +## Constructor of ESCSettingsManager object.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(): + # We leave the calls to ProjectSettings as-is since this constructor can be + # called from escoria.gd's own. + settings_folder = ProjectSettings.get_setting("escoria/main/settings_path") + + +## Apply the loaded settings.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func apply_settings() -> void: + if not Engine.is_editor_hint(): + escoria.logger.info( + self, + "******* settings loaded" + ) + + AudioServer.set_bus_volume_db( + AudioServer.get_bus_index(escoria.BUS_MASTER), + linear_to_db( + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.MASTER_VOLUME + ) + ) + ) + AudioServer.set_bus_volume_db( + AudioServer.get_bus_index(escoria.BUS_SFX), + linear_to_db( + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SFX_VOLUME + ) + ) + ) + AudioServer.set_bus_volume_db( + AudioServer.get_bus_index(escoria.BUS_MUSIC), + linear_to_db( + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.MUSIC_VOLUME + ) + ) + ) + AudioServer.set_bus_volume_db( + AudioServer.get_bus_index(escoria.BUS_SPEECH), + linear_to_db( + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_VOLUME + ) + ) + ) + AudioServer.set_bus_volume_db( + AudioServer.get_bus_index(escoria.BUS_AMBIENT), + linear_to_db( + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.AMBIENT_VOLUME + ) + ) + ) + + var mode = Window.MODE_EXCLUSIVE_FULLSCREEN if ESCProjectSettingsManager.get_setting(ESCProjectSettingsManager.FULLSCREEN) else Window.MODE_WINDOWED + DisplayServer.window_set_mode(mode) + + TranslationServer.set_locale( + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TEXT_LANG + ) + ) + + escoria.game_scene.apply_custom_settings(custom_settings) + + +## Save the settings resource to project settings.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |settings|`ESCSaveSettings`|ESCSaveSettings resource to save.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func save_settings_resource_to_project_settings(settings: ESCSaveSettings): + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.TEXT_LANG, + settings.text_lang + ) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.VOICE_LANG, + settings.voice_lang + ) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.SPEECH_ENABLED, + settings.speech_enabled + ) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.MASTER_VOLUME, + settings.master_volume + ) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.MUSIC_VOLUME, + settings.music_volume + ) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.SFX_VOLUME, + settings.sfx_volume + ) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.SPEECH_VOLUME, + settings.speech_volume + ) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.AMBIENT_VOLUME, + settings.ambient_volume + ) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.FULLSCREEN, + settings.fullscreen + ) + custom_settings = settings.custom_settings + + +## Load the game settings from the settings file.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func load_settings(): + var save_settings_path: String = settings_folder.path_join(SETTINGS_TEMPLATE) + if not FileAccess.file_exists(save_settings_path): + escoria.logger.warn( + self, + "Settings file %s doesn't exist" % save_settings_path + + "Setting default settings." + ) + save_settings() + + var settings: ESCSaveSettings = load(save_settings_path) + save_settings_resource_to_project_settings(settings) + + +## Load the game settings from the settings file.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `ESCSaveSettings` value. (`ESCSaveSettings`) +func get_settings() -> ESCSaveSettings: + var settings: ESCSaveSettings = ESCSaveSettings.new() + var plugin_config = ConfigFile.new() + plugin_config.load("res://addons/escoria-core/plugin.cfg") + settings.escoria_version = plugin_config.get_value("plugin", "version") + + settings.text_lang = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TEXT_LANG + ) + settings.voice_lang = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.VOICE_LANG + ) + settings.speech_enabled = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_ENABLED + ) + settings.master_volume = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.MASTER_VOLUME + ) + settings.music_volume = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.MUSIC_VOLUME + ) + settings.sfx_volume = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SFX_VOLUME + ) + settings.speech_volume = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_VOLUME + ) + settings.ambient_volume = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.AMBIENT_VOLUME + ) + settings.fullscreen = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.WINDOW_MODE + ) in [DisplayServer.WINDOW_MODE_FULLSCREEN, DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN] + settings.custom_settings = custom_settings + + return settings + + +## Load the game settings from a dictionary.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |settings_dict|`Dictionary`|Dictionary containing the settings to load.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func load_settings_from_dict(settings_dict: Dictionary): + var settings: ESCSaveSettings = ESCSaveSettings.new() + settings.escoria_version = settings_dict["escoria_version"] + settings.text_lang = settings_dict["text_lang"] + settings.voice_lang = settings_dict["voice_lang"] + settings.speech_enabled = settings_dict["speech_enabled"] + settings.master_volume = settings_dict["master_volume"] + settings.music_volume = settings_dict["music_volume"] + settings.sfx_volume = settings_dict["sfx_volume"] + settings.speech_volume = settings_dict["speech_volume"] + settings.fullscreen = settings_dict["fullscreen"] + settings.custom_settings = settings_dict["custom_settings"] + save_settings_resource_to_project_settings(settings) + + +## Get the game settings as a dictionary.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `Dictionary` value. (`Dictionary`) +func get_settings_dict() -> Dictionary: + var settings: ESCSaveSettings = get_settings() + var settings_dict: Dictionary = {} + settings_dict["escoria_version"] = settings.escoria_version + settings_dict["text_lang"] = settings.text_lang + settings_dict["voice_lang"] = settings.voice_lang + settings_dict["speech_enabled"] = settings.speech_enabled + settings_dict["master_volume"] = settings.master_volume + settings_dict["music_volume"] = settings.music_volume + settings_dict["sfx_volume"] = settings.sfx_volume + settings_dict["speech_volume"] = settings.speech_volume + settings_dict["fullscreen"] = settings.fullscreen + settings_dict["custom_settings"] = settings.custom_settings + + return settings_dict + + +## Save the game settings in the settings file.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func save_settings(): + var settings = get_settings() + + if not DirAccess.dir_exists_absolute(settings_folder): + DirAccess.make_dir_recursive_absolute(settings_folder) + + var save_path = settings_folder.path_join(SETTINGS_TEMPLATE) + var error: int = ResourceSaver.save(settings, save_path) + if error != OK: + escoria.logger.error( + self, + "There was an issue writing settings %s" % save_path + ) diff --git a/addons/escoria-core/game/core-scripts/save_data/esc_settings_manager.gd.uid b/addons/escoria-core/game/core-scripts/save_data/esc_settings_manager.gd.uid new file mode 100644 index 0000000..10bf07e --- /dev/null +++ b/addons/escoria-core/game/core-scripts/save_data/esc_settings_manager.gd.uid @@ -0,0 +1 @@ +uid://ddh2jxjhohn8p diff --git a/addons/escoria-core/game/core-scripts/utils/esc_utils.gd b/addons/escoria-core/game/core-scripts/utils/esc_utils.gd new file mode 100644 index 0000000..14e4246 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/utils/esc_utils.gd @@ -0,0 +1,95 @@ +## A set of common utilities. +extends RefCounted +class_name ESCUtils + +## Convert radians to degrees.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |rad_angle|`float`|Angle in radians.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +static func get_deg_from_rad(rad_angle: float): + var deg = rad_to_deg(rad_angle) + if deg >= 360.0: + deg = clamp(deg, 0.0, 360.0) + if deg == 360.0: + deg = 0.0 + return deg + +## Get the content of a reg exp group by name.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |re_match|`RegExMatch`|The RegExMatch object.|yes|[br] +## |group|`String`|The name of the group.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `String` value. (`String`) +static func get_re_group(re_match: RegExMatch, group: String) -> String: + if group in re_match.names: + return re_match.strings[re_match.names[group]] + else: + return "" + +## Return a string value in the correct inferred type.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`String`|The original value.|yes|[br] +## |type_hint|`Variant`|The type it should be.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +static func get_typed_value(value: String, type_hint = []): + var regex_bool = RegEx.new() + regex_bool.compile("^true|false$") + var regex_float = RegEx.new() + regex_float.compile("^-?[0-9]*\\.[0-9]+$") + var regex_int = RegEx.new() + regex_int.compile("^-?[0-9]+$") + + if regex_float.search(value): + return float(value) + elif regex_int.search(value): + return int(value) + elif regex_bool.search(value.to_lower()): + return true if value.to_lower() == "true" else false + elif (typeof(type_hint) != TYPE_ARRAY and type_hint == TYPE_ARRAY) or \ + (typeof(type_hint) == TYPE_ARRAY and TYPE_ARRAY in type_hint) \ + and "," in value: + return value.split(",") + else: + return str(value) + +## Sanitize use of whitespaces in a string. Removes double whitespaces and converts tabs into space.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`String`|String to work on.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `String` value. (`String`) +static func sanitize_whitespace(value: String) -> String: + var tab_regex = RegEx.new() + tab_regex.compile("\\t") + var double_regex = RegEx.new() + double_regex.compile("\\s\\s+") + return double_regex.sub( + tab_regex.sub(value, " "), + " ", + true + ) diff --git a/addons/escoria-core/game/core-scripts/utils/esc_utils.gd.uid b/addons/escoria-core/game/core-scripts/utils/esc_utils.gd.uid new file mode 100644 index 0000000..90b5870 --- /dev/null +++ b/addons/escoria-core/game/core-scripts/utils/esc_utils.gd.uid @@ -0,0 +1 @@ +uid://baatkfocvpgnr diff --git a/addons/escoria-core/game/esc_autoload.gd b/addons/escoria-core/game/esc_autoload.gd new file mode 100644 index 0000000..72ef6f8 --- /dev/null +++ b/addons/escoria-core/game/esc_autoload.gd @@ -0,0 +1,248 @@ +extends Node +## This is Escoria's singleton script. +## It holds accessors to some utils, such as Escoria's logger. + +## Signal sent when Escoria is paused[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal paused + +## Signal sent when Escoria is resumed from pause[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal resumed + + +## Current game state +## * DEFAULT: Common game function +## * DIALOG: Game is playing a dialog +## * WAIT: Game is waiting +## * LOADING: Game is currently loading +enum GAME_STATE { + DEFAULT, + DIALOG, + WAIT, + LOADING, + PAUSED +} + + +## Master audio bus +const BUS_MASTER = "Master" + +## SFX audio bus +const BUS_SFX = "SFX" + +## Music audio bus +const BUS_MUSIC = "Music" + +## Speech audio bus +const BUS_SPEECH = "Speech" + +## Ambient sound audio bus +const BUS_AMBIENT = "Ambient" + +# Path to camera scene +const CAMERA_SCENE_PATH = "res://addons/escoria-core/game/scenes/camera_player/camera.tscn" + + +# Logger class +const EscLogger = preload("res://addons/escoria-core/tools/logging/esc_logger.gd") + +## Group for ESCItem's that can be collided with in a scene. Used for quick[br] +## retrieval of such nodes to easily change their attributes at the same time. +const GROUP_ITEM_CAN_COLLIDE = "item_can_collide" + +## Group for ESCItem's that are triggers +const GROUP_ITEM_TRIGGERS = "item_triggers" + + +# Logger instance +var logger := EscLogger.ESCLoggerFile.new() + +## ESC Compiler instance +var esc_compiler: ESCCompiler = ESCCompiler.new() + +## ESC Object Manager instance +var object_manager: ESCObjectManager + +## ESC Room Manager instance +var room_manager: ESCRoomManager = ESCRoomManager.new() + +## ESC Dependency Injector +var di: ESCDependencyInjector = ESCDependencyInjector.new() + +## Inventory manager instance +var inventory_manager: ESCInventoryManager + +## Action manager instance +var action_manager: ESCActionManager + +## Event manager instance +var event_manager: ESCEventManager + +## Globals registry instance +var globals_manager: ESCGlobalsManager + +## ASHES command registry instance +var command_registry: ESCCommandRegistry + +## Manager of game settings (resolution, sound, etc) +var settings_manager: ESCSettingsManager + +## Resource cache handler +var resource_cache: ESCResourceCache + +## Dialog player instantiator. This instance is called directly for dialogs. +var dialog_player: ESCDialogPlayer + +## ESCScript interpreter factory instance +var interpreter_factory: ESCInterpreterFactory + +## Inputs manager instance +var inputs_manager: ESCInputsManager + +## Savegames and settings manager +var save_manager: ESCSaveManager + +## The game scene loaded +var game_scene: ESCGame + +## The main player camera +var player_camera: ESCCamera + +## The compiled start script loaded from ProjectSettings +## escoria/main/game_start_script +var start_script: ESCScript + +## The "fallback" script to use when an action is tried on an item that hasn't +## been explicitly scripted. +var action_default_script: ESCScript + +## Whether we ran a room directly from editor, not a full game +var is_direct_room_run: bool = false + +## Whether we're quitting the game +var is_quitting: bool = false + +## Terrain of the current room +var room_terrain + +## Inventory scene +var inventory + +## The main scene +var main + +## Whether Escoria is creating a new game +var creating_new_game: bool = false +#var temp: int = 0 + +## Game actual resolution obtained from viewport. +@onready var game_size = Vector2( + ProjectSettings.get_setting("display/window/size/viewport_width"), + ProjectSettings.get_setting("display/window/size/viewport_height")) + +## Current state of Escoria (GAME_STATE enum) +@onready var current_state = GAME_STATE.DEFAULT + +## Ready function. Instantiates the main scene if running a room directly.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + init_main_scene() + +## Initializes the Escoria main scene as child of the Escoria autoloaded scene.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func init_main_scene() -> void: + # We check if we run the full game or a room scene directly + if not get_tree().current_scene is ESCMain: + # Running a room scene. We need to instantiate the main scene ourselves + # so that the Escoria scene is created and managers are instanced as well. + is_direct_room_run = true + var main_scene = preload("res://addons/escoria-core/game/main_scene.tscn").instantiate() + add_child(main_scene) + + +## Get the Escoria node. That node gives access to the Escoria scene that's instanced by the main_scene (if full game is run) or by this autoload if room is run directly.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func get_escoria(): + # We check if we run the full game or a room scene directly + if get_tree().current_scene is ESCMain: + return get_node("/root/main_scene").escoria_node + else: + return get_node("main_scene").escoria_node + + +## Pauses or unpause the game[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_paused|`bool`|if true, pauses the game. If false, unpauses the game.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_game_paused(p_paused: bool): + if p_paused: + paused.emit() + else: + resumed.emit() + + var scene_tree = get_tree() + + if is_instance_valid(scene_tree): + scene_tree.paused = p_paused + +## Called from main menu's "new game" button.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func new_game(): + get_escoria().new_game() + +## Called from main menu's "quit" button.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func quit(): + is_quitting = true + get_escoria().quit() diff --git a/addons/escoria-core/game/esc_autoload.gd.uid b/addons/escoria-core/game/esc_autoload.gd.uid new file mode 100644 index 0000000..8c7167a --- /dev/null +++ b/addons/escoria-core/game/esc_autoload.gd.uid @@ -0,0 +1 @@ +uid://dcskv8vx80gov diff --git a/addons/escoria-core/game/esc_dependency_injector.gd b/addons/escoria-core/game/esc_dependency_injector.gd new file mode 100644 index 0000000..570ec3d --- /dev/null +++ b/addons/escoria-core/game/esc_dependency_injector.gd @@ -0,0 +1,29 @@ +## Escoria dependency injector.[br] +## [br] +## Holds factory methods for replacing non-singleton Escoria core classes.[br] +## If you wish to replace any Escoria core classes with your own derived classes,[br] +## follow these steps:[br] +## 1. Create a class in your project that extends `ESCDependencyInjector`:[br] +## `extends ESCDependencyInjector`[br] +## 2. Override the methods that create the classes that you want to replace.[br] +## 3. In your game initialization, replace the `escoria.di` instance variable[br] +## with your new dependency injector: `escoria.di = MyDependencyInjector.new()`[br] +## [br] +## For more details see: https://docs.godotengine.org/en/stable/tutorials/best_practices/scene_organization.html +class_name ESCDependencyInjector + +## Factory method for creating an ESCInventoryItem from an ESCItem.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_item|`ESCItem`|The ESCItem to wrap in an ESCInventoryItem|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func esc_inventory_item(p_item: ESCItem): + return ESCInventoryItem.new(p_item) + +# TODO Add methods for relevant classes. \ No newline at end of file diff --git a/addons/escoria-core/game/esc_dependency_injector.gd.uid b/addons/escoria-core/game/esc_dependency_injector.gd.uid new file mode 100644 index 0000000..5775799 --- /dev/null +++ b/addons/escoria-core/game/esc_dependency_injector.gd.uid @@ -0,0 +1 @@ +uid://dvq6xda38jklr diff --git a/addons/escoria-core/game/esc_inputs_manager.gd b/addons/escoria-core/game/esc_inputs_manager.gd new file mode 100644 index 0000000..762da5e --- /dev/null +++ b/addons/escoria-core/game/esc_inputs_manager.gd @@ -0,0 +1,886 @@ +## Escoria inputs manager +## +## Catches, handles and distributes input events for the game. +## @MANAGER +extends Resource +class_name ESCInputsManager + + +## Valid input flags[br] +## * INPUT_ALL: All input is allowed[br] +## * INPUT_NONE: No input is allowed at all[br] +## * INPUT_SKIP: Only skipping dialogs is allowed +enum { + INPUT_ALL, + INPUT_NONE, + INPUT_SKIP, +} + + +## Input action for use by InputMap +const ESC_SHOW_DEBUG_PROMPT = "esc_show_debug_prompt" + +## Input action for use by InputMap that represents a "primary action" from an +## input device, such as a left-click on a mouse or the X button on an XBox +## controller +const ESC_UI_PRIMARY_ACTION = "esc_ui_primary_action" + + +## The current input mode +var input_mode = INPUT_ALL + +## A LIFO stack of hovered items +var hover_stack: HoverStack + +## The global id of the topmost item from the hover_stack +var hotspot_focused: String = "" + +## Function reference that can be used to intercept and process input events. +## If set, this function must have the following signature:[br] +##[br] +## (event: InputEvent, is_default_state: bool) -> bool[br] +##[br] +## #### Parameters[br] +##[br] +## - event: The event to process[br] +## - is_default_state: Whether the current state is escoria.GAME_STATE.DEFAULT[br] +##[br] +## **Returns** Whether the function processed the event. +var custom_input_handler = null + +## The currently hovered element. Usually the one on top of the hover stack. +var _hovered_element = null + +## Constructor.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(): + escoria.event_manager.connect("event_finished", Callable(self, "_on_event_finished")) + hover_stack = HoverStack.new() + hover_stack.connect("hover_stack_changed", Callable(self, "_on_hover_stack_changed")) + +## Called when an event is finished, so that the current hotspot is reset.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |return_code|`int`|The return code of the event.|yes|[br] +## |event_name|`String`|The name of the event.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_event_finished(return_code: int, event_name: String): + if _hovered_element == null: + hotspot_focused = "" + + +## Register core signals (from escoria.gd)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_core(): + escoria.game_scene.request_pause_menu.connect(_on_pause_menu_requested) + +## Connect the item signals to the local methods.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`Node`|The inventory item node to register.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_inventory_item(item: Node): + item.mouse_left_inventory_item.connect(_on_mouse_left_click_inventory_item) + item.mouse_double_left_inventory_item.connect(_on_mouse_double_left_click_inventory_item) + item.mouse_right_inventory_item.connect(_on_mouse_right_click_inventory_item) + item.inventory_item_focused.connect(_on_mouse_entered_inventory_item) + item.inventory_item_unfocused.connect(_on_mouse_exited_inventory_item) + +## Connect background signals to local methods.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |background|`ESCBackground`|The background to register.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_background(background: ESCBackground): + background.left_click_on_bg.connect(_on_left_click_on_bg) + background.right_click_on_bg.connect(_on_right_click_on_bg) + background.double_left_click_on_bg.connect(_on_double_left_click_on_bg) + background.mouse_wheel_up.connect(_on_mousewheel_action.bind(1)) + background.mouse_wheel_down.connect(_on_mousewheel_action.bind(-1)) + background.hovered_bg.connect(_on_hover_bg) + + +## Registers a function that can be used to intercept and process input events. `callback` must have the following signature: (event: InputEvent, is_default_state: bool) -> bool ...where:[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |callback|`Variant`|Function reference satisfying the above contract event The event to process is_default_state Whether the current state is escoria.GAME_STATE.DEFAULT returns whether the function processed the event `callback` is responsible for calling `get_tree().set_input_as_handled()`, if appropriate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register_custom_input_handler(callback) -> void: + custom_input_handler = callback + + +## If a callback was specified via `register_custom_input_handler()`, forwards the event to the callback and returns its result; otherwise,[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`InputEvent`|The event to process|yes|[br] +## |is_default_state|`bool`|Whether the current state is escoria.GAME_STATE.DEFAULT|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns `false`. (`bool`) +func try_custom_input_handler(event: InputEvent, is_default_state: bool) -> bool: + if custom_input_handler: + return custom_input_handler.call(event, is_default_state) + else: + return false + +## Callback called by hover stack content change.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_hover_stack_changed(): + if hover_stack.is_empty(): + unset_hovered_node(_hovered_element) + else: + set_hovered_node(hover_stack.get_top_item()) + + +## Sets the hovered node and calls its mouse_entered() method if it was the top most item in hover_stack.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|the item that was focused (mouse_entered)|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func set_hovered_node(item: ESCItem) -> bool: + if _hovered_element != item \ + and escoria.action_manager.is_object_actionable(item.global_id) \ + or (item is ESCPlayer and not (item as ESCPlayer).selectable): + _hovered_element = item + _hovered_element.mouse_entered() + return true + # If tested item was already hovered + # or is not actionable (not selectable for ESCPlayer) then do nothing + if _hovered_element == item \ + or not escoria.action_manager.is_object_actionable(item.global_id) \ + or (item is ESCPlayer and not (item as ESCPlayer).selectable): + return true + if not is_instance_valid(_hovered_element) or hover_stack.get_top_item() != item: + _hovered_element = item + _hovered_element.mouse_entered() + return true + # Else, the tested item is currently on top of hover stack, then do nothing + else: + return false + + +## Unsets the hovered node.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|the item that was unfocused (mouse_exited)|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func unset_hovered_node(item: ESCItem): + if item == null: + return + if _hovered_element == item: + _hovered_element.do_mouse_exited() + _hovered_element = null + hotspot_focused = "" + + +# Background was hovered +func _on_hover_bg() -> void: + escoria.main.current_scene.game.hovered_bg() + +## The background was clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|Position of the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_left_click_on_bg(position: Vector2) -> void: + if input_mode == INPUT_ALL: + hotspot_focused = "" + escoria.logger.info( + self, + "Left click on background at %s." % str(position) + ) + escoria.main.current_scene.game.left_click_on_bg(position) + +## The background was double-clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|Position of the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_double_left_click_on_bg(position: Vector2) -> void: + if input_mode == INPUT_ALL: + hotspot_focused = "" + escoria.logger.info( + self, + "Double left click on background at %s." % str(position) + ) + escoria.main.current_scene.game.left_double_click_on_bg(position) + +## The background was clicked with the RMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |position|`Vector2`|Position of the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_right_click_on_bg(position: Vector2) -> void: + if input_mode == INPUT_ALL and hotspot_focused.is_empty(): + escoria.logger.info( + self, + "Right click on background at %s." % str(position) + ) + escoria.main.current_scene.game.right_click_on_bg(position) + +## An inventory item was clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|The global id of the clicked inventory item.|yes|[br] +## |event|`InputEvent`|The input event received.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_left_click_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + escoria.logger.info( + self, + "Inventory item %s left clicked." % inventory_item_global_id + ) + escoria.main.current_scene.game.left_click_on_inventory_item( + inventory_item_global_id, + event + ) + +## An inventory item was clicked with the RMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|The global id of the clicked inventory item.|yes|[br] +## |event|`InputEvent`|The input event received.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_right_click_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + if input_mode == INPUT_ALL: + escoria.logger.info( + self, + "Inventory item %s right clicked." % inventory_item_global_id + ) + escoria.main.current_scene.game.right_click_on_inventory_item( + inventory_item_global_id, + event + ) + +## An inventory item was double-clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|The global id of the clicked inventory item.|yes|[br] +## |event|`InputEvent`|The input event received.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_double_left_click_inventory_item( + inventory_item_global_id: String, + event: InputEvent +) -> void: + if input_mode == INPUT_ALL: + escoria.logger.info( + self, + "Inventory item %s double left clicked." % inventory_item_global_id + ) + escoria.main.current_scene.game.left_double_click_on_inventory_item( + inventory_item_global_id, + event + ) + +## The mouse entered an inventory item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item_global_id|`String`|The global id of the inventory item that is hovered.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_entered_inventory_item(inventory_item_global_id: String) -> void: + escoria.logger.info( + self, + "Inventory item %s focused." % inventory_item_global_id + ) + escoria.main.current_scene.game.inventory_item_focused( + inventory_item_global_id + ) + +## The mouse exited an inventory item.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_exited_inventory_item() -> void: + escoria.logger.info( + self, + "Inventory item unfocused." + ) + escoria.main.current_scene.game.inventory_item_unfocused() + +## The mouse entered an Escoria item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The Escoria item hovered.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_entered_item(item: ESCItem) -> void: + if item as ESCPlayer and not (item as ESCPlayer).selectable: + escoria.logger.trace( + self, + "Ignoring mouse entering player %s: Player not selectable." % [item.global_id] + ) + if hover_stack.is_empty(): + hotspot_focused = "" + escoria.main.current_scene.game.element_unfocused() + else: + hotspot_focused = hover_stack.get_top_item().global_id + escoria.main.current_scene.game.element_focused(hotspot_focused) + return + + if not escoria.action_manager.is_object_actionable(item.global_id): + escoria.logger.debug( + self, + "Ignoring mouse entering item %s." % [item.global_id] + ) + return + + escoria.logger.info( + self, + "Item focused: %s" % item.global_id + ) + + hotspot_focused = item.global_id + escoria.main.current_scene.game.element_focused(item.global_id) + +## The mouse exited an Escoria item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The Escoria item hovered.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_exited_item(item: ESCItem) -> void: + var object: ESCObject = escoria.object_manager.get_object(item.global_id) + if object and not object.interactive: + hover_stack.erase_item(item) + escoria.main.current_scene.game.element_unfocused() + return + + if object and not object.interactive: + return + if object and is_instance_valid(object.node) and object.node is ESCPlayer and not (object.node as ESCPlayer).selectable: + hotspot_focused = "" + return + + escoria.logger.info( + self, + "Item unfocused: %s" % hotspot_focused + ) + + if hover_stack.is_empty(): + hotspot_focused = "" + escoria.main.current_scene.game.element_unfocused() + else: + hotspot_focused = hover_stack.get_top_item().global_id + escoria.main.current_scene.game.element_focused(hotspot_focused) + + +## Function called when the item is set to non-interactive, to re-trigger an input on underlying item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The `ESCItem` that was set non-interactive|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func on_item_non_interactive(item: ESCItem) -> void: + var object: ESCObject = escoria.object_manager.get_object(item.global_id) + if object and not object.interactive: + hover_stack.erase_item(item) + escoria.main.current_scene.game.element_unfocused() + + if hover_stack.is_empty(): + return + else: + var new_item = hover_stack.get_top_item() + escoria.action_manager.set_action_input_state(ESCActionManager.ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM) + new_item.mouse_entered() + + +# An Escoria item was clicked with the LMB +# +# #### Parameters +# +# - item: The Escoria item clicked +# - event: The input event from the click +func _on_mouse_left_clicked_item(item: ESCItem, event: InputEvent) -> void: + if input_mode == INPUT_ALL: + # Manage clicking through ESCPlayer (if ESCPlayer.selectable is false) + if item as ESCPlayer and not (item as ESCPlayer).selectable: + escoria.logger.trace( + self, + "Ignoring left click on player %s: Player not selectable." + % [item.global_id] + ) + + # Get next object in hover stack and forward event to it + if not hover_stack.is_empty(): + var next_item = hover_stack.pop_top_item() + _on_mouse_left_clicked_item(next_item, event) + else: # if no next object, consider this click as background click + hotspot_focused = "" + _on_left_click_on_bg(event.position) + return + + # Clicked object can't be actioned and there is no other object behind + # We consider this click as a background click + if not escoria.action_manager.is_object_actionable(item.global_id) \ + and hover_stack.is_empty(): + hotspot_focused = "" + _on_left_click_on_bg(event.position) + return + + # Finally, execute the action on the ESCItem + hotspot_focused = item.global_id + escoria.main.current_scene.game.left_click_on_item( + item.global_id, + event + ) + +## An Escoria item was double-clicked with the LMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The Escoria item clicked.|yes|[br] +## |event|`InputEvent`|The input event from the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_left_double_clicked_item( + item: ESCItem, + event: InputEvent +) -> void: + if input_mode == INPUT_ALL: + # Manage clicking through ESCPlayer (if ESCPlayer.selectable is false) + if item as ESCPlayer and not (item as ESCPlayer).selectable: + escoria.logger.trace( + self, + "Ignoring double left click on player %s: Player not selectable." + % [item.global_id] + ) + + # Get next object in hover stack and forward event to it + if not hover_stack.is_empty(): + var next_item = hover_stack.pop_top_item() + _on_mouse_left_double_clicked_item(next_item, event) + else: # if no next object, consider this click as background click + hotspot_focused = "" + _on_double_left_click_on_bg(event.position) + return + + # Clicked object can't be actioned and there is no other object behind + # We consider this click as a background click + if not escoria.action_manager.is_object_actionable(item.global_id) \ + and hover_stack.is_empty(): + hotspot_focused = "" + _on_double_left_click_on_bg(event.position) + return + + # Finally, execute the action on the ESCItem + hotspot_focused = item.global_id + escoria.main.current_scene.game.left_double_click_on_item( + item.global_id, + event + ) + +## An Escoria item was clicked with the RMB.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item|`ESCItem`|The Escoria item clicked.|yes|[br] +## |event|`InputEvent`|The input event from the click.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_mouse_right_clicked_item(item: ESCItem, event: InputEvent) -> void: + if input_mode == INPUT_ALL: + if item as ESCPlayer and not (item as ESCPlayer).selectable: + escoria.logger.debug( + self, + "Ignoring right click on player %s: Player not selectable." % [item.global_id] + ) + + if not hover_stack.is_empty(): + var next_item = hover_stack.pop_top_item() + _on_mouse_right_clicked_item(next_item, event) + return + + if not escoria.action_manager.is_object_actionable(item.global_id) \ + and hover_stack.is_empty(): + # Treat this as a background click now + hotspot_focused = "" + _on_right_click_on_bg(event.position) + return + + var actual_item + + # We check if the clicked object is ESCPlayer and not selectable. If so + # we consider we clicked through it. + var object: ESCObject = escoria.object_manager.get_object(item.global_id) + if object.node is ESCPlayer and not (object.node as ESCPlayer).selectable: + actual_item = hover_stack.get_top_item() + else: + actual_item = item + + if actual_item == null: + if event.position: + (escoria.main.current_scene.game as ESCGame).right_click_on_bg(event.position) + else: + escoria.logger.info( + self, + "Clicked item %s with event %s cannot be activated (player not selectable or not interactive).\n" + % [item.global_id, event] + + "No valid item found in the items stack. Action canceled." + ) + else: + escoria.logger.info( + self, + "Item %s right clicked with event %s." % [actual_item.global_id, event] + ) + hotspot_focused = actual_item.global_id + escoria.main.current_scene.game.right_click_on_item( + actual_item.global_id, + event + ) + + +# The mousewheel was turned +# +# #### Parameters +# +# - direction: The direction the wheel was turned. 1 = up, -1 = down +func _on_mousewheel_action(direction: int): + escoria.main.current_scene.game.mousewheel_action(direction) + +## Event when the pause menu was requested.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_pause_menu_requested(): + escoria.main.current_scene.game.pause_game() + + +## Hover Stack implementation. +class HoverStack: + + + ## Emitted when the content of the hover stack has changed[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + signal hover_stack_changed + + ## Emitted when the hover stack was emptied[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + signal hover_stack_emptied + + + ## Array representing the hover stack + var hover_stack: Array = [] + + + ## Add the given item to the stack if not already in it.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |item|`Variant`|the item to add to the hover stack|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func add_item(item): + if item is ESCPlayer and not (item as ESCPlayer).selectable: + return + if not hover_stack.has(item): + hover_stack.push_back(item) + _sort() + hover_stack_changed.emit() + + + ## Add the items contained in given list to the stack if not already in it.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |items|`Array`|the items list (array) to add to the hover stack|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func add_items(items: Array): + for item in items: + if escoria.action_manager.is_object_actionable(item.global_id): + add_item(item) + + + ## Clean the hover stack[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func clean(): + for e in hover_stack: + if e == null or !is_instance_valid(e): + hover_stack.erase(e) + hover_stack_changed.emit() + + + ## Pops the top element of the hover stack and returns it[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func pop_top_item(): + var ret = hover_stack.pop_back() + if is_instance_valid(ret): + hover_stack_changed.emit() + return ret + + + ## The top element of the hover stack[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the top element of the hover stack. (`Variant`) + func get_top_item(): + return hover_stack.back() + + + ## Remove the given item from the stack[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |item|`Variant`|the item to remove from the hover stack|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func erase_item(item): + if hover_stack.has(item): + hover_stack.erase(item) + _sort() + hover_stack_changed.emit() + + + ## Clear the stack of hovered items[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func clear(): + hover_stack = [] + hover_stack_emptied.emit() + + + ## True if the hover stack is empty, else false[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns true if the hover stack is empty, else false. (`bool`) + func is_empty() -> bool: + return hover_stack.is_empty() + + + ## Sort the hover stack by items' z-index.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func _sort(): + hover_stack.sort_custom(Callable(HoverStackSorter, "sort_ascending_z_index")) + + + ## True if the hover stack contains the given item[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |item|`Variant`|the item to search|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns true if the hover stack contains the given item. (`bool`) + func has(item) -> bool: + return hover_stack.has(item) + + + ## The hover stack array[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns the hover stack array. (`Array`) + func get_all() -> Array: + return hover_stack + + ## Z Sorter class for hover stack + class HoverStackSorter: + static func sort_ascending_z_index(a, b): + if a.z_index < b.z_index: + return true + return false diff --git a/addons/escoria-core/game/esc_inputs_manager.gd.uid b/addons/escoria-core/game/esc_inputs_manager.gd.uid new file mode 100644 index 0000000..ade3723 --- /dev/null +++ b/addons/escoria-core/game/esc_inputs_manager.gd.uid @@ -0,0 +1 @@ +uid://2v5nd5egkpvu diff --git a/addons/escoria-core/game/esc_project_settings_manager.gd b/addons/escoria-core/game/esc_project_settings_manager.gd new file mode 100644 index 0000000..3a2b10b --- /dev/null +++ b/addons/escoria-core/game/esc_project_settings_manager.gd @@ -0,0 +1,211 @@ +## Registers and allows access to Escoria-specific project settings. +## @MANAGER +extends Resource +class_name ESCProjectSettingsManager + +## Root for Escoria-specific project settings. +const _ESCORIA_SETTINGS_ROOT = "escoria" + +## UI-related Escoria project settings. +const _UI_ROOT = "ui" + +## Path to the default dialog type setting. +const DEFAULT_DIALOG_TYPE = _ESCORIA_SETTINGS_ROOT + "/" + _UI_ROOT + "/" + "default_dialog_type" +## Path to the default transition setting. +const DEFAULT_TRANSITION = _ESCORIA_SETTINGS_ROOT + "/" + _UI_ROOT + "/" + "default_transition" +## Path to the dialog managers setting. +const DIALOG_MANAGERS = _ESCORIA_SETTINGS_ROOT + "/" + _UI_ROOT + "/" + "dialog_managers" +## Path to the game scene setting. +const GAME_SCENE = _ESCORIA_SETTINGS_ROOT + "/" + _UI_ROOT + "/" + "game_scene" +## Path to the inventory item size setting. +const INVENTORY_ITEM_SIZE = _ESCORIA_SETTINGS_ROOT + "/" + _UI_ROOT + "/" + "inventory_item_size" +## Path to the inventory items path setting. +const INVENTORY_ITEMS_PATH = _ESCORIA_SETTINGS_ROOT + "/" + _UI_ROOT + "/" + "inventory_items_path" +## Path to the transition paths setting. +const TRANSITION_PATHS = _ESCORIA_SETTINGS_ROOT + "/" + _UI_ROOT + "/" + "transition_paths" + +## Main Escoria project settings root. +const _MAIN_ROOT = "main" + +## Path to the command directories setting. +const COMMAND_DIRECTORIES = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "command_directories" +## Path to the force quit setting. +const FORCE_QUIT = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "force_quit" +## Path to the game migration path setting. +const GAME_MIGRATION_PATH = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "game_migration_path" +## Path to the game version setting. +const GAME_VERSION = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "game_version" +## Path to the game start script setting. +const GAME_START_SCRIPT = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "game_start_script" +## Path to the action default script setting. +const ACTION_DEFAULT_SCRIPT = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "action_default_script" +## Path to the savegames path setting. +const SAVEGAMES_PATH = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "savegames_path" +## Path to the settings path setting. +const SETTINGS_PATH = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "settings_path" +## Path to the text language setting. +const TEXT_LANG = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "text_lang" +## Path to the voice language setting. +const VOICE_LANG = _ESCORIA_SETTINGS_ROOT + "/" + _MAIN_ROOT + "/" + "voice_lang" + +## Debug-related Escoria project settings root. +const _DEBUG_ROOT = "debug" + +## Path to the crash message setting. +const CRASH_MESSAGE = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "crash_message" +## Path to the development language setting. +const DEVELOPMENT_LANG = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "development_lang" +## If enabled, displays the room selection box for quick room change +const ENABLE_ROOM_SELECTOR = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "enable_room_selector" +## Path to the log file path setting. +const LOG_FILE_PATH = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "log_file_path" +## Path to the log level setting. +const LOG_LEVEL = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "log_level" +## Path to the room selector room directory setting. +const ROOM_SELECTOR_ROOM_DIR = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "room_selector_room_dir" +## Path to the terminate on errors setting. +const TERMINATE_ON_ERRORS = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "terminate_on_errors" +## Path to the terminate on warnings setting. +const TERMINATE_ON_WARNINGS = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "terminate_on_warnings" +## If enabled, displays the hover stack on screen +const ENABLE_HOVER_STACK_VIEWER = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "enable_hover_stack_viewer" +## If enabled, performs analysis of scripts while the game is running. Used to help find potential issues at runtime. +const PERFORM_SCRIPT_ANALYSIS_AT_RUNTIME = _ESCORIA_SETTINGS_ROOT + "/" + _DEBUG_ROOT + "/" + "perform_script_analysis_at_runtime" + +## Sound-related Escoria project settings root. +const _SOUND_ROOT = "sound" + +## Path to the master volume setting. +const MASTER_VOLUME = _ESCORIA_SETTINGS_ROOT + "/" + _SOUND_ROOT + "/" + "master_volume" +## Path to the music volume setting. +const MUSIC_VOLUME = _ESCORIA_SETTINGS_ROOT + "/" + _SOUND_ROOT + "/" + "music_volume" +## Path to the sfx volume setting. +const SFX_VOLUME = _ESCORIA_SETTINGS_ROOT + "/" + _SOUND_ROOT + "/" + "sfx_volume" +## Path to the speech enabled setting. +const SPEECH_ENABLED = _ESCORIA_SETTINGS_ROOT + "/" + _SOUND_ROOT + "/" + "speech_enabled" +## Path to the speech extension setting. +const SPEECH_EXTENSION = _ESCORIA_SETTINGS_ROOT + "/" + _SOUND_ROOT + "/" + "speech_extension" +## Path to the speech folder setting. +const SPEECH_FOLDER = _ESCORIA_SETTINGS_ROOT + "/" + _SOUND_ROOT + "/" + "speech_folder" +## Path to the speech volume setting. +const SPEECH_VOLUME = _ESCORIA_SETTINGS_ROOT + "/" + _SOUND_ROOT + "/" + "speech_volume" +## Path to the ambient volume setting. +const AMBIENT_VOLUME = _ESCORIA_SETTINGS_ROOT + "/" + _SOUND_ROOT + "/" + "ambient_volume" + +## Platform-related Escoria project settings root. +const _PLATFORM_ROOT = "platform" + +## Path to the skip cache setting. +const SKIP_CACHE = _ESCORIA_SETTINGS_ROOT + "/" + _PLATFORM_ROOT + "/" + "skip_cache" +## Path to the skip cache mobile setting. +const SKIP_CACHE_MOBILE = _ESCORIA_SETTINGS_ROOT + "/" + _PLATFORM_ROOT + "/" + "skip_cache.mobile" + +## Godot Windows project settings root. +const DISPLAY = "display" +const WINDOW = "window" +const SIZE = "size" +## Path to the window mode setting. +const WINDOW_MODE = DISPLAY + "/" + WINDOW + "/" + SIZE + "/" + "mode" +## Path to the fullscreen setting. +const FULLSCREEN = DISPLAY + "/" + WINDOW + "/" + SIZE + "/" + "fullscreen" + + +## Register a new project setting if it hasn't been defined already[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Fully qualified Project Settings key to register.|yes|[br] +## |default_value|`Variant`|Default value|yes|[br] +## |info|`Dictionary`|Property info for the setting|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +static func register_setting(name: String, default_value, info: Dictionary) -> void: + if default_value == null: + push_error("Default_value cannot be null. Use remove_setting function to remove settings.") + assert(false) + + ProjectSettings.set_setting( + name, + default_value + ) + if default_value != null: + info.name = name + + # Project settings require a "type" to be set + if not "type" in info: + info.type=typeof(default_value) + ProjectSettings.add_property_info(info) + + +## Removes the specified project setting.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Fully qualified Project Settings key to remove.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +static func remove_setting(name: String) -> void: + if not ProjectSettings.has_setting(name): + push_error("Cannot remove project setting %s - it does not exist." % name) + assert(false) + ProjectSettings.set_setting( + name, + null + ) + + +## Retrieves the specified project setting.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |key|`String`|Project setting name.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the value of the project setting located with key. (`Variant`) +static func get_setting(key: String): + if not ProjectSettings.has_setting(key): + push_error("Parameter %s is not set!" % key) + assert(false) + return ProjectSettings.get_setting(key) + + +## Sets the specified project setting to the provided value.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |key|`String`|Project setting name.|yes|[br] +## |value|`Variant`|Project setting value.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +static func set_setting(key: String, value) -> void: + ProjectSettings.set_setting(key, value) + + +## Simple wrapper for consistency's sake.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |key|`String`|Project setting name.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns true iff the project setting exists. (`bool`) +static func has_setting(key: String) -> bool: + return ProjectSettings.has_setting(key) diff --git a/addons/escoria-core/game/esc_project_settings_manager.gd.uid b/addons/escoria-core/game/esc_project_settings_manager.gd.uid new file mode 100644 index 0000000..3a02873 --- /dev/null +++ b/addons/escoria-core/game/esc_project_settings_manager.gd.uid @@ -0,0 +1 @@ +uid://c47moo50nuqxk 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 diff --git a/addons/escoria-core/game/escoria.gd.uid b/addons/escoria-core/game/escoria.gd.uid new file mode 100644 index 0000000..b9d1217 --- /dev/null +++ b/addons/escoria-core/game/escoria.gd.uid @@ -0,0 +1 @@ +uid://cdp5mdxmuvr22 diff --git a/addons/escoria-core/game/escoria.tscn b/addons/escoria-core/game/escoria.tscn new file mode 100644 index 0000000..6d8a662 --- /dev/null +++ b/addons/escoria-core/game/escoria.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=3 format=3 uid="uid://g1d0qhep3ywi"] + +[ext_resource type="PackedScene" uid="uid://dwlr0gu4erswn" path="res://addons/escoria-core/game/main.tscn" id="2"] +[ext_resource type="Script" uid="uid://cdp5mdxmuvr22" path="res://addons/escoria-core/game/escoria.gd" id="3"] + +[node name="escoria_scene" type="Node"] +script = ExtResource("3") + +[node name="main" parent="." instance=ExtResource("2")] + +[editable path="main"] 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() diff --git a/addons/escoria-core/game/main.gd.uid b/addons/escoria-core/game/main.gd.uid new file mode 100644 index 0000000..b99023b --- /dev/null +++ b/addons/escoria-core/game/main.gd.uid @@ -0,0 +1 @@ +uid://cgvex4ltwyld6 diff --git a/addons/escoria-core/game/main.tscn b/addons/escoria-core/game/main.tscn new file mode 100644 index 0000000..60f548b --- /dev/null +++ b/addons/escoria-core/game/main.tscn @@ -0,0 +1,52 @@ +[gd_scene load_steps=7 format=3 uid="uid://dwlr0gu4erswn"] + +[ext_resource type="Script" uid="uid://cgvex4ltwyld6" path="res://addons/escoria-core/game/main.gd" id="1"] +[ext_resource type="PackedScene" uid="uid://b0q36us3uuimq" path="res://addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.tscn" id="2"] +[ext_resource type="PackedScene" uid="uid://c1txn45mpksqd" path="res://addons/escoria-core/game/scenes/sound/esc_music_player.tscn" id="3"] +[ext_resource type="PackedScene" uid="uid://uwqpnwjmp6aq" path="res://addons/escoria-core/game/scenes/sound/esc_sound_player.tscn" id="5"] +[ext_resource type="PackedScene" uid="uid://c8ecyitwga1dx" path="res://addons/escoria-core/game/scenes/sound/esc_speech_player.tscn" id="6"] +[ext_resource type="PackedScene" uid="uid://wsdpiju6bxqd" path="res://addons/escoria-core/game/scenes/sound/esc_ambient_player.tscn" id="6_sy38t"] + +[node name="main" type="Node"] +script = ExtResource("1") + +[node name="layers" type="Node" parent="."] + +[node name="curtain" type="CanvasLayer" parent="layers"] +layer = 20 + +[node name="wait_timer" type="Timer" parent="layers"] + +[node name="debug_layer" type="CanvasLayer" parent="layers"] + +[node name="esc_prompt_popup" parent="layers/debug_layer" instance=ExtResource("2")] + +[node name="bg_music" parent="." instance=ExtResource("3")] +layout_mode = 3 +anchors_preset = 15 +offset_right = 0.0 +offset_bottom = 0.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="bg_sound" parent="." instance=ExtResource("5")] +layout_mode = 3 +anchors_preset = 15 +offset_right = 0.0 +offset_bottom = 0.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="speech" parent="." instance=ExtResource("6")] +layout_mode = 3 +anchors_preset = 15 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="bg_ambient" parent="." instance=ExtResource("6_sy38t")] +layout_mode = 3 +anchors_preset = 15 +offset_right = 0.0 +offset_bottom = 0.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/addons/escoria-core/game/main_scene.gd b/addons/escoria-core/game/main_scene.gd new file mode 100644 index 0000000..423c425 --- /dev/null +++ b/addons/escoria-core/game/main_scene.gd @@ -0,0 +1,27 @@ +## Main_scene is the entry point for Godot Engine. +## +## This scene sets up the main scene to load. +extends Node +class_name ESCMain + +## Reference to the Escoria node instance. +var escoria_node: Escoria + + +## Instanciate Escoria scene[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + escoria.logger.info(self, "Escoria starts...") + + escoria_node = preload("res://addons/escoria-core/game/escoria.tscn").instantiate() + add_child(escoria_node) + + if not escoria.is_direct_room_run: + escoria_node.init() diff --git a/addons/escoria-core/game/main_scene.gd.uid b/addons/escoria-core/game/main_scene.gd.uid new file mode 100644 index 0000000..a30f0e9 --- /dev/null +++ b/addons/escoria-core/game/main_scene.gd.uid @@ -0,0 +1 @@ +uid://cp3bquwe656qq diff --git a/addons/escoria-core/game/main_scene.tscn b/addons/escoria-core/game/main_scene.tscn new file mode 100644 index 0000000..04f419f --- /dev/null +++ b/addons/escoria-core/game/main_scene.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://b6fjpnn3uo0r1"] + +[ext_resource type="Script" uid="uid://cp3bquwe656qq" path="res://addons/escoria-core/game/main_scene.gd" id="1"] + +[node name="main_scene" type="Node"] +script = ExtResource("1") diff --git a/addons/escoria-core/game/scenes/camera_player/camera.tscn b/addons/escoria-core/game/scenes/camera_player/camera.tscn new file mode 100644 index 0000000..4dc2e9c --- /dev/null +++ b/addons/escoria-core/game/scenes/camera_player/camera.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=3 uid="uid://dmw5gicuenj53"] + +[ext_resource type="Script" uid="uid://b8xyisawuhtw3" path="res://addons/escoria-core/game/scenes/camera_player/esc_camera.gd" id="1"] + +[node name="camera" type="Camera2D"] +current = true +drag_horizontal_enabled = true +drag_vertical_enabled = true +script = ExtResource("1") diff --git a/addons/escoria-core/game/scenes/camera_player/esc_camera.gd b/addons/escoria-core/game/scenes/camera_player/esc_camera.gd new file mode 100644 index 0000000..6473121 --- /dev/null +++ b/addons/escoria-core/game/scenes/camera_player/esc_camera.gd @@ -0,0 +1,525 @@ +## Camera handling for Escoria scenes. +extends Camera2D +class_name ESCCamera + +## Reference to the tween node for animating camera movements. +var _tween: Tween3: + get = get_tween + +## Target position of the camera. +var _target: Vector2 = Vector2() + +## The object to follow. +var _follow_target: Node = null + +## Target zoom of the camera. +var _zoom_target: Vector2 + +## Prepare the tween for camera movement.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + _tween = Tween3.new(self) + _tween.finished.connect(_target_reached) + +## Update the position if the followed target is moving.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |_delta|`Variant`|Frame delta time|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _process(_delta): + if is_instance_valid(_follow_target) and not _tween.is_running() and _follow_target.has_moved(): + self.global_position = _follow_target.global_position + +## Register this camera with the object manager so it can be used before being made active as part of the current scene tree.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |room|`Variant`|The room with which to register the camera|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func register(room = null): + escoria.object_manager.register_object( + ESCObject.new( + escoria.object_manager.CAMERA, + self + ), + room, + true + ) + +## The camera's tween instance.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the camera's tween instance. (`Tween3`) +func get_tween() -> Tween3: + return _tween + +## Sets camera limits so it doesn't go out of the scene.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |limits|`ESCCameraLimits`|The limits to set|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_limits(limits: ESCCameraLimits): + self.limit_left = limits.limit_left + self.limit_right = limits.limit_right + self.limit_top = limits.limit_top + self.limit_bottom = limits.limit_bottom + +## Enable or disable drag margins for the camera.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_dm_h_enabled|`Variant`|Enable horizontal drag margin|yes|[br] +## |p_dm_v_enabled|`Variant`|Enable vertical drag margin|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_drag_margin_enabled(p_dm_h_enabled, p_dm_v_enabled): + self.drag_horizontal_enabled = p_dm_h_enabled + self.drag_vertical_enabled = p_dm_v_enabled + +## Set the target for the camera to move to.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_target|`Variant`|Object to target|yes|[br] +## |p_time|`float`|Number of seconds for the camera to reach the target|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_target(p_target, p_time : float = 0.0): + _resolve_target_and_zoom(p_target) + + escoria.logger.info( + self, + "Current camera position = %s." % str(self.global_position) + ) + + if p_time == 0.0: + self.global_position = _target + else: + # Need to wait a frame in order to ensure the screen centre position is + # recalculated. Also to allow any close-calls with the tween to finish. + await get_tree().process_frame + + if _tween.is_running(): + escoria.logger.debug( + self, + "set_target tween is still active: %f seconds of %s completed." % [ + _tween.get_total_elapsed_time(), + _tween.get_duration() + ] + ) + _tween.stop() + + set_drag_margin_enabled(false, false) + + _convert_current_global_pos_for_disabled_drag_margin() + _target = _convert_pos_for_disabled_drag_margin(_target) + + _tween.interpolate_property( + self, + "global_position", + self.global_position, + _target, + p_time, + Tween.TRANS_LINEAR, + Tween.EASE_IN_OUT + ) + _tween.play() + +## Set the camera zoom level.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_zoom_level|`float`|Zoom level to set|yes|[br] +## |p_time|`float`|Number of seconds for the camera to reach the zoom level|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_camera_zoom(p_zoom_level: float, p_time: float): + if p_zoom_level <= 0.0: + escoria.logger.error( + self, + "Tried to set negative or zero zoom level." + ) + + _zoom_target = Vector2(1, 1) * p_zoom_level + + if p_time == 0: + self.zoom = _zoom_target + else: + # Need to wait a frame in order to ensure the screen centre position is + # recalculated. Also to allow any close-calls with the tween to finish. + await get_tree().process_frame + + if _tween.is_running(): + escoria.logger.debug( + self, + "set_camera_zoom tween is still active: %f seconds of %s completed." % [ + _tween.get_total_elapsed_time(), + _tween.get_duration() + ] + ) + _tween.stop() + + set_drag_margin_enabled(false, false) + + _convert_current_global_pos_for_disabled_drag_margin() + + _tween.interpolate_property( + self, + "zoom", + self.zoom, + _zoom_target, + p_time, + Tween.TRANS_LINEAR, + Tween.EASE_IN_OUT + ) + _tween.play() + +## Push the camera towards the target in terms of position and zoom level using a given transition type and time. See https://docs.godotengine.org/en/stable/classes/class_tween.html#enumerations[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_target|`Variant`|Target to push to|yes|[br] +## |p_time|`float`|Number of seconds for the transition to take|no|[br] +## |p_type|`int`|Tween transition type|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func push(p_target, p_time: float = 0.0, p_type: int = 0): + _resolve_target_and_zoom(p_target) + + var push_target = null + + if _follow_target != null: + push_target = p_target.position + else: + push_target = _target + + if p_time == 0: + self.global_position = push_target + + if _zoom_target != Vector2(): + self.zoom = _zoom_target + else: + # Need to wait a frame in order to ensure the screen centre position is + # recalculated. Also to allow any close-calls with the tween to finish. + await get_tree().process_frame + + if _tween.is_running(): + escoria.logger.debug( + self, + "camera push tween is still active: %f seconds of %f completed." % [ + _tween.tell(), + _tween.get_runtime() + ] + ) + _tween.stop() + + if _zoom_target != Vector2(): + _tween.interpolate_property( + self, + "zoom", + self.zoom, + _zoom_target, + p_time, + p_type, + Tween.EASE_IN_OUT + ) + + set_drag_margin_enabled(false, false) + + _convert_current_global_pos_for_disabled_drag_margin() + + _tween.interpolate_property( + self, + "global_position", + self.global_position, + push_target, + p_time, + p_type, + Tween.EASE_IN_OUT + ) + + _tween.play() + +## Shift the camera by the given vector in a given time and using a specific Tween transition type. See https://docs.godotengine.org/en/stable/classes/class_tween.html#enumerations[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_target|`Vector2`|Vector to shift the camera by|yes|[br] +## |p_time|`float`|Number of seconds for the transition to take|yes|[br] +## |p_type|`int`|Tween transition type|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func shift(p_target: Vector2, p_time: float, p_type: int): + _follow_target = null + + var new_pos = self.global_position + p_target + _target = new_pos + + if _tween.is_running(): + # Need to wait a frame in order to ensure the screen centre position is + # recalculated. Also to allow any close-calls with the tween to finish. + await get_tree().process_frame + + escoria.logger.debug( + self, + "camera shift tween is still active: %f seconds of %f completed." % [ + _tween.tell(), + _tween.get_runtime() + ] + ) + _tween.stop() + + set_drag_margin_enabled(false, false) + + _convert_current_global_pos_for_disabled_drag_margin() + + _tween.interpolate_property( + self, + "global_position", + self.global_position, + _target, + p_time, + p_type, + Tween.EASE_IN_OUT + ) + _tween.play() + +## Checks whether the given point is contained within the viewport's limits. Note that this is different from the camera's limits when using anchor mode DRAG_CENTER.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |point|`Vector2`|Point to be tested against viewport limits.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `bool` value. (`bool`) +func check_point_is_inside_viewport_limits(point: Vector2) -> bool: + var viewport_rect: Rect2 = get_viewport_rect() + var screen_half_size: Vector2 = viewport_rect.size * 0.5 + + var limits_to_test: Rect2 = Rect2( + limit_left + screen_half_size.x, + limit_top + screen_half_size.y, + limit_right - limit_left - viewport_rect.size.x + 1, + limit_bottom - limit_top - viewport_rect.size.y + 1 + ) + + return limits_to_test.has_point(point) + +## The inclusive minimum and maximum values for the x-component of the current valid viewport. Mainly used in any logging messages related to same. the current valid viewport.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the inclusive minimum and maximum values for the x-component of the current valid viewport. Mainly used in any logging messages related to same. the current valid viewport. (`Array`) +func get_current_valid_viewport_values_x() -> Array: + var viewport_rect: Rect2 = get_viewport_rect() + + return [limit_left + viewport_rect.size.x * 0.5, limit_right - viewport_rect.size.x * 0.5] + +## The inclusive minimum and maximum values for the y-component of the current valid viewport. Mainly used in any logging messages related to same. the current valid viewport.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the inclusive minimum and maximum values for the y-component of the current valid viewport. Mainly used in any logging messages related to same. the current valid viewport. (`Array`) +func get_current_valid_viewport_values_y() -> Array: + var viewport_rect: Rect2 = get_viewport_rect() + + return [limit_top + viewport_rect.size.y * 0.5, limit_bottom - viewport_rect.size.y * 0.5] + +## The camera's current limits as a Rect2. Mainly used in any logging messages related to same.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the camera's current limits as a Rect2. Mainly used in any logging messages related to same. (`Rect2`) +func get_camera_limit_rect() -> Rect2: + return Rect2(limit_left, limit_top, limit_right - limit_left, limit_bottom - limit_top) + +## Used when drag margins are enabled. Clamps the camera so it respects the viewport limits inside the camera limits.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func clamp_to_viewport_limits() -> void: + var viewport_rect: Rect2 = get_viewport_rect() + + var cur_camera_pos: Vector2 = self.get_screen_center_position() + var ret_position: Vector2 = cur_camera_pos + + if cur_camera_pos.x - viewport_rect.size.x * 0.5 * zoom.x <= limit_left: + ret_position.x = limit_left + viewport_rect.size.x * 0.5 * zoom.x * (1 + drag_left_margin) + elif cur_camera_pos.x + viewport_rect.size.x * 0.5 * zoom.x >= limit_right: + ret_position.x = limit_right - viewport_rect.size.x * 0.5 * zoom.x * (1 + drag_right_margin) + + if cur_camera_pos.y - viewport_rect.size.y * 0.5 * zoom.y <= limit_top: + ret_position.y = limit_top + viewport_rect.size.y * 0.5 * zoom.y * (1 + drag_top_margin) + elif cur_camera_pos.y + viewport_rect.size.y * 0.5 * zoom.y >= limit_bottom: + ret_position.y = limit_bottom - viewport_rect.size.y * 0.5 * zoom.y * (1 + drag_bottom_margin) + + self.global_position = ret_position + +## Called when the camera's target is reached. Stops and resets the tween, and re-enables drag margins.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _target_reached(): + _tween.stop() + _tween.reset() + set_drag_margin_enabled(true, true) + +## Compensates the camera's current global_position when disabling drag margins. This helps to ensure that when we disable or enable drag margins that the position on the screen is maintained without the camera "jumping". (See https://github.com/godotengine/godot/blob/3.5/scene/2d/camera_2d.cpp for more details.)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _convert_current_global_pos_for_disabled_drag_margin() -> void: + var cur_camera_pos: Vector2 = self.get_screen_center_position() + var ret_position: Vector2 = _convert_pos_for_disabled_drag_margin(cur_camera_pos) + + self.global_position = ret_position + +## Converts the given position set with drag margins enabled to the same position when calculated with drag margins disabled. This is helpful for preventing the camera from "jumping" when disabling drag margins, e.g. in order to perform some camera translations/tweening. when rendered with drag margins disabled.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |pos|`Vector2`|Position to be converted.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns a `Vector2` value. (`Vector2`) +func _convert_pos_for_disabled_drag_margin(pos: Vector2) -> Vector2: + var viewport_rect: Rect2 = get_viewport_rect() + var ret_position: Vector2 = pos + + # If the current calculated centre of the camera/viewport is close enough to + # the set camera limits (i.e. the centre is upto and including half the + # viewport's size to the limit being tested), then we make sure the + # global_position is at the same coordinates since Camera2D will recalculate + # that position to the exact same position (i.e. no funny math). + # Otherwise, we set the global_position to be the value that would allow + # Camera2D to convert it to the value of the current calculated centre. This + # compensates for the switch when disabling drag margins. + if ret_position.x - viewport_rect.size.x * 0.5 * zoom.x <= limit_left: + ret_position.x = limit_left + viewport_rect.size.x * 0.5 * zoom.x + elif ret_position.x + viewport_rect.size.x * 0.5 * zoom.x >= limit_right: + ret_position.x = limit_right - viewport_rect.size.x * 0.5 * zoom.x + + if ret_position.y - viewport_rect.size.y * 0.5 * zoom.y <= limit_top: + ret_position.y = limit_top + viewport_rect.size.y * 0.5 * zoom.y + elif ret_position.y + viewport_rect.size.y * 0.5 * zoom.y >= limit_bottom: + ret_position.y = limit_bottom - viewport_rect.size.y * 0.5 * zoom.y + + return ret_position + +## Resolve the correct position and zoom of the target object.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_target|`Variant`|The target to resolve|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _resolve_target_and_zoom(p_target) -> void: + _target = Vector2() + _zoom_target = Vector2() + _follow_target = null + + if p_target is Node and "is_movable" in p_target and p_target.is_movable: + _follow_target = p_target + + if p_target is Vector2: + _target = p_target + elif p_target is Array and p_target.size() > 0: + var target_pos = Vector2() + + for obj in p_target: + target_pos += obj.get_camera_pos() + + _target = target_pos / p_target.size() + elif p_target.has_method("get_camera_node"): + if "global_position" in p_target.get_camera_node(): + _target = p_target.get_camera_node().global_position + if "zoom" in p_target.get_camera_node(): + _zoom_target = p_target.get_camera_node().zoom + else: + _target = p_target.global_position diff --git a/addons/escoria-core/game/scenes/camera_player/esc_camera.gd.uid b/addons/escoria-core/game/scenes/camera_player/esc_camera.gd.uid new file mode 100644 index 0000000..8c1b7c1 --- /dev/null +++ b/addons/escoria-core/game/scenes/camera_player/esc_camera.gd.uid @@ -0,0 +1 @@ +uid://b8xyisawuhtw3 diff --git a/addons/escoria-core/game/scenes/camera_player/esc_camera_limits.gd b/addons/escoria-core/game/scenes/camera_player/esc_camera_limits.gd new file mode 100644 index 0000000..316e392 --- /dev/null +++ b/addons/escoria-core/game/scenes/camera_player/esc_camera_limits.gd @@ -0,0 +1,40 @@ +## Describes a bounding box that limits the camera movement in the scene. +extends RefCounted +class_name ESCCameraLimits + +## The left side of the bounding box. +var limit_left: int = -10000 + +## The right side of the bounding box. +var limit_right: int = 10000 + +## The top side of the bounding box. +var limit_top: int = -10000 + +## The bottom side of the bounding box. +var limit_bottom: int = 10000 + +## Initializes the camera limits with the given bounding box values.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |left|`int`|The left side of the bounding box|yes|[br] +## |right|`int`|The right side of the bounding box|yes|[br] +## |top|`int`|The top side of the bounding box|yes|[br] +## |bottom|`int`|The bottom side of the bounding box|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init( + left: int, + right: int, + top: int, + bottom: int +): + limit_left = left + limit_right = right + limit_top = top + limit_bottom = bottom diff --git a/addons/escoria-core/game/scenes/camera_player/esc_camera_limits.gd.uid b/addons/escoria-core/game/scenes/camera_player/esc_camera_limits.gd.uid new file mode 100644 index 0000000..0319263 --- /dev/null +++ b/addons/escoria-core/game/scenes/camera_player/esc_camera_limits.gd.uid @@ -0,0 +1 @@ +uid://dwcagpvytibk5 diff --git a/addons/escoria-core/game/scenes/dialogs/esc_dialog_manager.gd b/addons/escoria-core/game/scenes/dialogs/esc_dialog_manager.gd new file mode 100644 index 0000000..0a00456 --- /dev/null +++ b/addons/escoria-core/game/scenes/dialogs/esc_dialog_manager.gd @@ -0,0 +1,164 @@ +## A base class for dialog plugins to work with Escoria +## @MANAGER +extends Control +class_name ESCDialogManager + +## Emitted when the say function has completed showing the text[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal say_finished + +## Emitted when text has just become fully visible[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal say_visible + +## Emitted when the player has chosen an option[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |option|`Variant`|Dialog option chosen by the player.|yes|[br] +## [br] +signal option_chosen(option) + +## Checks whether a specific type is supported by the dialog plugin.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |type|`String`|Required type.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns whether the type is supported or not. (`bool`) +func has_type(type: String) -> bool: + return false + +## Checks whether a specific chooser type is supported by the dialog plugin.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |type|`String`|Required chooser type.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns whether the type is supported or not. (`bool`) +func has_chooser_type(type: String) -> bool: + return false + +## Outputs a text said by the item specified by the global id and emits `say_finished` after finishing displaying the text.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |dialog_player|`Node`|Node of the dialog player in the UI.|yes|[br] +## |global_id|`String`|Global id of the item that is speaking.|yes|[br] +## |text|`String`|Text to say, optional prefixed by a translation key separated by a ":".|yes|[br] +## |type|`String`|Type of dialog box to use.|yes|[br] +## |key|`String`|Translation key.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func say(dialog_player: Node, global_id: String, text: String, type: String, key: String): + pass + +## Instructs the dialog manager to preserve the next dialog box used by a `say` command until a call to `disable_preserve_dialog_box` is made. This method should be idempotent, i.e. if called after the first time and prior to `disable_preserve_dialog_box` being called, the result should be the same.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func enable_preserve_dialog_box() -> void: + pass + +## Instructs the dialog manager to no longer preserve the currently-preserved dialog box or to not preserve the next dialog box used by a `say` command (this is the default state). This method should be idempotent, i.e. if called after the first time and prior to `enable_preserve_dialog_box` being called, the result should be the same.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func disable_preserve_dialog_box() -> void: + pass + +## Presents an option chooser to the player and sends the signal `option_chosen` with the chosen dialog option.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |dialog_player|`Node`|Node of the dialog player in the UI.|yes|[br] +## |dialog|`ESCDialog`|Information about the dialog to display.|yes|[br] +## |type|`String`|The dialog chooser type to use.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func choose(dialog_player: Node, dialog: ESCDialog, type: String): + pass + +## Triggers running the dialogue faster.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func speedup(): + pass + +## Triggers an instant finish of the current dialog.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func finish(): + pass + +## The say command has been interrupted, cancel the dialog display.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt(): + pass + +## To be called if voice audio has finished.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func voice_audio_finished(): + pass diff --git a/addons/escoria-core/game/scenes/dialogs/esc_dialog_manager.gd.uid b/addons/escoria-core/game/scenes/dialogs/esc_dialog_manager.gd.uid new file mode 100644 index 0000000..25eac9e --- /dev/null +++ b/addons/escoria-core/game/scenes/dialogs/esc_dialog_manager.gd.uid @@ -0,0 +1 @@ +uid://xalkeght0hmj diff --git a/addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd b/addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd new file mode 100644 index 0000000..44e2a3f --- /dev/null +++ b/addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd @@ -0,0 +1,60 @@ +## Base class for all dialog options implementations +extends Control +class_name ESCDialogOptionsChooser + +## Emitted when an option is chosen.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |option|`Variant`|The dialog option that was chosen.|yes|[br] +## [br] +signal option_chosen(option) + +## The dialog to show +var dialog: ESCDialog + +## Sets the dialog used for the chooser.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |new_dialog|`ESCDialog`|Dialog to set.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_dialog(new_dialog: ESCDialog) -> void: + self.dialog = new_dialog + +## Shows the dialog chooser UI.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func show_chooser() -> void: + escoria.logger.error( + self, + "Dialog chooser does not implement the show method." + ) + +## Hides the dialog chooser UI.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func hide_chooser() -> void: + escoria.logger.error( + self, + "Dialog chooser does not implement the hide method." + ) diff --git a/addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd.uid b/addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd.uid new file mode 100644 index 0000000..9dbe944 --- /dev/null +++ b/addons/escoria-core/game/scenes/dialogs/esc_dialog_options_chooser.gd.uid @@ -0,0 +1 @@ +uid://c8rd32hdq72l2 diff --git a/addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd b/addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd new file mode 100644 index 0000000..70e61e2 --- /dev/null +++ b/addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd @@ -0,0 +1,256 @@ +## Escoria dialog player +extends Control +class_name ESCDialogPlayer + +## Emitted when an answer is chosen.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |option|`Variant`|The dialog option that was chosen.|yes|[br] +## [br] +signal option_chosen(option) + +## Emitted when a say command finished.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal say_finished + + +## Used when specifying dialog types in various methods. +const DIALOG_TYPE_SAY = "say" + +## Used when specifying dialog types in various methods. +const DIALOG_TYPE_CHOOSE = "choose" + +## Reference to the currently playing "say" dialog manager. +var _say_dialog_manager: ESCDialogManager = null + +## Reference to the currently playing "choose" dialog manager. +var _choose_dialog_manager: ESCDialogManager = null + +## Whether to use the "dialog box preservation" feature. +var _block_say_enabled: bool = false + +## Registers the dialog player and loads the dialog resources.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + if Engine.is_editor_hint(): + return + + escoria.dialog_player = self + +## Instructs the dialog manager to preserve the next dialog box used by a `say` command until a call to `disable_preserve_dialog_box` is made. This method should be idempotent, i.e. if called after the first time and prior to `disable_preserve_dialog_box` being called, the result should be the same.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func enable_preserve_dialog_box() -> void: + _block_say_enabled = true + +## Instructs the dialog manager to no longer preserve the currently-preserved dialog box or to not preserve the next dialog box used by a `say` command (this is the default state). This method should be idempotent, i.e. if called after the first time and prior to `enable_preserve_dialog_box` being called, the result should be the same.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func disable_preserve_dialog_box() -> void: + _block_say_enabled = false + _say_dialog_manager.disable_preserve_dialog_box() + +## Makes a character say some text.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |character|`String`|Character that is talking.|yes|[br] +## |type|`String`|UI to use for the dialog.|yes|[br] +## |text|`String`|Text to say.|yes|[br] +## |key|`String`|Translation key.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func say(character: String, type: String, text: String, key: String) -> void: + if type == "": + type = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DEFAULT_DIALOG_TYPE + ) + + # We only need to remove the dialog manager from the scene tree if the dialog manager type + # has changed since the last use of this method. + _update_dialog_manager(DIALOG_TYPE_SAY, _say_dialog_manager, type) + + if _block_say_enabled: + _say_dialog_manager.enable_preserve_dialog_box() + + _say_dialog_manager.say(self, character, text, type, key) + + +## Displays a list of choices.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |dialog|`ESCDialog`|The dialog to start.|yes|[br] +## |type|`String`|The dialog chooser type to use (default: "simple").|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func start_dialog_choices(dialog: ESCDialog, type: String = "simple"): + # We only need to remove the dialog manager from the scene tree if the dialog manager type + # has changed since the last use of this method. + _update_dialog_manager(DIALOG_TYPE_CHOOSE, _choose_dialog_manager, type) + + _choose_dialog_manager.choose(self, dialog, type) + + +## Interrupts the currently running dialog.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func interrupt() -> void: + if is_instance_valid(_say_dialog_manager): + _say_dialog_manager.interrupt() + + +## Loads the first dialog manager that supports the specified "say" type; otherwise, the engine throws an error and stops.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |type|`String`|The type the dialog manager should support, e.g. "floating".|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _determine_say_dialog_manager(type: String) -> void: + var dialog_manager: ESCDialogManager = null + + for _manager_class in ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DIALOG_MANAGERS + ): + if ResourceLoader.exists(_manager_class): + var _manager: ESCDialogManager = load(_manager_class).new() + if _manager.has_type(type): + dialog_manager = _manager + else: + dialog_manager = null + + if not is_instance_valid(dialog_manager): + escoria.logger.error( + self, + "No dialog manager called '%s' configured." % type + ) + + _say_dialog_manager = dialog_manager + + +## Loads the first dialog manager that supports the specified "choose" type; otherwise, the engine throws an error and stops.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |type|`String`|The type the dialog manager should support, e.g. "simple".|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _determine_choose_dialog_manager(type: String) -> void: + var dialog_manager: ESCDialogManager = null + + for _manager_class in ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DIALOG_MANAGERS + ): + if ResourceLoader.exists(_manager_class): + var _manager: ESCDialogManager = load(_manager_class).new() + if _manager.has_chooser_type(type): + dialog_manager = _manager + else: + dialog_manager = null + + if not is_instance_valid(dialog_manager): + escoria.logger.error( + self, + "No dialog manager called '%s' configured." % type + ) + + _choose_dialog_manager = dialog_manager + + +## If necessary, updates the dialog manager for the specified dialog type.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |dialog_type|`String`|The type of dialog that will be managed, e.g. "say" or "choose".|yes|[br] +## |current_dialog_manager|`ESCDialogManager`|The dialog manager currently being used (if any) for the specified dialog type.|yes|[br] +## |dialog_manager_type|`String`|Type name of the dialog manager implementation to instantiate.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _update_dialog_manager(dialog_type: String, current_dialog_manager: ESCDialogManager, \ + dialog_manager_type: String) -> void: + + if is_instance_valid(current_dialog_manager): + if not current_dialog_manager.has_type(dialog_manager_type): + if is_ancestor_of(current_dialog_manager): + remove_child(current_dialog_manager) + + add_child(_determine_dialog_manager(dialog_type, dialog_manager_type)) + else: + add_child(_determine_dialog_manager(dialog_type, dialog_manager_type)) + + +## Sets the requested dialog manager type for the specified dialog function.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |dialog_type|`String`|The type of dialog that will be managed, e.g. "say" or "choose".|yes|[br] +## |dialog_manager_type|`String`|The dialog manager type specific to the dialog manager being requested.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the newly-resolved dialog manager. (`ESCDialogManager`) +func _determine_dialog_manager(dialog_type: String, dialog_manager_type: String) -> ESCDialogManager: + if dialog_type == DIALOG_TYPE_SAY: + _determine_say_dialog_manager(dialog_manager_type) + return _say_dialog_manager + elif dialog_type == DIALOG_TYPE_CHOOSE: + _determine_choose_dialog_manager(dialog_manager_type) + return _choose_dialog_manager + + # This line will never be hit as a failure above will result in an Escoria error + return null diff --git a/addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd.uid b/addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd.uid new file mode 100644 index 0000000..6a97923 --- /dev/null +++ b/addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd.uid @@ -0,0 +1 @@ +uid://dfl7khtlretr7 diff --git a/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd b/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd new file mode 100644 index 0000000..d538036 --- /dev/null +++ b/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd @@ -0,0 +1,184 @@ +## A debug window which can run esc commands +extends Window + +## Reference to the past actions display +@onready var past_actions = $VBoxContainer/past_actions + +## Reference to the command input +@onready var command = $VBoxContainer/command + +## ESC commands kept around for references to their command names. +var _print: PrintCommand + +## History of typed commands +var commands_history: PackedStringArray + +## The current index in the command history +var commands_history_current_id: int + +## The maximum number of commands to keep in history +const COMMANDS_HISTORY_LENGTH: int = 20 + +## Called when the node is added to the scene tree for the first time.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready() -> void: + _print = PrintCommand.new() + escoria.logger.connect("error_message_signal",_on_error_message) + +## Handles input events for command history navigation.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`InputEvent`|The input event to process.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _input(event: InputEvent): + if event.is_pressed() and event is InputEventKey: + if (event as InputEventKey).keycode == KEY_UP and not commands_history.is_empty(): + commands_history_current_id -= 1 + if commands_history_current_id < 0: + commands_history_current_id = 0 + command.text = commands_history[commands_history_current_id] + command.call_deferred("grab_focus") + if (event as InputEventKey).keycode == KEY_DOWN and not commands_history.is_empty(): + commands_history_current_id += 1 + if commands_history_current_id > commands_history.size() - 1: + commands_history_current_id = commands_history.size() - 1 + command.text = commands_history[commands_history_current_id] + command.call_deferred("grab_focus") + + +## Runs a command entered in the prompt.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_command_str|`String`|Command to execute.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_command_text_entered(p_command_str : String): + if p_command_str.is_empty(): + return + + command.text = "" + past_actions.text += "\n" + past_actions.text += "# " + p_command_str + past_actions.text += "\n" + + _historize_command(p_command_str) + + if p_command_str in ["history", "hist"]: + for ch in commands_history: + past_actions.text += ch + "\n" + return + + _historize_command(p_command_str) + + if p_command_str in ["history", "hist"]: + for ch in commands_history: + past_actions.text += ch + "\n" + return + + _historize_command(p_command_str) + + if p_command_str in ["history", "hist"]: + for ch in commands_history: + past_actions.text += ch + "\n" + return + + var errors = [] + escoria.logger.dont_assert = true + var script = escoria.esc_compiler.compile( + "%s%s" % [ESCEvent.PREFIX, _print.get_command_name(), + p_command_str + ], + get_class() + ) + + if script: + escoria.logger.dont_assert = true + escoria.event_manager.queue_event(script.events[escoria.event_manager.EVENT_PRINT]) + var ret = await escoria.event_manager.event_finished + while ret[1] != _print.get_command_name(): + ret = await escoria.event_manager.event_finished + past_actions.text += "Returned code: %d" % ret[0] + + past_actions.scroll_vertical = past_actions.get_line_count() + + past_actions.scroll_vertical = past_actions.get_line_count() + + past_actions.scroll_vertical = past_actions.get_line_count() + + +## Sets the focus to the command input field.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_esc_prompt_popup_about_to_show(): + command.call_deferred("grab_focus") + +## Handles error messages and displays them in the past actions display.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |message|`Variant`|The error message to display.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_error_message(message) -> void: + past_actions.text += message + "\n" + past_actions.scroll_vertical = past_actions.get_line_count() + + +## Adds a command to the history and manages the history size.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_command|`String`|The command to add to history.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _historize_command(p_command: String) -> void: + commands_history_current_id += 1 + commands_history.append(p_command) + if commands_history.size() + 1 > COMMANDS_HISTORY_LENGTH: + commands_history.remove_at(0) + commands_history_current_id = COMMANDS_HISTORY_LENGTH - 1 + + +## Handles the close request for the prompt popup.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_close_requested(): + escoria.main.get_node("layers/debug_layer/esc_prompt_popup").hide() diff --git a/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd.uid b/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd.uid new file mode 100644 index 0000000..b5f9d85 --- /dev/null +++ b/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd.uid @@ -0,0 +1 @@ +uid://d2ek8auf3siqt diff --git a/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.tscn b/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.tscn new file mode 100644 index 0000000..08c3239 --- /dev/null +++ b/addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=2 format=3 uid="uid://b0q36us3uuimq"] + +[ext_resource type="Script" uid="uid://d2ek8auf3siqt" path="res://addons/escoria-core/game/scenes/esc_prompt/esc_prompt_popup.gd" id="1"] + +[node name="esc_prompt_popup" type="Window"] +position = Vector2i(0, 36) +size = Vector2i(500, 300) +visible = false +script = ExtResource("1") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="past_actions" type="TextEdit" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +editable = false +wrap_mode = 1 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +layout_mode = 2 + +[node name="command" type="LineEdit" parent="VBoxContainer"] +layout_mode = 2 +caret_blink = true + +[connection signal="about_to_popup" from="." to="." method="_on_esc_prompt_popup_about_to_show"] +[connection signal="close_requested" from="." to="." method="_on_close_requested"] +[connection signal="text_submitted" from="VBoxContainer/command" to="." method="_on_command_text_entered"] diff --git a/addons/escoria-core/game/scenes/inventory/inventory_ui.gd b/addons/escoria-core/game/scenes/inventory/inventory_ui.gd new file mode 100644 index 0000000..082c250 --- /dev/null +++ b/addons/escoria-core/game/scenes/inventory/inventory_ui.gd @@ -0,0 +1,197 @@ +## Manages the inventory on the GUI connected to the inventory_ui_container variable. +extends Control +class_name ESCInventory + +## The actual container node to add items as children of. Should be a Container. +@export var inventory_ui_container: NodePath + +## A registry of inventory ESCInventoryItem nodes. +var items_ids_in_inventory: Dictionary = {} + +## Fill the items the player has from the start, do sanity checks and listen when a global has changed.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + if inventory_ui_container == null or inventory_ui_container.is_empty(): + escoria.logger.error( + self, + "Inventory items container is empty." + ) + return + + for item_id in escoria.inventory_manager.items_in_inventory(): + call_deferred("add_new_item_by_id", item_id) + + escoria.inventory = self + escoria.globals_manager.global_changed.connect(_on_escoria_global_changed) + +## Add item to Inventory UI using its id set in its scene.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_id|`String`|The id of the item to add|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func add_new_item_by_id(item_id: String) -> void: + if item_id.begins_with("i/"): + item_id = item_id.rsplit("i/", false)[0] + if not items_ids_in_inventory.has(item_id): + if not escoria.object_manager.has(item_id) or not is_instance_valid( \ + escoria.object_manager.get_object(item_id).node): + var inventory_file = "%s/%s.tscn" % [ + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.INVENTORY_ITEMS_PATH + ).trim_suffix("/"), + item_id + ] + if ResourceLoader.exists(inventory_file): + escoria.object_manager.register_object( + ESCObject.new( + item_id, + ResourceLoader.load(inventory_file).instantiate() + ), + null, + true + ) + else: + escoria.logger.error( + self, + ( + "Item global id '%s' is not registered because the item's scene file was not found.\n" + + "Attempted scene file path: %s.\n" + + "Please ensure that the '%s' project setting points at **your inventory items folder** (current is: \"%s\")." + ) + % [ + item_id, + inventory_file, + ESCProjectSettingsManager.INVENTORY_ITEMS_PATH, + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.INVENTORY_ITEMS_PATH + ) + ] + ) + + var inventory_item = escoria.di.esc_inventory_item( + escoria.object_manager.get_object(item_id).node + ) + var inventory_item_button = get_node( + inventory_ui_container + ).add_item(inventory_item) + + items_ids_in_inventory[item_id] = inventory_item + + if not escoria.object_manager.has(item_id): + escoria.object_manager.register_object( + ESCObject.new( + item_id, + inventory_item_button + ), + null, + true + ) + + escoria.inputs_manager.register_inventory_item(inventory_item_button) + +## Remove item from Inventory UI using its id set in its scene.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_id|`String`|The id of the item to remove|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func remove_item_by_id(item_id: String) -> void: + if items_ids_in_inventory.has(item_id): + var item_inventory = items_ids_in_inventory[item_id] + var item_inventory_button = get_node( + inventory_ui_container + ).get_inventory_button(item_inventory) + + if item_inventory_button.mouse_left_inventory_item.is_connected( + escoria.inputs_manager._on_mouse_left_click_inventory_item + ): + item_inventory_button.mouse_left_inventory_item.disconnect( + escoria.inputs_manager._on_mouse_left_click_inventory_item + ) + if item_inventory_button.mouse_double_left_inventory_item.is_connected( + escoria.inputs_manager._on_mouse_double_left_click_inventory_item + ): + item_inventory_button.mouse_double_left_inventory_item.disconnect( + escoria.inputs_manager._on_mouse_double_left_click_inventory_item + ) + if item_inventory_button.mouse_right_inventory_item.is_connected( + escoria.inputs_manager._on_mouse_right_click_inventory_item + ): + item_inventory_button.mouse_right_inventory_item.disconnect( + escoria.inputs_manager._on_mouse_right_click_inventory_item + ) + if item_inventory_button.inventory_item_focused.is_connected( + escoria.inputs_manager._on_mouse_entered_inventory_item + ): + item_inventory_button.inventory_item_focused.disconnect( + escoria.inputs_manager._on_mouse_entered_inventory_item + ) + if item_inventory_button.inventory_item_unfocused.is_connected( + escoria.inputs_manager._on_mouse_exited_inventory_item + ): + item_inventory_button.inventory_item_unfocused.disconnect( + escoria.inputs_manager._on_mouse_exited_inventory_item + ) + + get_node(inventory_ui_container).remove_item(item_inventory) + items_ids_in_inventory.erase(item_id) + +## React to changes to inventory globals adding items or removing them.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |global|`String`|The global variable name|yes|[br] +## |old_value|`Variant`|The old value of the global|yes|[br] +## |new_value|`Variant`|The new value of the global|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_escoria_global_changed(global: String, old_value, new_value) -> void: + if !global.begins_with("i/"): + return + var item = global.rsplit("i/", false) + if item.size() == 1: + if new_value: + add_new_item_by_id(item[0]) + else: + remove_item_by_id(item[0]) + else: + escoria.logger.error( + self, + "Global must contain only one item name (received: %s)." % global + ) + +## Clear the inventory UI of all its items.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func clear() -> void: + var items_in_inventory_keys: Array = items_ids_in_inventory.keys() + for item_id in items_in_inventory_keys: + remove_item_by_id(item_id) diff --git a/addons/escoria-core/game/scenes/inventory/inventory_ui.gd.uid b/addons/escoria-core/game/scenes/inventory/inventory_ui.gd.uid new file mode 100644 index 0000000..0184ef8 --- /dev/null +++ b/addons/escoria-core/game/scenes/inventory/inventory_ui.gd.uid @@ -0,0 +1 @@ +uid://b1l4nnky23hwo diff --git a/addons/escoria-core/game/scenes/sound/esc_ambient_player.gd b/addons/escoria-core/game/scenes/sound/esc_ambient_player.gd new file mode 100644 index 0000000..298e7db --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_ambient_player.gd @@ -0,0 +1,61 @@ +## Background ambient sound player +extends Control +class_name ESCAmbientPlayer + +## Global id of the background ambient sound player. +@export var global_id: String = "_ambient" + +## The state of the music player. "default" or "off" disable music. Any other +## state refers to a music stream that should be played. +var state: String = "default" + +## Reference to the audio player. +@onready var stream: AudioStreamPlayer = $AudioStreamPlayer + +## Sets the state of this player.[br] +## [br] +## #### Parameters[br] +## [br] +## - p_state: New state to use.[br] +## - from_seconds: Sets the starting playback position.[br] +## - p_force: Override the existing state even if the stream is still playing. +func set_state(p_state: String, from_seconds: float = 0.0, p_force: bool = false) -> void: + # If already playing this stream, keep playing, unless p_force + if p_state == state and not p_force and stream.is_playing(): + return + + state = p_state + + # If state is "off"/"default", turn off music + if state == "off" or state == "default": + stream.stream = null + return + + var resource = load(p_state) + + stream.stream = resource + + if stream.stream: + if resource is AudioStreamWAV: + resource.loop_mode = AudioStreamWAV.LOOP_FORWARD + resource.loop_end = resource.mix_rate * resource.get_length() + elif "loop" in resource: + resource.loop = true + stream.play(from_seconds) + + +## Registers this music player to the object registry. +func _ready(): + process_mode = Node.PROCESS_MODE_PAUSABLE + escoria.object_manager.register_object( + ESCObject.new(global_id, self), + null, + true + ) + + +## Returns the playback position of the audio stream in seconds.[br] +## [br] +## *Returns* the playback position as a float value. +func get_playback_position() -> float: + return $AudioStreamPlayer.get_playback_position() diff --git a/addons/escoria-core/game/scenes/sound/esc_ambient_player.gd.uid b/addons/escoria-core/game/scenes/sound/esc_ambient_player.gd.uid new file mode 100644 index 0000000..7a20f5b --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_ambient_player.gd.uid @@ -0,0 +1 @@ +uid://b6kjfij1s1lpb diff --git a/addons/escoria-core/game/scenes/sound/esc_ambient_player.tscn b/addons/escoria-core/game/scenes/sound/esc_ambient_player.tscn new file mode 100644 index 0000000..ca4f039 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_ambient_player.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=2 format=3 uid="uid://wsdpiju6bxqd"] + +[ext_resource type="Script" uid="uid://b6kjfij1s1lpb" path="res://addons/escoria-core/game/scenes/sound/esc_ambient_player.gd" id="1_ih1im"] + +[node name="bg_ambient" type="Control"] +process_mode = 3 +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1680.0 +offset_bottom = -1050.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_ih1im") + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +bus = &"Ambient" diff --git a/addons/escoria-core/game/scenes/sound/esc_music_player.gd b/addons/escoria-core/game/scenes/sound/esc_music_player.gd new file mode 100644 index 0000000..b8f6c45 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_music_player.gd @@ -0,0 +1,81 @@ +## Background music player +extends Control +class_name ESCMusicPlayer + +## Global id of the background music player. +@export var global_id: String = "_music" + +## The state of the music player. "default" or "off" disable music. Any other +## state refers to a music stream that should be played. +var state: String = "default" + +## Reference to the audio player. +@onready var stream: AudioStreamPlayer = $AudioStreamPlayer + +## Sets the state of this player.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_state|`String`|New state to use.|yes|[br] +## |from_seconds|`float`|Sets the starting playback position.|no|[br] +## |p_force|`bool`|Override the existing state even if the stream is still playing.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_state(p_state: String, from_seconds: float = 0.0, p_force: bool = false) -> void: + # If already playing this stream, keep playing, unless p_force + if p_state == state and not p_force and stream.is_playing(): + return + + state = p_state + + # If state is "off"/"default", turn off music + if state == "off" or state == "default": + stream.stream = null + return + + var resource = load(p_state) + + stream.stream = resource + + if stream.stream: + if resource is AudioStreamWAV: + resource.loop_mode = AudioStreamWAV.LOOP_FORWARD + resource.loop_end = resource.mix_rate * resource.get_length() + elif "loop" in resource: + resource.loop = true + stream.play(from_seconds) + + +## Registers this music player to the object registry.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + process_mode = Node.PROCESS_MODE_PAUSABLE + escoria.object_manager.register_object( + ESCObject.new(global_id, self), + null, + true + ) + + +## The playback position of the audio stream in seconds.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the playback position of the audio stream in seconds. the playback position as a float value. (`float`) +func get_playback_position() -> float: + return $AudioStreamPlayer.get_playback_position() diff --git a/addons/escoria-core/game/scenes/sound/esc_music_player.gd.uid b/addons/escoria-core/game/scenes/sound/esc_music_player.gd.uid new file mode 100644 index 0000000..50d1f36 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_music_player.gd.uid @@ -0,0 +1 @@ +uid://ch8x1lue4qy34 diff --git a/addons/escoria-core/game/scenes/sound/esc_music_player.tscn b/addons/escoria-core/game/scenes/sound/esc_music_player.tscn new file mode 100644 index 0000000..5e71381 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_music_player.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=2 format=3 uid="uid://c1txn45mpksqd"] + +[ext_resource type="Script" uid="uid://ch8x1lue4qy34" path="res://addons/escoria-core/game/scenes/sound/esc_music_player.gd" id="1"] + +[node name="bg_music" type="Control"] +process_mode = 3 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1680.0 +offset_bottom = -1050.0 +mouse_filter = 2 +script = ExtResource("1") + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +bus = "Music" diff --git a/addons/escoria-core/game/scenes/sound/esc_sound_player.gd b/addons/escoria-core/game/scenes/sound/esc_sound_player.gd new file mode 100644 index 0000000..2a9fa64 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_sound_player.gd @@ -0,0 +1,93 @@ +## Background sound player +extends Control +class_name ESCSoundPlayer + +## Global id of the sfx sound player. +@export var global_id: String = "_sound" + +## The state of the sound player. "default" or "off" disable sound. Any other +## state refers to a sound stream that should be played. +var state: String = "default" + +## Reference to the audio player. +@onready var stream: AudioStreamPlayer = $AudioStreamPlayer + +## Sets the state of this player.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_state|`String`|New state to use.|yes|[br] +## |from_seconds|`float`|Sets the starting playback position.|no|[br] +## |p_force|`bool`|Override the existing state even if the stream is still playing.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_state(p_state: String, from_seconds: float = 0.0, p_force: bool = false): + # If already playing this stream, keep playing, unless p_force + if p_state == state and not p_force and stream.is_playing(): + return + + state = p_state + + # If state is "off"/"default", turn off music + if state == "off" or state == "default": + stream.stream = null + return + + var resource = load(p_state) + + stream.stream = resource + + if stream.stream: + if resource is AudioStreamWAV: + resource.loop_mode = AudioStreamWAV.LOOP_DISABLED + elif "loop" in resource: + resource.loop = false + stream.play(from_seconds) + + +## Registers this sound player to the object registry.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + process_mode = Node.PROCESS_MODE_PAUSABLE + escoria.object_manager.register_object( + ESCObject.new(global_id, self), + null, + true + ) + + +## Sets state to default when finished playing.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_sound_finished(): + state = "default" + + +## The playback position of the audio stream in seconds.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the playback position of the audio stream in seconds. The playback position in seconds. (`float`) +func get_playback_position() -> float: + return $AudioStreamPlayer.get_playback_position() diff --git a/addons/escoria-core/game/scenes/sound/esc_sound_player.gd.uid b/addons/escoria-core/game/scenes/sound/esc_sound_player.gd.uid new file mode 100644 index 0000000..2831142 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_sound_player.gd.uid @@ -0,0 +1 @@ +uid://b4fkhx0prl41e diff --git a/addons/escoria-core/game/scenes/sound/esc_sound_player.tscn b/addons/escoria-core/game/scenes/sound/esc_sound_player.tscn new file mode 100644 index 0000000..984e040 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_sound_player.tscn @@ -0,0 +1,20 @@ +[gd_scene load_steps=2 format=3 uid="uid://uwqpnwjmp6aq"] + +[ext_resource type="Script" uid="uid://b4fkhx0prl41e" path="res://addons/escoria-core/game/scenes/sound/esc_sound_player.gd" id="1"] + +[node name="bg_sound" type="Control"] +process_mode = 3 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1680.0 +offset_bottom = -1050.0 +mouse_filter = 2 +script = ExtResource("1") +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +bus = "SFX" + +[connection signal="finished" from="AudioStreamPlayer" to="." method="_on_sound_finished"] diff --git a/addons/escoria-core/game/scenes/sound/esc_speech_player.gd b/addons/escoria-core/game/scenes/sound/esc_speech_player.gd new file mode 100644 index 0000000..31fc668 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_speech_player.gd @@ -0,0 +1,111 @@ +## Speech player +extends Control +class_name ESCSpeechPlayer + +## Global id of the speech player. +@export var global_id: String = "_speech" + +## Reference to the audio player. +@onready var stream: AudioStreamPlayer = $AudioStreamPlayer + +## Sets the state of this player.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_state|`String`|New state to use.|yes|[br] +## |from_seconds|`float`|Sets the starting playback position.|no|[br] +## |p_force|`bool`|Override the existing state even if the stream is still playing.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_state(p_state: String, from_seconds: float = 0.0, p_force: bool = false) -> void: + # If speech is disabled, return + if not ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_ENABLED + ): + return + + # If state is "off"/"default", turn off speech + if p_state in ["off", "default"]: + stream.stream = null + return + + var resource = load(p_state) + stream.stream = resource + + if stream.stream: + stream.stream.set_loop(false) + $AudioStreamPlayer.play(from_seconds) + + +## Registers this speech player to the object registry.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + process_mode = Node.PROCESS_MODE_PAUSABLE + escoria.object_manager.register_object( + ESCObject.new(global_id, self), + null, + true + ) + + +## Callback called when the audio stream player finished playing.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_AudioStreamPlayer_finished() -> void: + set_state("off") + + +## Pauses the speech player.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func pause(): + stream.stream_paused = true + + +## Unpauses the speech player.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func resume(): + stream.stream_paused = false + + +## The playback position of the audio stream in seconds.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the playback position of the audio stream in seconds. The playback position in seconds. (`float`) +func get_playback_position() -> float: + return $AudioStreamPlayer.get_playback_position() diff --git a/addons/escoria-core/game/scenes/sound/esc_speech_player.gd.uid b/addons/escoria-core/game/scenes/sound/esc_speech_player.gd.uid new file mode 100644 index 0000000..63a6af9 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_speech_player.gd.uid @@ -0,0 +1 @@ +uid://6aijk4hl0t0o diff --git a/addons/escoria-core/game/scenes/sound/esc_speech_player.tscn b/addons/escoria-core/game/scenes/sound/esc_speech_player.tscn new file mode 100644 index 0000000..2142df8 --- /dev/null +++ b/addons/escoria-core/game/scenes/sound/esc_speech_player.tscn @@ -0,0 +1,18 @@ +[gd_scene load_steps=2 format=3 uid="uid://c8ecyitwga1dx"] + +[ext_resource type="Script" uid="uid://6aijk4hl0t0o" path="res://addons/escoria-core/game/scenes/sound/esc_speech_player.gd" id="1"] + +[node name="Control" type="Control"] +process_mode = 3 +anchor_right = 1.0 +anchor_bottom = 1.0 +mouse_filter = 2 +script = ExtResource("1") +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +bus = "Speech" + +[connection signal="finished" from="AudioStreamPlayer" to="." method="_on_AudioStreamPlayer_finished"] diff --git a/addons/escoria-core/game/scenes/transitions/esc_transition_player.gd b/addons/escoria-core/game/scenes/transitions/esc_transition_player.gd new file mode 100644 index 0000000..40b0b61 --- /dev/null +++ b/addons/escoria-core/game/scenes/transitions/esc_transition_player.gd @@ -0,0 +1,195 @@ +## A transition player for scene changes +extends ColorRect +class_name ESCTransitionPlayer + +## Emitted when the transition was played[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |transition_id|`Variant`|Identifier of the transition that completed.|yes|[br] +## [br] +signal transition_done(transition_id) + +## The valid transition modes +enum TRANSITION_MODE { + IN, + OUT +} + +## Id to represent instant/no transitions +const TRANSITION_ID_INSTANT = -1 + +## Instant transition type +const TRANSITION_INSTANT = "instant" + +## Id of the transition. Allows keeping track of the actual transition +## being played or finished +var transition_id: int = 0 + +## The tween instance to animate +var _tween: Tween3 + +## If the current tween was canceled +var _was_canceled: bool = false + +## Fade in when the scene is starting[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready() -> void: + anchor_left = 0 + anchor_top = 0 + anchor_right = 1 + anchor_bottom = 1 + color = Color.WHITE + color.a = 0 + mouse_filter = MOUSE_FILTER_IGNORE + _tween = Tween3.new(self) + +## Play a transition animation[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |transition_name|`String`|Name of the transition to play (if empty string, uses the default transition).|no|[br] +## |mode|`int`|Mode to transition (in/out).|no|[br] +## |duration|`float`|The duration the transition should take.|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the transition id. (`int`) +func transition( + transition_name: String = "", + mode: int = TRANSITION_MODE.IN, + duration: float = 1.0 +) -> int: + + # We put this here instead of the constructor since if we have it in the + # constructor, the transition will ALWAYS happen on game start, which might + # not be desired if 'false' is used for automatic_transitions in a + # change_scene call in :init. + if not _tween.finished.is_connected(_on_tween_completed): + _tween.finished.connect(_on_tween_completed) + + if transition_name.is_empty(): + transition_name = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.DEFAULT_TRANSITION + ) + + if not has_transition(transition_name): + escoria.logger.error( + self, + "transition: Transition %s not found" % transition_name + ) + + # If this is an "instant" transition, we need to set the alpha of the base + # ColorRect to 0, since the transition materials used have a final state + # that sets this scene's root (ColorRect) alpha to 0. + if transition_name == TRANSITION_INSTANT: + color.a = 0 + return TRANSITION_ID_INSTANT + + var material_path = get_transition(transition_name) + + material = ResourceLoader.load(get_transition(transition_name)) + transition_id += 1 + + var start = 0.0 + var end = 1.0 + + if mode == TRANSITION_MODE.OUT: + start = 1.0 + end = 0.0 + + if _tween.is_running(): + _was_canceled = true + _tween.stop() + _tween.reset() + transition_done.emit(transition_id-1) + + _tween.interpolate_property( + $".", + "material:shader_parameter/cutoff", + start, + end, + duration + ) + _was_canceled = false + _tween.play() + return transition_id + + +## The full path for a transition shader based on its name[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Transition name whose material path should be resolved.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the full path for a transition shader based on its name the full path to the shader or an empty string if it can't be found. (`String`) +func get_transition(name: String) -> String: + for directory in ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TRANSITION_PATHS + ): + if ResourceLoader.exists(directory.path_join("%s.material" % name)): + return directory.path_join("%s.material" % name) + return "" + + +## True whether the transition scene has a transition corresponding to name provided.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Transition name to check for availability.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns true whether the transition scene has a transition corresponding to name provided. true if a transition exists with given name. (`bool`) +func has_transition(name: String) -> bool: + return name == TRANSITION_INSTANT or get_transition(name) != "" + + +## Resets the current material's cutoff parameter instantly.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func reset_shader_cutoff() -> void: + if not is_instance_valid(material): + return + + material.set_shader_parameter("cutoff", 1.0) + + +## Called when the tween completes the transition.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_tween_completed(): + if not _was_canceled: + _tween.stop() + _tween.reset() + escoria.logger.debug(self, "Transition %s done." % str(transition_id)) + transition_done.emit(transition_id) diff --git a/addons/escoria-core/game/scenes/transitions/esc_transition_player.gd.uid b/addons/escoria-core/game/scenes/transitions/esc_transition_player.gd.uid new file mode 100644 index 0000000..435545c --- /dev/null +++ b/addons/escoria-core/game/scenes/transitions/esc_transition_player.gd.uid @@ -0,0 +1 @@ +uid://bo0xyume0yqcc diff --git a/addons/escoria-core/game/scenes/transitions/masks/curtain.png b/addons/escoria-core/game/scenes/transitions/masks/curtain.png new file mode 100644 index 0000000..befa82a Binary files /dev/null and b/addons/escoria-core/game/scenes/transitions/masks/curtain.png differ diff --git a/addons/escoria-core/game/scenes/transitions/masks/curtain.png.import b/addons/escoria-core/game/scenes/transitions/masks/curtain.png.import new file mode 100644 index 0000000..4766f6d --- /dev/null +++ b/addons/escoria-core/game/scenes/transitions/masks/curtain.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://djebg0o8s418r" +path="res://.godot/imported/curtain.png-fd4a78beb232b0a99b1a008eb4a104fd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/game/scenes/transitions/masks/curtain.png" +dest_files=["res://.godot/imported/curtain.png-fd4a78beb232b0a99b1a008eb4a104fd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/game/scenes/transitions/masks/from_center.png b/addons/escoria-core/game/scenes/transitions/masks/from_center.png new file mode 100644 index 0000000..1c28b7a Binary files /dev/null and b/addons/escoria-core/game/scenes/transitions/masks/from_center.png differ diff --git a/addons/escoria-core/game/scenes/transitions/masks/from_center.png.import b/addons/escoria-core/game/scenes/transitions/masks/from_center.png.import new file mode 100644 index 0000000..9d28061 --- /dev/null +++ b/addons/escoria-core/game/scenes/transitions/masks/from_center.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bvkmesi350a3d" +path="res://.godot/imported/from_center.png-5a7489c4008ce50848f1d5c7238d9315.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/game/scenes/transitions/masks/from_center.png" +dest_files=["res://.godot/imported/from_center.png-5a7489c4008ce50848f1d5c7238d9315.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/game/scenes/transitions/masks/shards.png b/addons/escoria-core/game/scenes/transitions/masks/shards.png new file mode 100644 index 0000000..1d62b47 Binary files /dev/null and b/addons/escoria-core/game/scenes/transitions/masks/shards.png differ diff --git a/addons/escoria-core/game/scenes/transitions/masks/shards.png.import b/addons/escoria-core/game/scenes/transitions/masks/shards.png.import new file mode 100644 index 0000000..180b89a --- /dev/null +++ b/addons/escoria-core/game/scenes/transitions/masks/shards.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dpfnq2e14vwbg" +path="res://.godot/imported/shards.png-a75d64f3ee4c4b3daca9792e71d7f91f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/game/scenes/transitions/masks/shards.png" +dest_files=["res://.godot/imported/shards.png-a75d64f3ee4c4b3daca9792e71d7f91f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/game/scenes/transitions/shaders/curtain.material b/addons/escoria-core/game/scenes/transitions/shaders/curtain.material new file mode 100644 index 0000000..7ad0fab Binary files /dev/null and b/addons/escoria-core/game/scenes/transitions/shaders/curtain.material differ diff --git a/addons/escoria-core/game/scenes/transitions/shaders/fade_black.material b/addons/escoria-core/game/scenes/transitions/shaders/fade_black.material new file mode 100644 index 0000000..d0a7528 Binary files /dev/null and b/addons/escoria-core/game/scenes/transitions/shaders/fade_black.material differ diff --git a/addons/escoria-core/game/scenes/transitions/shaders/fade_white.material b/addons/escoria-core/game/scenes/transitions/shaders/fade_white.material new file mode 100644 index 0000000..8cc21c9 Binary files /dev/null and b/addons/escoria-core/game/scenes/transitions/shaders/fade_white.material differ diff --git a/addons/escoria-core/game/scenes/transitions/shaders/from_center.material b/addons/escoria-core/game/scenes/transitions/shaders/from_center.material new file mode 100644 index 0000000..acf5a47 Binary files /dev/null and b/addons/escoria-core/game/scenes/transitions/shaders/from_center.material differ diff --git a/addons/escoria-core/game/scenes/transitions/shaders/shards.material b/addons/escoria-core/game/scenes/transitions/shaders/shards.material new file mode 100644 index 0000000..f9027b1 Binary files /dev/null and b/addons/escoria-core/game/scenes/transitions/shaders/shards.material differ diff --git a/addons/escoria-core/game/scenes/transitions/transition.tscn b/addons/escoria-core/game/scenes/transitions/transition.tscn new file mode 100644 index 0000000..84c8dbb --- /dev/null +++ b/addons/escoria-core/game/scenes/transitions/transition.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=3 format=3 uid="uid://dep0fi7t7av8"] + +[ext_resource type="Script" uid="uid://bo0xyume0yqcc" path="res://addons/escoria-core/game/scenes/transitions/esc_transition_player.gd" id="1"] +[ext_resource type="Material" uid="uid://bwr4fkt0w426g" path="res://addons/escoria-core/game/scenes/transitions/shaders/curtain.material" id="2"] + +[node name="scene_transition" type="ColorRect"] +material = ExtResource("2") +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +mouse_filter = 2 +script = ExtResource("1") diff --git a/addons/escoria-core/game/tween3.gd b/addons/escoria-core/game/tween3.gd new file mode 100644 index 0000000..88a3e8a --- /dev/null +++ b/addons/escoria-core/game/tween3.gd @@ -0,0 +1,264 @@ +## In Godot 4, tweens cannot be resumed. But if you re-create them,[br] +## the bindings are lost. So this class wraps a tween and offers a reset function. +extends RefCounted +class_name Tween3 + +## Interpolates a property on an object using a tween.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |tween|`Tween`|The Tween instance to use.|yes|[br] +## |object|`Object`|The object whose property will be tweened.|yes|[br] +## |property|`NodePath`|The property to tween.|yes|[br] +## |initial_val|`Variant`|The initial value of the property.|yes|[br] +## |final_val|`Variant`|The final value of the property.|yes|[br] +## |duration|`float`|The duration of the tween in seconds.|yes|[br] +## |trans_type|`Tween.TransitionType`|The transition type (default 0).|no|[br] +## |ease_type|`Tween.EaseType`|The ease type (default 2).|no|[br] +## |delay|`float`|The delay before starting the tween (default 0).|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the interpolation was set up successfully. (`bool`) +static func tween_interpolate_property( + tween: Tween, + object: Object, property: NodePath, + initial_val: Variant, final_val: Variant, + duration: float, + trans_type: Tween.TransitionType = 0, ease_type: Tween.EaseType = 2, + delay: float = 0) -> bool: + var t := tween.tween_property(object, property, final_val, duration) + assert(t != null, "Cannot interpolate property \"%s\" in \"%s\"! Did you reset the tween?" % [property, str(object)]) + + if initial_val == null: + t.as_relative() + else: + t.from(initial_val) + + t.set_trans(trans_type) + t.set_ease(ease_type) + t.set_delay(delay) + + return true + + +## The parent node for the tween. +var _tween_parent: Node + +## The Tween instance being managed. +var _tween: Tween + +## The duration of the tween. +var _duration: float + +## Emitted when the tween finishes.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal finished() + +## Called when the tween finishes.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_finished(): + finished.emit() + +## Constructor.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |tween_parent|`Node`|The parent node for the tween.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(tween_parent: Node): + _tween_parent = tween_parent + _duration = 0 + + _create_tween() + +## Creates a new tween instance and connects its finished signal.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _create_tween(): + _tween = _tween_parent.get_tree().create_tween() + _tween.pause() # prevent autoplay + + _tween.finished.connect(_on_finished) + +## Resets the tween, stopping and killing the current tween and creating a new one.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func reset(): + _duration = 0 + _tween.stop() + _tween.kill() + + _create_tween() + +## Adds a method tween to the tween sequence.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |method|`Callable`|The method to tween.|yes|[br] +## |from|`Variant`|The initial value.|yes|[br] +## |to|`Variant`|The final value.|yes|[br] +## |duration|`float`|The duration of the tween in seconds.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func tween_method(method: Callable, from: Variant, to: Variant, duration: float) -> void: + _duration = maxf(_duration, duration) + _tween.tween_method(method, from, to, duration) + +## Interpolates a property on an object using the managed tween.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |object|`Object`|The object whose property will be tweened.|yes|[br] +## |property|`NodePath`|The property to tween.|yes|[br] +## |initial_val|`Variant`|The initial value of the property.|yes|[br] +## |final_val|`Variant`|The final value of the property.|yes|[br] +## |duration|`float`|The duration of the tween in seconds.|yes|[br] +## |trans_type|`Tween.TransitionType`|The transition type (default 0).|no|[br] +## |ease_type|`Tween.EaseType`|The ease type (default 2).|no|[br] +## |delay|`float`|The delay before starting the tween (default 0).|no|[br] +## [br] +## #### Returns[br] +## [br] +## Returns True if the interpolation was set up successfully. (`bool`) +func interpolate_property( + object: Object, property: NodePath, + initial_val: Variant, final_val: Variant, + duration: float, + trans_type: Tween.TransitionType = 0, ease_type: Tween.EaseType = 2, + delay: float = 0) -> bool: + _duration = maxf(_duration, duration) + return tween_interpolate_property(_tween, object, property, initial_val, final_val, duration, trans_type, ease_type, delay) + +## Plays the tween.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func play(): + _tween.play() + +## Resumes the tween if it is valid.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func resume(): + if _tween.is_valid(): + _tween.play() + +## Pauses the tween.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func pause(): + _tween.pause() + +## Stops the tween.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func stop(): + _tween.stop() + +## True if the tween is running.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns true if the tween is running. True if the tween is running. (`bool`) +func is_running(): + return _tween.is_running() + +## True if the tween is valid.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns true if the tween is valid. True if the tween is valid. (`bool`) +func is_valid(): + return _tween.is_valid() + +## Gets the total elapsed time of the tween.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the total elapsed time in seconds. (`Variant`) +func get_total_elapsed_time(): + return _tween.get_total_elapsed_time() + +## Gets the duration of the tween.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns the duration of the tween in seconds. (`Variant`) +func get_duration(): + return _duration diff --git a/addons/escoria-core/game/tween3.gd.uid b/addons/escoria-core/game/tween3.gd.uid new file mode 100644 index 0000000..b3148ba --- /dev/null +++ b/addons/escoria-core/game/tween3.gd.uid @@ -0,0 +1 @@ +uid://d0ukj4mi1vnri diff --git a/addons/escoria-core/patterns/state_machine/state.gd b/addons/escoria-core/patterns/state_machine/state.gd new file mode 100644 index 0000000..cfa9e0f --- /dev/null +++ b/addons/escoria-core/patterns/state_machine/state.gd @@ -0,0 +1,81 @@ +extends Node +class_name State +## Base interface for all states. +## +## This class doesn't do anything in itself but forces us to pass the right +## arguments to the methods below and makes sure every State object had all of +## these methods. + +## Signal sent when the state just changed. Parameter is the new state value.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |next_state_name|`Variant`|Name of the state that should become active after this state finishes.|yes|[br] +## [br] +signal finished(next_state_name) + + +## Initialize the state. E.g. change the animation[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func enter(): + return + + +## Clean up the state. Reinitialize values like a timer.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func exit(): + return + + +## Manage an input event while this state is active.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |_event|`InputEvent`|InputEvent to process|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func handle_input(_event: InputEvent): + return + + +## Perform an update while this state is active.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |_delta|`float`|float value obtained from a _process() call|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func update(_delta: float): + return + + +# Callback called when an animation is finished. +# [br] +# #### Parameters[br] +# - _anim_name: the animation name that just finished. +func _on_animation_finished(_anim_name: String): + return diff --git a/addons/escoria-core/patterns/state_machine/state.gd.uid b/addons/escoria-core/patterns/state_machine/state.gd.uid new file mode 100644 index 0000000..6ac0bde --- /dev/null +++ b/addons/escoria-core/patterns/state_machine/state.gd.uid @@ -0,0 +1 @@ +uid://c2huw3nfouhce diff --git a/addons/escoria-core/patterns/state_machine/state_machine.gd b/addons/escoria-core/patterns/state_machine/state_machine.gd new file mode 100644 index 0000000..2fa5d96 --- /dev/null +++ b/addons/escoria-core/patterns/state_machine/state_machine.gd @@ -0,0 +1,147 @@ +extends Node +class_name StateMachine +## Base interface for a generic state machine +## It handles initializing, setting the machine active or not +## delegating _physics_process, _input calls to the State nodes, +## and changing the current/active state. + +## Signal emitted when the state has changed. Parameter is the new current state.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |current_state|`Variant`|Instance of the state that became active.|yes|[br] +## [br] +signal state_changed(current_state) + +## Starting state. This node has to be set *before* the initialize(START_STATE) +## command is called. +var START_STATE: Node + +## List of states +var states_map = {} + +## Stack of states +var states_stack = [] # can also be used as a pushdown automaton + +## Value of the current state +var current_state = null + +## Name of the current state +var current_state_name = "" + +## Whether the state machine is currently enabled or not. +var _active = false: + set = set_active + + +## Initialize the state machine with the start_state parameter.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |start_state|`State`|State value to use as starting state for the state machine.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func initialize(start_state: State): + if START_STATE == null: + escoria.logger.error( + self, + "Starting state is required to be initialized with a defined state, + but it is null. Escoria cannot determine which of the defined states + (in states_map dictionary) is supposed to be the starting one. + Please assign a state to START_STATE in your implementation of the StateMachine class.") + for child in get_children(): + child.connect("finished", Callable(self, "_change_state")) + + set_active(true) + states_stack.push_front(start_state) + current_state = states_stack[0] + current_state.enter() + + +## Enable or disable the state machine.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |value|`bool`|if true, enables the state machine. If false, disables it.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func set_active(value: bool): + _active = value + set_physics_process(value) + set_process_input(value) + if not _active: + states_stack = [] + current_state = null + + +# Manage an input event by the state machine's current state. +# +# #### Parameters +# +# - event: InputEvent to manage. +func _input(event: InputEvent): + current_state.handle_input(event) + + +# Lets the state machine's current state perform an update during +# _physics_process() phase. +# +# #### Parameters +# +# - delta: float value corresponding to the elapsed time since last frame update. +func _physics_process(delta: float): + current_state.update(delta) + + +# Lets the state machine's current state perform an action on animation_finished +# signal. +# +# #### Parameters +# +# - anim_name: name of the animation that finished. +func _on_animation_finished(anim_name: String): + if not _active: + return + current_state._on_animation_finished(anim_name) + + +# Change the current state of the state machine using its name. The value of +# the state to be set is obtained in states_map dictionary. +# +# #### Parameters +# +# - state_name: name of the state to set. +func _change_state(state_name: String): + if not _active: + return + + escoria.logger.trace( + self, + "Dialog State Machine: Changing state from '%s' to '%s'." % [current_state_name, state_name] + ) + + current_state.exit() + + if state_name == "previous": + states_stack.pop_front() + else: + states_stack[0] = states_map[state_name] + + current_state = states_stack[0] + + state_changed.emit(current_state) + + #if state_name != "previous": + current_state.enter() + + current_state_name = state_name diff --git a/addons/escoria-core/patterns/state_machine/state_machine.gd.uid b/addons/escoria-core/patterns/state_machine/state_machine.gd.uid new file mode 100644 index 0000000..5b8e593 --- /dev/null +++ b/addons/escoria-core/patterns/state_machine/state_machine.gd.uid @@ -0,0 +1 @@ +uid://bti0tjqa05fo8 diff --git a/addons/escoria-core/plugin.cfg b/addons/escoria-core/plugin.cfg new file mode 100644 index 0000000..88dd676 --- /dev/null +++ b/addons/escoria-core/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Escoria" +description="A point'n'click framework built for Godot Engine 4.x" +author="Escoria Project" +version="1.0.0" +script="plugin.gd" diff --git a/addons/escoria-core/plugin.gd b/addons/escoria-core/plugin.gd new file mode 100644 index 0000000..8c5dc09 --- /dev/null +++ b/addons/escoria-core/plugin.gd @@ -0,0 +1,613 @@ +@tool +## Plugin script to initialize Escoria +extends EditorPlugin + +## Comma separator const used to build enabled extensions. +const COMMA_SEPARATOR = "," + +## ESC files extension. +const ESC_SCRIPT_EXTENSION = "esc" +const ASH_SCRIPT_EXTENSION = "ash" +const ASHES_ANALYZER_MENU_ITEM = "Analyze ASHES Scripts" + + +## The warning popup displayed on escoria-core enabling. +var popup_info: AcceptDialog + +## ASHES scripts analyzer. Needed to allow calling the analyzer from +## Project>Tools menu. +var _compiler_analyzer: ESCAshesAnalyzer = ESCAshesAnalyzer.new() + + +## Virtual function called when plugin is enabled.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _enable_plugin(): + add_autoload_singleton( + "escoria", + "res://addons/escoria-core/game/esc_autoload.gd" + ) + # Prepare settings + _set_escoria_main_settings() + _set_escoria_debug_settings() + _set_escoria_ui_settings() + _set_escoria_sound_settings() + _set_escoria_platform_settings() + _set_filesystem_show_esc_files() + + # Define standard settings + ProjectSettings.set_setting( + "application/run/main_scene", + "res://addons/escoria-core/game/main_scene.tscn" + ) + + ProjectSettings.set_setting( + "audio/buses/default_bus_layout", + "res://addons/escoria-core/buses/default_bus_layout.tres" + ) + + popup_info = AcceptDialog.new() + popup_info.dialog_text = """You enabled escoria-core plugin. + + Please ignore error messages in Output console and reload your project using + Godot editor's \"Project / Reload Current Project\" menu. + """ + popup_info.confirmed.connect(self._on_warning_popup_confirmed, CONNECT_ONE_SHOT) + get_editor_interface().get_editor_main_screen().add_child(popup_info) + popup_info.popup_centered() + + +## Callback for warning popup displayed on escoria-core plugin enabling.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_warning_popup_confirmed(): + popup_info.queue_free() + + +## Virtual function called when plugin is disabled.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _disable_plugin(): + remove_autoload_singleton("escoria") + _set_filesystem_hide_esc_files() + + +## Called when Escoria plugin gets added to Godot Editor's tree.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _enter_tree(): + # have to add this here since reloading the project doesn't re-add the Tools menu item + add_tool_menu_item(ASHES_ANALYZER_MENU_ITEM, _compiler_analyzer.analyze) + + +## Called when Escoria plugin gets removed from Godot Editor's tree.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _exit_tree(): + remove_tool_menu_item(ASHES_ANALYZER_MENU_ITEM) + + +## Prepare the settings in the Escoria UI category[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_escoria_ui_settings(): + register_setting( + ESCProjectSettingsManager.DEFAULT_DIALOG_TYPE, + "", + { + "type": TYPE_STRING + } + ) + print("Default dialog type has been reset!") + + register_setting( + ESCProjectSettingsManager.GAME_SCENE, + "", + { + "name": ESCProjectSettingsManager.GAME_SCENE, + "type": TYPE_STRING, + "hint": PROPERTY_HINT_FILE, + "hint_string": "*.tscn, *.scn" + } + ) + + register_setting( + ESCProjectSettingsManager.INVENTORY_ITEMS_PATH, + "res://game/items/inventory/", + { + "name": ESCProjectSettingsManager.INVENTORY_ITEMS_PATH, + "type": TYPE_STRING, + "hint": PROPERTY_HINT_DIR + } + ) + + register_setting( + ESCProjectSettingsManager.DEFAULT_TRANSITION, + "curtain", + { + "name": ESCProjectSettingsManager.DEFAULT_TRANSITION, + "type": TYPE_STRING, + "hint": PROPERTY_HINT_ENUM_SUGGESTION, + "hint_string": "instant,fade_black,fade_white,curtain" + } + ) + + register_setting( + ESCProjectSettingsManager.TRANSITION_PATHS, + [ + "res://addons/escoria-core/game/scenes/transitions/shaders/" + ], + { + "name": ESCProjectSettingsManager.TRANSITION_PATHS, + "type": TYPE_PACKED_STRING_ARRAY, + "hint": PROPERTY_HINT_DIR + } + ) + + register_setting( + ESCProjectSettingsManager.INVENTORY_ITEM_SIZE, + Vector2(72, 72), + { + "name": ESCProjectSettingsManager.INVENTORY_ITEM_SIZE, + "type": TYPE_VECTOR2 + } + ) + + register_setting( + ESCProjectSettingsManager.DIALOG_MANAGERS, + [], + { + "type": TYPE_PACKED_STRING_ARRAY + } + ) + + +## Prepare the settings in the Escoria main category[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_escoria_main_settings(): + register_setting( + ESCProjectSettingsManager.GAME_VERSION, + "", + { + "type": TYPE_STRING + } + ) + + register_setting( + ESCProjectSettingsManager.GAME_START_SCRIPT, + "", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_FILE, + "hint_string": "*.esc,*.ash" + } + ) + + register_setting( + ESCProjectSettingsManager.ACTION_DEFAULT_SCRIPT, + "", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_FILE, + "hint_string": "*.esc,*.ash" + } + ) + + register_setting( + ESCProjectSettingsManager.FORCE_QUIT, + true, + { + "type": TYPE_BOOL + } + ) + + register_setting( + ESCProjectSettingsManager.COMMAND_DIRECTORIES, + [ + "res://addons/escoria-core/game/core-scripts/esc/commands" + ], + { + "type": TYPE_ARRAY, + } + ) + + register_setting( + ESCProjectSettingsManager.TEXT_LANG, + TranslationServer.get_locale(), + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_NONE + } + ) + + register_setting( + ESCProjectSettingsManager.VOICE_LANG, + TranslationServer.get_locale(), + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_NONE + } + ) + + register_setting( + ESCProjectSettingsManager.SAVEGAMES_PATH, + "user://saves/", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_DIR + } + ) + + register_setting( + ESCProjectSettingsManager.SETTINGS_PATH, + "user://", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_DIR + } + ) + + register_setting( + ESCProjectSettingsManager.GAME_MIGRATION_PATH, + "", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_DIR + } + ) + + register_setting( + ESCProjectSettingsManager.WINDOW_MODE, + false, + { + "type": TYPE_BOOL, + } + ) + + +## Prepare the settings in the Escoria debug category[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_escoria_debug_settings(): + register_setting( + ESCProjectSettingsManager.TERMINATE_ON_WARNINGS, + false, + { + "type": TYPE_BOOL + } + ) + + register_setting( + ESCProjectSettingsManager.TERMINATE_ON_ERRORS, + true, + { + "type": TYPE_BOOL + } + ) + + register_setting( + ESCProjectSettingsManager.DEVELOPMENT_LANG, + "en", + { + "type": TYPE_STRING + } + ) + + register_setting( + ESCProjectSettingsManager.LOG_LEVEL, + "ERROR", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_ENUM, + "hint_string": "ERROR,WARNING,INFO,DEBUG,TRACE" + } + ) + + register_setting( + ESCProjectSettingsManager.LOG_FILE_PATH, + "user://", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_DIR + } + ) + + register_setting( + ESCProjectSettingsManager.CRASH_MESSAGE, + "We're sorry, but the game crashed. Please send us the " + + "following files:\n\n%s", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_MULTILINE_TEXT + } + ) + + register_setting( + ESCProjectSettingsManager.ENABLE_ROOM_SELECTOR, + false, + { + "type": TYPE_BOOL + } + ) + + register_setting( + ESCProjectSettingsManager.ROOM_SELECTOR_ROOM_DIR, + "", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_DIR + } + ) + + register_setting( + ESCProjectSettingsManager.ENABLE_HOVER_STACK_VIEWER, + false, + { + "type": TYPE_BOOL + } + ) + + register_setting( + ESCProjectSettingsManager.PERFORM_SCRIPT_ANALYSIS_AT_RUNTIME, + true, + { + "type": TYPE_BOOL + } + ) + + +## Prepare the settings in the Escoria sound settings[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_escoria_sound_settings(): + register_setting( + ESCProjectSettingsManager.MASTER_VOLUME, + 1, + { + "type": TYPE_FLOAT, + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,1" + } + ) + + register_setting( + ESCProjectSettingsManager.MUSIC_VOLUME, + 1, + { + "type": TYPE_FLOAT, + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,1" + } + ) + + register_setting( + ESCProjectSettingsManager.SFX_VOLUME, + 1, + { + "type": TYPE_FLOAT, + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,1" + } + ) + + register_setting( + ESCProjectSettingsManager.AMBIENT_VOLUME, + 1, + { + "type": TYPE_FLOAT, + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,1" + } + ) + + register_setting( + ESCProjectSettingsManager.SPEECH_VOLUME, + 1, + { + "type": TYPE_FLOAT, + "hint": PROPERTY_HINT_RANGE, + "hint_string": "0,1" + } + ) + + register_setting( + ESCProjectSettingsManager.SPEECH_ENABLED, + true, + { + "type": TYPE_BOOL + } + ) + + register_setting( + ESCProjectSettingsManager.SPEECH_FOLDER, + "res://speech", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_DIR + } + ) + + register_setting( + ESCProjectSettingsManager.SPEECH_EXTENSION, + "ogg", + { + "type": TYPE_STRING + } + ) + + +## Prepare the settings in the Escoria platform category and may need special setting per build.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_escoria_platform_settings(): + # Skip cache - certain platforms (esp. mobile) lack memory for caching + # scenes. If set to true, all generic scenes (UI, inventory, etc) will be + # loaded as any other scene. + register_setting( + ESCProjectSettingsManager.SKIP_CACHE, + false, + { + "type": TYPE_BOOL + } + ) + + register_setting( + ESCProjectSettingsManager.SKIP_CACHE_MOBILE, + true, + { + "type": TYPE_BOOL + } + ) + + +## Register a new project setting if it hasn't been defined already[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |name|`String`|Fully qualified Project Settings key to register.|yes|[br] +## |default|`Variant`|Default value|yes|[br] +## |info|`Dictionary`|Property info for the setting|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +static func register_setting(name: String, default: Variant, info: Dictionary) -> void: + if not ProjectSettings.has_setting(name): + # Only core settings should set this to true. Settings configured in + # plugins should not set this to true. + info["core_setting"] = "true" + ProjectSettings.set_setting( + name, + default + ) + info.name = name + ProjectSettings.add_property_info(info) + + +## Sets the Godot Editor settings to display ESC files in the filesystem.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_filesystem_show_esc_files(): + print("setting esc and ash files display") + var settings = EditorInterface.get_editor_settings() + var displayed_extensions: PackedStringArray = settings.get_setting( + "docks/filesystem/textfile_extensions").split(COMMA_SEPARATOR) + var needs_modification: bool = false + if not ESC_SCRIPT_EXTENSION in displayed_extensions: + displayed_extensions.append(ESC_SCRIPT_EXTENSION) + needs_modification = true + if not ASH_SCRIPT_EXTENSION in displayed_extensions: + displayed_extensions.append(ASH_SCRIPT_EXTENSION) + needs_modification = true + if needs_modification: + settings.set_setting( + "docks/filesystem/textfile_extensions", + COMMA_SEPARATOR.join(displayed_extensions) + ) + if not displayed_extensions.has(ASH_SCRIPT_EXTENSION): + displayed_extensions.append(ASH_SCRIPT_EXTENSION) + settings.set_setting( + "docks/filesystem/textfile_extensions", + COMMA_SEPARATOR.join(displayed_extensions) + ) + + +## Sets the Godot Editor settings to hide ESC and ASH files in the filesystem.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _set_filesystem_hide_esc_files(): + print("setting esc files hide") + var settings = EditorInterface.get_editor_settings() + var displayed_extensions: PackedStringArray = settings.get_setting( + "docks/filesystem/textfile_extensions").split(COMMA_SEPARATOR) + var index_esc: int = displayed_extensions.find(ESC_SCRIPT_EXTENSION) + var index_ash: int = displayed_extensions.find(ASH_SCRIPT_EXTENSION) + var needs_modification: bool = false + while index_esc != -1: + displayed_extensions.remove_at(index_esc) + index_esc = displayed_extensions.find(ESC_SCRIPT_EXTENSION) + needs_modification = true + while index_ash != -1: + displayed_extensions.remove_at(index_ash) + index_ash = displayed_extensions.find(ASH_SCRIPT_EXTENSION) + needs_modification = true + if needs_modification: + settings.set_setting( + "docks/filesystem/textfile_extensions", + COMMA_SEPARATOR.join(displayed_extensions) + ) diff --git a/addons/escoria-core/plugin.gd.uid b/addons/escoria-core/plugin.gd.uid new file mode 100644 index 0000000..8e12d44 --- /dev/null +++ b/addons/escoria-core/plugin.gd.uid @@ -0,0 +1 @@ +uid://bhf6sjrdkurfb diff --git a/addons/escoria-core/testing/NotoSans-Regular.ttf b/addons/escoria-core/testing/NotoSans-Regular.ttf new file mode 100644 index 0000000..0a01a06 Binary files /dev/null and b/addons/escoria-core/testing/NotoSans-Regular.ttf differ diff --git a/addons/escoria-core/testing/NotoSans-Regular.ttf.import b/addons/escoria-core/testing/NotoSans-Regular.ttf.import new file mode 100644 index 0000000..2c5fd00 --- /dev/null +++ b/addons/escoria-core/testing/NotoSans-Regular.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://el1rp51n7kpd" +path="res://.godot/imported/NotoSans-Regular.ttf-3d2825f96eb34e3bdf9d923048c8a54f.fontdata" + +[deps] + +source_file="res://addons/escoria-core/testing/NotoSans-Regular.ttf" +dest_files=["res://.godot/imported/NotoSans-Regular.ttf-3d2825f96eb34e3bdf9d923048c8a54f.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/escoria-core/testing/README b/addons/escoria-core/testing/README new file mode 100644 index 0000000..b81e0cb --- /dev/null +++ b/addons/escoria-core/testing/README @@ -0,0 +1,8 @@ +In this folder, you can find various scenes and tools to test some of the scenes of your actual game project. + +These scenes are not intended to be shipped with your game, you may want to use them only for testing purposes, such as: + +- RichTextLabels screen clamping (rtl_screen_clamping.tscn) + This scene helps you test the RichTextLabels used in your game, such as labels used when pointing an item or NPC or dialogs. + To use this testing scene, simply add your actual label scene as a child of the root node and set the path to this node in it. + You can then run the scene, move your mouse around the edges of the screen and see if the label reacts properly. diff --git a/addons/escoria-core/testing/player_angles_finder.gd b/addons/escoria-core/testing/player_angles_finder.gd new file mode 100644 index 0000000..e47b83c --- /dev/null +++ b/addons/escoria-core/testing/player_angles_finder.gd @@ -0,0 +1,231 @@ +extends Node2D + +# This scenes purpose is to help determining the angles between X and Y axis +# when clicking somewhere on the screen. +# Let us consider that player position is always 0,0. +# Clicking right above his head is angle 0. +# Clicking on the right is angle 90. +# Clicking under his feet is angle 180. +# etc. + +var number_of_directions: int +var angle_horizontal_axes: float +var angle_vertical_axes: float +var angle_diagonal_axes: float +const POLYGON_DISTANCE = 400 + +enum Directions { + NORTH, # 0 + NORTHEAST, # 1 + EAST, # 2 + SOUTHEAST, # 3 + SOUTH, # 4 + SOUTHWEST, # 5 + WEST, # 6 + NORTHWEST # 7 +} + +const starting_angles = [ + 0, # 0 NORTH + PI/4, # 1 NORTHEAST + PI/2, # 2 EAST + 3*PI/4, # 3 SOUTHEAST + PI, # 4 SOUTH + 5*PI/4, # 5 SOUTHWEST + 3*PI/2, # 6 WEST + 7*PI/4, # 7 NORTHWEST +] + +var colors = [ + Color("red"), # 0 NORTH + Color("green"), # 1 NORTHEAST + Color("blue"), # 2 EAST + Color("black"), # 3 SOUTHEAST + Color("yellow"), # 4 SOUTH + Color("cyan"), # 5 SOUTHWEST + Color("white"), # 6 WEST + Color("purple") # 7 NORTHWEST +] + +var result_angles = [] + +#onready var result_angles_anim = { +# "angle_offset_rad": PI/2, +# Directions.NORTH: { +# "direction_base_angle_rad": 0, +# "angle_start_deg": 0, +# "angle_area_deg": 0, +# "animations": {} +# }, +# Directions.NORTHEAST: { +# "direction_base_angle_rad": PI/4, +# "angle_start_deg": 0, +# "angle_area_deg": 0, +# "animation": "" +# }, +# Directions.EAST: { +# "direction_base_angle_rad": PI/2, +# "angle_start_deg": 0, +# "angle_area_deg": 0, +# "animation": "" +# }, +# Directions.SOUTHEAST: { +# "direction_base_angle_rad": 3*PI/4, +# "angle_start_deg": 0, +# "angle_area_deg": 0, +# "animation": "" +# }, +# Directions.SOUTH: { +# "direction_base_angle_rad": PI, +# "angle_start_deg": 0, +# "angle_area_deg": 0, +# "animation": "" +# +# }, +# Directions.SOUTHWEST: { +# "direction_base_angle_rad": 5*PI/4, +# "angle_start_deg": 0, +# "angle_area_deg": 0, +# "animation": "" +# }, +# Directions.WEST: { +# "direction_base_angle_rad": 3*PI/2, +# "angle_start_deg": 0, +# "angle_area_deg": 0, +# "animation": "" +# }, +# Directions.NORTHWEST: { +# "direction_base_angle_rad": 7*PI/4, +# "angle_start_deg": 0, +# "angle_area_deg": 0, +# "animation": "" +# } +# } + + + +func _ready(): + # Find player animations + $player_animations.add_item("") + for anim_name in $player.get_animation_player().get_animations(): + $player_animations.add_item(anim_name) + + # Set initial angles + var initial_angle: float = 360.0 / 8.0 + $VBoxContainer/angle_x/angle_horiz.text = str(40) + $VBoxContainer/angle_y/angle_vert.text = str(40) + $VBoxContainer/angle_diag/angle_diag.text = str(50) + angle_horizontal_axes = 40 + angle_vertical_axes = 40 + angle_diagonal_axes = 50 + + calculate_areas() + + +func _on_number_of_directions_text_changed(new_text: String): + if !new_text.is_valid_int(): + return + number_of_directions = int(new_text) + calculate_areas(number_of_directions) + + +func clear_areas_node(): + for n in $areas.get_children(): + n.queue_free() + + +func calculate_areas(nb_directions: int = 8): + clear_areas_node() + var angles = [] + for i in range(nb_directions): + var angle_area: float = 0.0 + var start_angle: float = 0.0 + # MANUAL + match i: + Directions.EAST,Directions.WEST: + angle_area = deg_to_rad(angle_horizontal_axes) + Directions.NORTH,Directions.SOUTH: + angle_area = deg_to_rad(angle_vertical_axes) + Directions.NORTHEAST,Directions.NORTHWEST,Directions.SOUTHEAST,Directions.SOUTHWEST: + angle_area = deg_to_rad(angle_diagonal_axes) + + # Since angles start from EAST, offset by -PI/2 (= -90°) to start on up direction + # Then minus angle_area/2 to align on direction + start_angle = PI/2 + angle_area / 2 + + var angle_start = starting_angles[i] - start_angle + var angle_end = angle_start + angle_area + angles.push_back([angle_start, angle_end]) + + result_angles.push_back([clamp360(rad_to_deg(angle_start) + rad_to_deg(PI/2)),clamp360(rad_to_deg(angle_area))]) + + $VBoxContainer/VBoxContainer/angles/angle_array.text = str(result_angles) + construct_scene_nodes(angles) + + +func construct_scene_nodes(angles): + var areas_nodes = [] + for i in angles.size(): + var polygon_node = Polygon2D.new() + polygon_node.color = colors[i] + var area_node = Area2D.new() + area_node.name = Directions.keys()[i] + area_node.connect("input_event", Callable(self, "_on_area_click").bind(area_node.name)) + polygon_node.add_child(area_node) + + var collision = CollisionShape2D.new() + var collision_shape = ConvexPolygonShape2D.new() + + var p_points = [] + p_points.push_back($player.position) + p_points.push_back(POLYGON_DISTANCE * Vector2.from_angle(angles[i][0]) + $player.position) + p_points.push_back(POLYGON_DISTANCE * Vector2.from_angle(angles[i][1]) + $player.position) + polygon_node.polygon = p_points + collision_shape.points = p_points + collision.set_shape(collision_shape) + area_node.add_child(collision) + + $areas.add_child(polygon_node) + + +func _on_angle_horiz_text_changed(new_text: String): + if !new_text.is_valid_float(): + return + angle_horizontal_axes = float(new_text) +# result_angles_anim.Directions.EAST.angle_area_deg = clamp360(angle_horizontal_axes) +# result_angles_anim.Directions.WEST.angle_area_deg = clamp360(angle_horizontal_axes) + calculate_areas() + + +func _on_angle_vert_text_changed(new_text: String): + if !new_text.is_valid_float(): + return + angle_vertical_axes = float(new_text) +# result_angles_anim.Directions.NORTH.angle_area_deg = clamp360(angle_vertical_axes) +# result_angles_anim.Directions.SOUTH.angle_area_deg = clamp360(angle_vertical_axes) + calculate_areas() + + +func _on_angle_diag_text_changed(new_text: String): + if !new_text.is_valid_float(): + return + angle_diagonal_axes = float(new_text) +# result_angles_anim.Directions.NORTHEAST.angle_area_deg = clamp360(angle_diagonal_axes) +# result_angles_anim.Directions.SOUTHEAST.angle_area_deg = clamp360(angle_diagonal_axes) +# result_angles_anim.Directions.NORTHWEST.angle_area_deg = clamp360(angle_diagonal_axes) +# result_angles_anim.Directions.SOUTHWEST.angle_area_deg = clamp360(angle_diagonal_axes) + calculate_areas() + + +func _on_area_click(viewport: Object, event: InputEvent, shape_idx: int, area_name: String): + if event is InputEventMouseButton and event.is_pressed(): + pass + +func clamp360(angle: float): + if angle < 0.0: + while angle < 0.0: + angle += 360.0 + elif angle >= 360.0: + while angle >= 360.0: + angle -= 360.0 + return angle diff --git a/addons/escoria-core/testing/player_angles_finder.gd.uid b/addons/escoria-core/testing/player_angles_finder.gd.uid new file mode 100644 index 0000000..cef4f64 --- /dev/null +++ b/addons/escoria-core/testing/player_angles_finder.gd.uid @@ -0,0 +1 @@ +uid://grwlokhruew6 diff --git a/addons/escoria-core/testing/player_angles_finder.tscn b/addons/escoria-core/testing/player_angles_finder.tscn new file mode 100644 index 0000000..3817db0 --- /dev/null +++ b/addons/escoria-core/testing/player_angles_finder.tscn @@ -0,0 +1,137 @@ +[gd_scene load_steps=3 format=3 uid="uid://cj02p2rfv12br"] + +[ext_resource type="PackedScene" uid="uid://chf3qovs1q8kr" path="res://game/characters/mark/mark.tscn" id="1"] +[ext_resource type="Script" uid="uid://grwlokhruew6" path="res://addons/escoria-core/testing/player_angles_finder.gd" id="2"] + +[node name="player_angles_finder" type="Node2D"] +script = ExtResource("2") + +[node name="areas" type="Node2D" parent="."] + +[node name="player" parent="." instance=ExtResource("1")] +position = Vector2(640, 480) +dialog_position_node = NodePath("../../player_angles_finder/player/dialog_position") + +[node name="x_axis" type="Line2D" parent="."] +points = PackedVector2Array(-10, 480, 1300, 480) +width = 2.0 +default_color = Color(0.980392, 0.00392157, 0.00392157, 1) + +[node name="y_axis" type="Line2D" parent="."] +points = PackedVector2Array(640, -20, 640, 810) +width = 2.0 +default_color = Color(0.156863, 0.0117647, 0.984314, 1) + +[node name="three" type="Node2D" parent="."] +visible = false + +[node name="Line2D" type="Line2D" parent="three"] +points = PackedVector2Array(160, 0, 1120, 960) +width = 2.0 +default_color = Color(1, 1, 1, 1) + +[node name="Line2D2" type="Line2D" parent="three"] +points = PackedVector2Array(160, 960, 1120, 0) +width = 2.0 +default_color = Color(1, 1, 1, 1) + +[node name="six" type="Node2D" parent="."] +visible = false + +[node name="Line2D" type="Line2D" parent="six"] +points = PackedVector2Array(0, 210, 640, 480) +width = 2.0 +default_color = Color(1, 1, 1, 1) + +[node name="Line2D4" type="Line2D" parent="six"] +points = PackedVector2Array(320, 0, 640, 480) +width = 2.0 +default_color = Color(1, 1, 1, 1) + +[node name="Line2D2" type="Line2D" parent="six"] +points = PackedVector2Array(640, 480, 1280, 210) +width = 2.0 +default_color = Color(1, 1, 1, 1) + +[node name="Line2D3" type="Line2D" parent="six"] +points = PackedVector2Array(640, 480, 960, 0) +width = 2.0 +default_color = Color(1, 1, 1, 1) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +offset_right = 370.0 +offset_bottom = 170.0 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +visible = false +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Number of directions" + +[node name="number_of_directions" type="LineEdit" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="angle_x" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/angle_x"] +layout_mode = 2 +text = "Angle on horizontal axis (X)" + +[node name="angle_horiz" type="LineEdit" parent="VBoxContainer/angle_x"] +layout_mode = 2 + +[node name="angle_y" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/angle_y"] +layout_mode = 2 +text = "Angle on vertical axis (Y)" + +[node name="angle_vert" type="LineEdit" parent="VBoxContainer/angle_y"] +layout_mode = 2 + +[node name="angle_diag" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/angle_diag"] +layout_mode = 2 +text = "Angle on diagonal axes" + +[node name="angle_diag" type="LineEdit" parent="VBoxContainer/angle_diag"] +layout_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="angles" type="HBoxContainer" parent="VBoxContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Label" type="Label" parent="VBoxContainer/VBoxContainer/angles"] +layout_mode = 2 +text = "Angles array" + +[node name="angle_array" type="TextEdit" parent="VBoxContainer/VBoxContainer/angles"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="player_animations" type="OptionButton" parent="."] +visible = false +offset_left = 470.0 +offset_top = 10.0 +offset_right = 638.0 +offset_bottom = 33.0 + +[connection signal="text_changed" from="VBoxContainer/HBoxContainer/number_of_directions" to="." method="_on_number_of_directions_text_changed"] +[connection signal="text_changed" from="VBoxContainer/angle_x/angle_horiz" to="." method="_on_angle_horiz_text_changed"] +[connection signal="text_changed" from="VBoxContainer/angle_y/angle_vert" to="." method="_on_angle_vert_text_changed"] +[connection signal="text_changed" from="VBoxContainer/angle_diag/angle_diag" to="." method="_on_angle_diag_text_changed"] +[connection signal="text_changed" from="VBoxContainer/VBoxContainer/angles/angle_array" to="." method="_on_angle_diag_text_changed"] diff --git a/addons/escoria-core/testing/rtl_screen_offset_testing.gd b/addons/escoria-core/testing/rtl_screen_offset_testing.gd new file mode 100644 index 0000000..4083bbe --- /dev/null +++ b/addons/escoria-core/testing/rtl_screen_offset_testing.gd @@ -0,0 +1,174 @@ +extends Control + +@onready var screen_width = get_tree().get_root().get_viewport().size.x +@onready var screen_height = get_tree().get_root().get_viewport().size.y +var global_distance_to_clamp = 40: get = get_global_dist_clamp, set = set_global_dist_clamp + +signal mouse_moved(position) +signal text_selected(text) + +@export var path_to_richtextlabel: NodePath +const ONE_LINE_HEIGHT = 16 +@export var max_width: int = 200 +const MIN_HEIGHT = 30 +const MAX_HEIGHT = 500 + +func _ready(): + assert(!path_to_richtextlabel.is_empty()) + $VBoxContainer/HBoxContainer/clamp_distance.text = str(global_distance_to_clamp) + + var button_group = ButtonGroup.new() + $HBoxContainer2/foo.button_group = button_group + $HBoxContainer2/foobar.button_group = button_group + $HBoxContainer2/whatisit.button_group = button_group + + + # Add a white TextureRect behind the RTL to see its actual size + var texturerect_node = TextureRect.new() + get_node(path_to_richtextlabel).add_child(texturerect_node) + texturerect_node.texture = load("res://addons/escoria-core/game/assets/images/white.png") + texturerect_node.expand = true + texturerect_node.stretch_mode = TextureRect.STRETCH_TILE + texturerect_node.size_flags_horizontal = SIZE_EXPAND_FILL + texturerect_node.size_flags_vertical = SIZE_EXPAND_FILL + texturerect_node.show_behind_parent = true + texturerect_node.anchor_right = 1.0 + texturerect_node.anchor_bottom = 1.0 + move_child(get_node(path_to_richtextlabel), 1) + + update_line2d() + _on_new_text_pressed() + set_process_input(true) + +func set_path_to_richtextlabel(path): + path_to_richtextlabel = path + + +func _input(event): + if event is InputEventMouseMotion: + mouse_moved.emit(event.position) + +func set_global_dist_clamp(d): + global_distance_to_clamp = d + update_line2d() + +func get_global_dist_clamp(): + return global_distance_to_clamp + +func update_line2d(): + $Line2D.clear_points() + $Line2D.add_point(Vector2(global_distance_to_clamp, global_distance_to_clamp)) + $Line2D.add_point(Vector2(global_distance_to_clamp, screen_height - global_distance_to_clamp)) + $Line2D.add_point(Vector2(screen_width - global_distance_to_clamp, screen_height - global_distance_to_clamp)) + $Line2D.add_point(Vector2(screen_width - global_distance_to_clamp, global_distance_to_clamp)) + $Line2D.add_point(Vector2(global_distance_to_clamp, global_distance_to_clamp)) + + +func _on_new_text_pressed(): + var pressed_button = $HBoxContainer2/foo.button_group.get_pressed_button() + if pressed_button == null: + return + printt(pressed_button.name, pressed_button.text) + text_selected.emit(pressed_button.text) + + +func tooltip_distance_to_edge_top(position: Vector2): + return position.y + +func tooltip_distance_to_edge_bottom(position: Vector2): + return screen_height - position.y + +func tooltip_distance_to_edge_left(position: Vector2): + return position.x + +func tooltip_distance_to_edge_right(position: Vector2): + return screen_width - position.x + +func _on_Control_mouse_moved(mouse_pos): +# printt("mousepos", mouse_pos) +# printt("label_container_pos", rect_position) + + #var corrected_position = _offset(new_pos) + var corrected_position = mouse_pos + + # clamp TOP + if tooltip_distance_to_edge_top(mouse_pos) <= global_distance_to_clamp: + corrected_position.y = global_distance_to_clamp + + # clamp BOTTOM + if tooltip_distance_to_edge_bottom(mouse_pos + get_node(path_to_richtextlabel).size) <= global_distance_to_clamp: + corrected_position.y = screen_height - global_distance_to_clamp - get_node(path_to_richtextlabel).size.y + + # clamp LEFT + if tooltip_distance_to_edge_left(mouse_pos - get_node(path_to_richtextlabel).size/2) <= global_distance_to_clamp: + corrected_position.x = global_distance_to_clamp + + # clamp RIGHT + if tooltip_distance_to_edge_right(mouse_pos + get_node(path_to_richtextlabel).size/2) <= global_distance_to_clamp: + corrected_position.x = screen_width - global_distance_to_clamp - get_node(path_to_richtextlabel).size.x + + get_node(path_to_richtextlabel).anchor_right = 0.2 + get_node(path_to_richtextlabel).position = corrected_position + + +func _on_Control_text_selected(text): + get_node(path_to_richtextlabel).text = "[color=red]" + text.replace("
", "\n") + "[/color]" + update_size() + +func _on_clamp_distance_text_changed(new_text): + global_distance_to_clamp = int(new_text) + update_line2d() + + +func _offset(position): + var center_offset_x = size.x / 2 + var offset_y = 5 + + position.x -= center_offset_x + position.y += offset_y + return position + + +func update_size(): + ## RECT_SIZE ## + var rtl_node = get_node(path_to_richtextlabel) + var rtl_width = rtl_node.size.x + var rtl_height = rtl_node.size.y + var content_height = rtl_node.get_content_height() + var nb_visible_characters = rtl_node.visible_characters + var nb_visible_lines = rtl_node.get_visible_line_count() + + printt("BEFORE", "text_height", content_height, "rtl_height", rtl_node.size.y) + + # if text is too long and is wrapped +# var nblines = float(rtl_node.get_content_height()) / float(ONE_LINE_HEIGHT) + var nblines = nb_visible_lines + if nblines >= 1: + + await get_tree().process_frame + await get_tree().process_frame + var text_height = rtl_node.get_content_height() + if text_height > MAX_HEIGHT: + text_height = MAX_HEIGHT + if text_height <= MIN_HEIGHT: + text_height = MIN_HEIGHT + + var parent_width = rtl_node.size.x + + # first, try to increase width until it goes above max_width + while parent_width < max_width && float(text_height) / float(ONE_LINE_HEIGHT) > 1.0: + rtl_node.size.x += 1 + parent_width = rtl_node.size.x + + + rtl_node.size.y = text_height + + if rtl_node.size.x >= max_width: + rtl_node.size.x = max_width + + ## END RECT_SIZE ## + rtl_node.anchor_top = 0.0 + rtl_node.anchor_right = 0.0 + rtl_node.anchor_bottom = 0.0 + rtl_node.anchor_left = 0.0 + printt("AFTER", "text_height", get_node(path_to_richtextlabel).get_content_height(), "rtl_height", rtl_node.size.y) diff --git a/addons/escoria-core/testing/rtl_screen_offset_testing.gd.uid b/addons/escoria-core/testing/rtl_screen_offset_testing.gd.uid new file mode 100644 index 0000000..ab281ea --- /dev/null +++ b/addons/escoria-core/testing/rtl_screen_offset_testing.gd.uid @@ -0,0 +1 @@ +uid://cgtqofwnv18rn diff --git a/addons/escoria-core/testing/rtl_screen_offset_testing.tscn b/addons/escoria-core/testing/rtl_screen_offset_testing.tscn new file mode 100644 index 0000000..04abfaf --- /dev/null +++ b/addons/escoria-core/testing/rtl_screen_offset_testing.tscn @@ -0,0 +1,73 @@ +[gd_scene load_steps=3 format=3 uid="uid://diq5rsixfcxri"] + +[ext_resource type="Script" uid="uid://cgtqofwnv18rn" path="res://addons/escoria-core/testing/rtl_screen_offset_testing.gd" id="1"] +[ext_resource type="PackedScene" uid="uid://b3hru28v8y36i" path="res://addons/escoria-ui-simplemouse/tooltip/target_tooltip.tscn" id="2"] + +[node name="rtl_screen_offset" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -1280.0 +offset_bottom = -800.0 +script = ExtResource("1") +path_to_richtextlabel = NodePath("tooltip") + +[node name="Line2D" type="Line2D" parent="."] +points = PackedVector2Array(40, 40, 40, 976, 1808, 976, 1808, 40, 40, 40) +width = 2.51 +texture_mode = 116 + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 0 +offset_left = 315.72 +offset_top = 269.104 +offset_right = 475.72 +offset_bottom = 321.104 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Clamp distance" + +[node name="clamp_distance" type="LineEdit" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "40" +max_length = 4 + +[node name="HBoxContainer2" type="VBoxContainer" parent="."] +layout_mode = 0 +offset_left = 310.0 +offset_top = 340.0 +offset_right = 470.0 +offset_bottom = 380.0 + +[node name="foo" type="CheckBox" parent="HBoxContainer2"] +layout_mode = 2 +text = "Foo" + +[node name="foobar" type="CheckBox" parent="HBoxContainer2"] +layout_mode = 2 +text = "Foo bar" + +[node name="whatisit" type="CheckBox" parent="HBoxContainer2"] +layout_mode = 2 +text = "A super extremely long sentence to test
the behaviour of that RichTextLabel node..." + +[node name="tooltip" parent="." instance=ExtResource("2")] +custom_minimum_size = Vector2(400, 0) +layout_mode = 0 +offset_left = 238.815 +offset_top = 131.18 +offset_right = 638.815 +offset_bottom = 231.18 +text = "[center][color=#200606][/color][/center]" + +[connection signal="mouse_moved" from="." to="." method="_on_Control_mouse_moved"] +[connection signal="text_selected" from="." to="." method="_on_Control_text_selected"] +[connection signal="text_changed" from="VBoxContainer/HBoxContainer/clamp_distance" to="." method="_on_clamp_distance_text_changed"] +[connection signal="pressed" from="HBoxContainer2/foo" to="." method="_on_new_text_pressed"] +[connection signal="pressed" from="HBoxContainer2/foobar" to="." method="_on_new_text_pressed"] +[connection signal="pressed" from="HBoxContainer2/whatisit" to="." method="_on_new_text_pressed"] diff --git a/addons/escoria-core/tools/logging/esc_log_level.gd b/addons/escoria-core/tools/logging/esc_log_level.gd new file mode 100644 index 0000000..517cbf5 --- /dev/null +++ b/addons/escoria-core/tools/logging/esc_log_level.gd @@ -0,0 +1,35 @@ +class_name ESCLogLevel +## Log levels for ESCLogger. + +## Valid log levels by order of granularity (E, W, I, D, T) +enum { + LOG_ERROR, + LOG_WARNING, + LOG_INFO, + LOG_DEBUG, + LOG_TRACE +} + + +## A map of log level names to log level ints +const LEVEL_MAP: Dictionary = { + "ERROR": LOG_ERROR, + "WARNING": LOG_WARNING, + "INFO": LOG_INFO, + "DEBUG": LOG_DEBUG, + "TRACE": LOG_TRACE, +} + +## Static function to determine the int log level value defined in Project Settings (Escoria>Debug>Log Level)[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns a `int` value. (`int`) +static func determine_escoria_log_level() -> int: + return LEVEL_MAP[ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.LOG_LEVEL + ).to_upper()] diff --git a/addons/escoria-core/tools/logging/esc_log_level.gd.uid b/addons/escoria-core/tools/logging/esc_log_level.gd.uid new file mode 100644 index 0000000..d3fee64 --- /dev/null +++ b/addons/escoria-core/tools/logging/esc_log_level.gd.uid @@ -0,0 +1 @@ +uid://cfnhsqcchmqbd diff --git a/addons/escoria-core/tools/logging/esc_logger.gd b/addons/escoria-core/tools/logging/esc_logger.gd new file mode 100644 index 0000000..26d8d86 --- /dev/null +++ b/addons/escoria-core/tools/logging/esc_logger.gd @@ -0,0 +1,462 @@ +## Base class of all logger types. +class ESCLoggerBase: + ## Signal sent when Escoria requires performing an emergency savegame.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + signal perform_emergency_savegame + + ## Signal sent when an error or warning happened.[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |message|`Variant`|Error or warning message emitted through the logger.|yes|[br] + ## [br] + signal error_message_signal(message) + + ## Log filename format + const LOG_FILE_FORMAT: String = "log_%s_%s.log" + + # Configured log level + var _log_level: int + + ## If true, assert() functions will not be called, thus the program won't + ## exit or error. Resets to false after an assert() call was ignored once. + ## Useful for console calls. + var dont_assert: bool = false + + + # Constructor + func _init(): + _log_level = ESCLogLevel.determine_escoria_log_level() + + ## Formats a message depending on context, message and letter, then returns it. The formatted string[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |context|`String`|usually, the escoria file that sent the message.|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## |letter|`String`|letter to add to the formatted log (I for Info, W for Warning...)|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns a `String` value. (`String`) + func formatted_message(context: String, msg: String, letter: String) -> String: + return "ESC ({0}) {1} {2}: {3}".format([_formatted_date(), letter, context, msg]) + + ## Trace log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func trace(owner: Object, msg: String): + var context: String = owner.get_script().resource_path.get_file() + _trace_message(context, msg) + + # Direct message trace log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _trace_message(context: String, msg: String): + print(formatted_message(context, msg, "T")) + + ## Debug log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func debug(owner: Object, msg: String): + var context: String = owner.get_script().resource_path.get_file() + _debug_message(context, msg) + + # Static debug log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _debug_message(context: String, msg: String): + print(formatted_message(context, msg, "D")) + + ## Info log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func info(owner: Object, msg: String): + var context: String = owner.get_script().resource_path.get_file() + _info_message(context, msg) + + # Static info log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _info_message(context: String, msg: String): + print(formatted_message(context, msg, "I")) + + ## Warning log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func warn(owner: Object, msg: String): + var context: String = owner.get_script().resource_path.get_file() + _warn_message(context, msg) + + # Static warning log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _warn_message(context: String, msg: String): + print(formatted_message(context, msg, "W")) + push_warning(formatted_message(context, msg, "W")) + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TERMINATE_ON_WARNINGS + ): + if not dont_assert: + assert(false) + escoria.get_tree().quit() + dont_assert = false + error_message_signal.emit(msg) + + ## Error log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func error(owner: Object, msg: String): + var context = owner.get_script().resource_path.get_file() + _error_message(context, msg) + + # Static error log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _error_message(context: String, msg: String): + printerr(formatted_message(context, msg, "E")) + push_error(formatted_message(context, msg, "E")) + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TERMINATE_ON_ERRORS + ): + if not dont_assert: + assert(false) + escoria.get_tree().quit() + dont_assert = false + error_message_signal.emit(msg) + + # Formats the current system's datetime as 'YYYY-mm-ddTHH:MM:SS'. + func _formatted_date() -> String: + var info = Time.get_datetime_dict_from_system() + info["year"] = "%04d" % info["year"] + info["month"] = "%02d" % info["month"] + info["day"] = "%02d" % info["day"] + info["hour"] = "%02d" % info["hour"] + info["minute"] = "%02d" % info["minute"] + info["second"] = "%02d" % info["second"] + return "{year}-{month}-{day}T{hour}:{minute}:{second}".format(info) + + +## A logger that logs to the terminal and to a log file. +class ESCLoggerFile extends ESCLoggerBase: + ## Log file handler + var log_file: FileAccess + + # Constructor + func _init(): + super() + # This is left alone as this constructor is called from escoria.gd's own + # constructor + var log_file_path = ProjectSettings.get_setting( + ESCProjectSettingsManager.LOG_FILE_PATH + ) + var date = Time.get_datetime_dict_from_system() + log_file_path = log_file_path.path_join(LOG_FILE_FORMAT % [ + str(date["year"]) + str(date["month"]) + str(date["day"]), + str(date["hour"]) + str(date["minute"]) + str(date["second"]) + ]) + log_file = FileAccess.open( + log_file_path, + FileAccess.WRITE + ) + + ## Trace log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func trace(owner: Object, msg: String): + if _log_level >= ESCLogLevel.LOG_TRACE: + _log_to_file(owner, msg, "T") + super.trace(owner, msg) + + # Direct message trace log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _trace_message(context: String, msg: String): + if _log_level >= ESCLogLevel.LOG_TRACE: + _log_to_file_message(context, msg, "T") + super._trace_message(context, msg) + + ## Debug log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func debug(owner: Object, msg: String): + if _log_level >= ESCLogLevel.LOG_DEBUG: + _log_to_file(owner, msg, "D") + super.debug(owner, msg) + + # Static debug log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _debug_message(context: String, msg: String): + if _log_level >= ESCLogLevel.LOG_DEBUG: + _log_to_file_message(context, msg, "D") + super._debug_message(context, msg) + + ## Info log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func info(owner: Object, msg: String): + if _log_level >= ESCLogLevel.LOG_INFO: + _log_to_file(owner, msg, "I") + super.info(owner, msg) + + # Static info log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _info_message(context: String, msg: String): + if _log_level >= ESCLogLevel.LOG_INFO: + _log_to_file_message(context, msg, "I") + super._info_message(context, msg) + + ## Warning log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func warn(owner: Object, msg: String): + if _log_level >= ESCLogLevel.LOG_WARNING: + _log_to_file(owner, msg, "W") + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TERMINATE_ON_WARNINGS + ): + _log_stack_trace_to_file() + print_stack() + close_logs() + super.warn(owner, msg) + + # Static warning log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _warn_message(context: String, msg: String): + if _log_level >= ESCLogLevel.LOG_WARNING: + _log_to_file_message(context, msg, "W") + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TERMINATE_ON_WARNINGS + ): + _log_stack_trace_to_file() + print_stack() + close_logs() + super._warn_message(context, msg) + + ## Error log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func error(owner: Object, msg: String): + if _log_level >= ESCLogLevel.LOG_ERROR: + _log_to_file(owner, msg, "E") + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TERMINATE_ON_ERRORS + ): + _log_stack_trace_to_file() + print_stack() + close_logs() + super.error(owner, msg) + + # Static error log (requiring a string for the context) + # + # #### PARAMETERS + # + # - owner: caller as a string value + # - msg: logged message. + func _error_message(context: String, msg: String): + if _log_level >= ESCLogLevel.LOG_ERROR: + _log_to_file_message(context, msg, "E") + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.TERMINATE_ON_ERRORS + ): + _log_stack_trace_to_file() + print_stack() + close_logs() + super._error_message(context, msg) + + + ## Close the log file cleanly[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## None. + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func close_logs(): + print("Closing logs peacefully.") + _log_line_to_file("Closing logs peacefully.") + log_file.close() + + # Log the log message and context to file + func _log_to_file(owner: Object, msg: String, letter: String): + var context: String + if owner != null: + context = owner.get_script().resource_path.get_file() + _log_to_file_message(context, msg, letter) + + # Log the log message and context as string to file + func _log_to_file_message(context: String, msg: String, letter: String): + if log_file.is_open(): + log_file.store_string(formatted_message(context, msg, letter) + "\n") + + # Log the message line to file + func _log_line_to_file(msg: String): + if log_file.is_open(): + log_file.store_string(msg + "\n") + + # Log the stack trace to file + func _log_stack_trace_to_file(): + var frame_number = 0 + for stack in get_stack().slice(2, get_stack().size()): + _log_line_to_file( + "Frame %s - %s:%s in function '%s'" % [ + str(frame_number), + stack["source"], + stack["line"], + stack["function"], + ] + ) + frame_number += 1 + + +## A simple logger that logs to terminal using debug() function +class ESCLoggerVerbose extends ESCLoggerBase: + # Constructor + func _init(): + pass + + ## Debug log[br] + ## [br] + ## #### Parameters[br] + ## [br] + ## | Name | Type | Description | Required? |[br] + ## |:-----|:-----|:------------|:----------|[br] + ## |owner|`Object`|caller object (usually, `self`)|yes|[br] + ## |msg|`String`|logged message.|yes|[br] + ## [br] + ## #### Returns[br] + ## [br] + ## Returns nothing. + func debug(owner: Object, msg: String): + var context = owner.get_script().resource_path.get_file() + print(context, ": ", msg) diff --git a/addons/escoria-core/tools/logging/esc_logger.gd.uid b/addons/escoria-core/tools/logging/esc_logger.gd.uid new file mode 100644 index 0000000..be8df74 --- /dev/null +++ b/addons/escoria-core/tools/logging/esc_logger.gd.uid @@ -0,0 +1 @@ +uid://3rkr84aiwrdt diff --git a/addons/escoria-core/tools/logging/esc_safe_logging.gd b/addons/escoria-core/tools/logging/esc_safe_logging.gd new file mode 100644 index 0000000..392d71b --- /dev/null +++ b/addons/escoria-core/tools/logging/esc_safe_logging.gd @@ -0,0 +1,60 @@ +# This static class is primarily for situations where there's a possibility that logging to the +# console may be done by a tool script, and so won't have access to the autoloader/singleton that is +# `escoria`. +class_name ESCSafeLogging + + +const COLOUR_RED = "red" +const COLOUR_GREEN = "green" +const COLOUR_YELLOW = "yellow" + + +static func log_level() -> String: + return ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.LOG_LEVEL + ).to_upper() + + +static func log_error(owner: Object, message: String) -> void: + if Engine.is_editor_hint(): + print_rich("[color=%s]%s[/color]" % [COLOUR_RED, message]) + else: + escoria.logger.error(owner, message) + + +static func log_warn(owner: Object, message: String) -> void: + if Engine.is_editor_hint(): + print_rich("[color=%s]%s[/color]" % [COLOUR_YELLOW, message]) + else: + escoria.logger.warn(owner, message) + + +static func log_info(owner: Object, message: String) -> void: + if Engine.is_editor_hint(): + print(message) + else: + escoria.logger.info(owner, message) + + +static func log_debug(owner: Object, message: String) -> void: + if Engine.is_editor_hint(): + print(message) + else: + escoria.logger.debug(owner, message) + + +static func log_trace(owner: Object, message: String) -> void: + if Engine.is_editor_hint(): + print(message) + else: + escoria.logger.trace(owner, message) + + +# Doesn't correpond to a logging level for offline logging; meant more for messages conveying a result +static func log_result(owner: Object, message: String, is_successful_result: bool) -> void: + var colour: String = COLOUR_GREEN if is_successful_result else COLOUR_RED + + if Engine.is_editor_hint(): + print_rich("[color=%s]%s[/color]" % [colour, message]) + else: + escoria.logger.info(owner, message) diff --git a/addons/escoria-core/tools/logging/esc_safe_logging.gd.uid b/addons/escoria-core/tools/logging/esc_safe_logging.gd.uid new file mode 100644 index 0000000..4e08a9a --- /dev/null +++ b/addons/escoria-core/tools/logging/esc_safe_logging.gd.uid @@ -0,0 +1 @@ +uid://cqexq6xpcccac diff --git a/addons/escoria-core/tools/script_analysis/analyzers/esc_exit_scene_script_analyzer.gd b/addons/escoria-core/tools/script_analysis/analyzers/esc_exit_scene_script_analyzer.gd new file mode 100644 index 0000000..3ed0025 --- /dev/null +++ b/addons/escoria-core/tools/script_analysis/analyzers/esc_exit_scene_script_analyzer.gd @@ -0,0 +1,151 @@ +extends ESCScriptAnalyzer +class_name ESCExitSceneScriptAnalyzer + + +const EXIT_SCENE_EVENT_NAME = "exit_scene" +const ACCEPT_INPUT_DISABLE_ARGS = ["none", "skip"] + +const CHANGE_SCENE_MISSING_MESSAGE = "Event ':exit_scene' is missing 'change_scene' command. Scene might not change as expected. Ignore this warning if the command was omitted on purpose." + +const COMMANDS_AFTER_CHANGE_SCENE_MESSAGE = "Event ':exit_scene' may have commands that are expected to be run after a call to 'change_scene'. Such commands will not be executed." + +const MISSING_ACCEPT_INPUT_MESSAGE = \ + "Event ':exit_scene' may allow for the player character to move while exiting and changing the scene." \ + + " To prevent this, ensure a call to 'accept_input' with an argument evaluating to 'NONE' or 'SKIP' is" \ + + " made before any calls to 'transition' and/or 'change_scene'." + +const BULLET_CHARACTER = "- " + + +# These are vars because they have to be, but should be treated as consts +var TRANSITION_COMMAND_NAME = TransitionCommand.new().get_command_name() +var CHANGE_SCENE_COMMAND_NAME = ChangeSceneCommand.new().get_command_name() +var ACCEPT_INPUT_COMMAND_NAME = AcceptInputCommand.new().get_command_name() + +var _has_change_scene_command: bool = false + +var _has_commands_after_change_scene_command: bool = false + +var _change_scene_token: ESCToken = null + +var _accept_input_disable_missing: bool = false + +var _accept_input_token: ESCToken = null + + +func analyze(statements: Array) -> void: + if not statements is Array: + statements = [statements] + + for statement in statements: + if statement.get_event_name().to_lower() == EXIT_SCENE_EVENT_NAME: + _execute(statement) + + _add_warning_messages() + + break + + +func _add_warning_messages() -> void: + var messages: Array[String] = [] + + if not _has_change_scene_command: + messages.append(BULLET_CHARACTER + CHANGE_SCENE_MISSING_MESSAGE) + + if _accept_input_disable_missing: + messages.append(BULLET_CHARACTER + MISSING_ACCEPT_INPUT_MESSAGE) + + if _has_commands_after_change_scene_command: + messages.append(BULLET_CHARACTER + COMMANDS_AFTER_CHANGE_SCENE_MESSAGE) + + if not messages.is_empty(): + _rich_messages.append(ESCSafeLogging.log_warn.bind(self, "\n".join(messages))) + + +func _execute_block(statements: Array, env: ESCEnvironment): + var previous_env = _environment + var ret = null + + _environment = env + + for stmt in statements: + ret = _execute(stmt) + + _environment = previous_env + + return ret + + +# Visitor overriding +func visit_call_expr(expr: ESCGrammarExprs.Call): + var callee = _evaluate(expr.get_callee()) + + var args: Array = [] + + for arg in expr.get_arguments(): + arg = _evaluate(arg) + + # "Adapter" for current ESC commands since they don't currently take + # ESCObject's (or ESCRoom's) as arguments. + if arg is ESCObject or arg is ESCRoom: + arg = arg.global_id + + args.append(arg) + + var command_name: String = _get_command_name(callee) + + _has_commands_after_change_scene_command = _has_change_scene_command_in_scope() + + if command_name == CHANGE_SCENE_COMMAND_NAME: + _has_change_scene_command = true + + _change_scene_token = expr.get_paren_token() + _environment.define(_change_scene_token.get_lexeme(), true) + + # First arg is always the path of the scene to transition to. + # Second arg determines whether the transition is automatic, and defaults to true + if args.size() > 1: + var is_auto_transition = args[1] as bool + + if is_auto_transition != null and not is_auto_transition: + _check_for_missing_accept_input_disable() + elif command_name == TRANSITION_COMMAND_NAME: + _check_for_missing_accept_input_disable() + elif command_name == ACCEPT_INPUT_COMMAND_NAME: + var first_arg = args[0] as String + + if first_arg != null and first_arg.to_lower() in ACCEPT_INPUT_DISABLE_ARGS: + _accept_input_token = expr.get_paren_token() + _environment.define(_accept_input_token.get_lexeme(), true) + + return ESCExecution.RC_OK + + +# as a key in the environment/scope to serve as a marker to check concerning whether `accept_input` +# has previously been called with the appropriate argument +func _check_for_missing_accept_input_disable() -> void: + if _accept_input_token == null: + _accept_input_disable_missing = true + return + + _accept_input_disable_missing = \ + not _environment.is_valid_key(_accept_input_token) \ + or not _environment.get_value(_accept_input_token) + + +func _has_change_scene_command_in_scope() -> bool: + if _change_scene_token == null: + return false + + return \ + _environment.is_valid_key(_change_scene_token) \ + and _environment.get_value(_change_scene_token) + + +func _get_command_name(callee) -> String: + # if we don't have a command to run, check against any built-in functions and, + # if one is found and is executed, we're done + if callee is ESCBaseCommand: + return callee.get_command_name() + + return callee diff --git a/addons/escoria-core/tools/script_analysis/analyzers/esc_exit_scene_script_analyzer.gd.uid b/addons/escoria-core/tools/script_analysis/analyzers/esc_exit_scene_script_analyzer.gd.uid new file mode 100644 index 0000000..902e168 --- /dev/null +++ b/addons/escoria-core/tools/script_analysis/analyzers/esc_exit_scene_script_analyzer.gd.uid @@ -0,0 +1 @@ +uid://0jmaf8d0mjpe diff --git a/addons/escoria-core/tools/script_analysis/analyzers/esc_script_analyzer.gd b/addons/escoria-core/tools/script_analysis/analyzers/esc_script_analyzer.gd new file mode 100644 index 0000000..874ffa1 --- /dev/null +++ b/addons/escoria-core/tools/script_analysis/analyzers/esc_script_analyzer.gd @@ -0,0 +1,387 @@ +extends RefCounted +class_name ESCScriptAnalyzer + + +const CURRENT_PLAYER_KEYWORD = "CURRENT_PLAYER" + + +var _rich_messages: Array[Callable] = [] + +var _globals: ESCEnvironment +var _environment: ESCEnvironment = _globals + +var _locals: Dictionary = {} + +var _builtin_functions: Array = [ + "print" +] + + +# This must be implemented in child class. +func analyze(statements: Array) -> void: + pass + + +func print_messages() -> void: + for print_method in _rich_messages: + print_method.call() + + +func _init(): + var globals: Dictionary = ESCCompiler.load_globals() + + _globals = ESCEnvironment.new() + + for callable in ESCCompiler.load_commands(): + _globals.define(callable.get_command_name(), callable) + + for key in ESCCompiler.load_globals().keys(): + _globals.define(key, globals[key]) + + +# Visitor implementations +func visit_block_stmt(stmt: ESCGrammarStmts.Block): + var env: ESCEnvironment = ESCEnvironment.new() + env.init(_environment) + + return _execute_block(stmt.get_statements(), env) + + +func visit_event_stmt(stmt: ESCGrammarStmts.Event): + _execute(stmt.get_body()) + + +func visit_expression_stmt(stmt: ESCGrammarStmts.ESCExpression): + return _evaluate(stmt.get_expression()) + + +func visit_call_expr(expr: ESCGrammarExprs.Call): + var callee = _evaluate(expr.get_callee()) + + var args: Array = [] + + for arg in expr.get_arguments(): + arg = _evaluate(arg) + + # "Adapter" for current ESC commands since they don't currently take + # ESCObject's (or ESCRoom's) as arguments. + if arg is ESCObject or arg is ESCRoom: + arg = arg.global_id + + args.append(arg) + + # if we don't have a command to run, check against any built-in functions and, + # if one is found and is executed, we're done + if not callee is ESCBaseCommand: + if not callee in _builtin_functions: + return 0 + else: + return _handle_builtin_function(callee, args) + + return ESCExecution.RC_OK + + +func _handle_builtin_function(fn_name: String, args: Array) -> int: + var rc = ESCExecution.RC_ERROR + + match fn_name: + 'print': + if args.size() > 1: + ESCSafeLogging.log_warn( + self, + "'print' only takes one argument" + ) + + rc = ESCExecution.RC_OK + + return rc + + +func visit_if_stmt(stmt: ESCGrammarStmts.If): + _execute(stmt.get_then_branch()) + + for branch in stmt.get_elif_branches(): + _execute(branch) + + if stmt.get_else_branch(): + _execute(stmt.get_else_branch()) + + return null + + +func visit_while_stmt(stmt: ESCGrammarStmts.While): + _execute(stmt.get_body()) + + return null + + +func visit_pass_stmt(stmt: ESCGrammarStmts.Pass): + pass + + +func visit_stop_stmt(stmt: ESCGrammarStmts.Stop): + return stmt + + +func visit_var_stmt(stmt: ESCGrammarStmts.Var): + var value = null + + if stmt.get_initializer(): + value = _evaluate(stmt.get_initializer()) + + _environment.define(stmt.get_name().get_lexeme(), value) + return null + + +func visit_global_stmt(stmt: ESCGrammarStmts.Global): + var value = null + + if stmt.get_initializer(): + value = _evaluate(stmt.get_initializer()) + + # Only define the global if we haven't already done so; otherwise, just + # ignore it + if not _globals.get_values().has(stmt.get_name().get_lexeme()): + _globals.define(stmt.get_name().get_lexeme(), value) + + return null + + +func visit_dialog_stmt(stmt: ESCGrammarStmts.Dialog): + return null + + +func visit_dialog_option_stmt(stmt: ESCGrammarStmts.DialogOption): + pass + + +func visit_break_stmt(stmt: ESCGrammarStmts.Break): + return stmt + + +func visit_done_stmt(stmt: ESCGrammarStmts.Done): + return stmt + + +func visit_assign_expr(expr: ESCGrammarExprs.Assign): + var value = _evaluate(expr.get_value()) + + var distance: int = _locals.get(expr, -1) + + return value + + +func visit_in_inventory_expr(expr: ESCGrammarExprs.InInventory): + var arg = _evaluate(expr.get_identifier()) + + if arg is ESCObject: + arg = arg.global_id + + return null + + +func visit_is_expr(expr: ESCGrammarExprs.Is): + var arg = _evaluate(expr.get_identifier()) + + return true + + +func visit_binary_expr(expr: ESCGrammarExprs.Binary): + var left_part = _evaluate(expr.get_left()) + var right_part = _evaluate(expr.get_right()) + + match expr.get_operator().get_type(): + ESCTokenType.TokenType.EQUAL_EQUAL: + return _is_equal(left_part, right_part) + ESCTokenType.TokenType.BANG_EQUAL: + return not _is_equal(left_part, right_part) + ESCTokenType.TokenType.GREATER: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part > right_part + ESCTokenType.TokenType.GREATER_EQUAL: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part >= right_part + ESCTokenType.TokenType.LESS: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part < right_part + ESCTokenType.TokenType.LESS_EQUAL: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part <= right_part + ESCTokenType.TokenType.MINUS: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part - right_part + ESCTokenType.TokenType.PLUS: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part, false) + + if check: + return left_part + right_part + + check = _check_at_least_one_string(left_part, right_part) + + if check: + return str(left_part) + str(right_part) + + ESCSafeLogging.log_warn( + self, + "%s: Operands must be numbers or strings." % expr.get_operator().get_lexeme() + ) + ESCTokenType.TokenType.SLASH: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part / right_part + ESCTokenType.TokenType.STAR: + var check = _check_are_numbers(left_part, expr.get_operator(), right_part) + return left_part * right_part + + return null + + +func visit_unary_expr(expr: ESCGrammarExprs.Unary): + var right_part = _evaluate(expr.get_right()) + + match expr.get_operator().get_type(): + ESCTokenType.TokenType.BANG, ESCTokenType.TokenType.NOT: + return not _is_truthy(right_part) + ESCTokenType.TokenType.MINUS: + var check = _check_is_number(right_part, expr.get_operator()) + return -right_part + + return null + + +func visit_variable_expr(expr: ESCGrammarExprs.Variable): + if expr.get_name().get_lexeme().begins_with("$"): + return _look_up_object(expr.get_name()) + + if expr.get_name().get_lexeme() in _builtin_functions: + return expr.get_name().get_lexeme() + + return look_up_variable(expr.get_name(), expr) + + +func visit_literal_expr(expr: ESCGrammarExprs.Literal): + return expr.get_value() + + +func visit_logical_expr(expr: ESCGrammarExprs.Logical): + var left = _evaluate(expr.get_left()) + + if expr.get_operator().get_type() == ESCTokenType.TokenType.OR: + if _is_truthy(left): + return left + else: + if not _is_truthy(left): + return left + + return _evaluate(expr.get_right()) + + +func visit_grouping_expr(expr: ESCGrammarExprs.Grouping): + return _evaluate(expr.get_expression()) + + +func resolve(expr: ESCGrammarExpr, depth: int): + _locals[expr] = depth + + +# Private methods +func _look_up_object(name: ESCToken): + var global_id: String = name.get_lexeme().substr(1) + + if global_id.to_upper() == CURRENT_PLAYER_KEYWORD: + pass + + return _look_up_object_by_global_id(global_id) + + +# We have no way of knowing what the value will actually be since this is called during static +# analysis. As such, we'll just return null. +func _look_up_object_by_global_id(global_id: String): + return null + + +# We don't care about the values of variables for static analysis purposes. +func look_up_variable(name: ESCToken, expr: ESCGrammarExpr): + var distance: int = _locals[expr] if _locals.has(expr) else -1 + + if distance == -1: + return _globals.get_value(name) + else: + return _environment.get_at(distance, name.get_lexeme()) + + +func _evaluate(expr: ESCGrammarExpr): + var ret = expr.accept(self) + + # TODO: Error handling + return ret + + +func _execute(stmt: ESCGrammarStmt): + var ret = stmt.accept(self) + + # TODO: Error handling + return ret + + +func _execute_block(statements: Array, env: ESCEnvironment): + var previous_env = _environment + var ret = null + + _environment = env + + for stmt in statements: + ret = _execute(stmt) + + _environment = previous_env + + return ret + + +func _is_truthy(value) -> bool: + if value == null: + return false + + if typeof(value) in [TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_BOOL]: + return bool(value) == true + + return false + + +func _is_equal(left_part, right_part) -> bool: + if not left_part and not right_part: + return true + + if not left_part: + return false + + return left_part == right_part + + +func _check_are_numbers(value_1, operator: ESCToken, value_2, strict: bool = true): + if typeof(value_1) in [TYPE_INT, TYPE_FLOAT] and typeof(value_2) in [TYPE_INT, TYPE_FLOAT]: + return true + + if strict: + ESCSafeLogging.log_warn( + self, + "%s: Operands must be numbers." % operator.get_lexeme() + ) + + return false + + +func _check_is_number(value, operator: ESCToken, strict: bool = true): + if typeof(value) in [TYPE_INT, TYPE_FLOAT]: + return true + + if strict: + ESCSafeLogging.log_warn( + self, + "%s: Operand must be number." % operator.get_lexeme() + ) + + return false + + +func _check_at_least_one_string(value_1, value_2): + return typeof(value_1) == TYPE_STRING || typeof(value_2) == TYPE_STRING diff --git a/addons/escoria-core/tools/script_analysis/analyzers/esc_script_analyzer.gd.uid b/addons/escoria-core/tools/script_analysis/analyzers/esc_script_analyzer.gd.uid new file mode 100644 index 0000000..4ee126f --- /dev/null +++ b/addons/escoria-core/tools/script_analysis/analyzers/esc_script_analyzer.gd.uid @@ -0,0 +1 @@ +uid://cv8ptpeiod6g2 diff --git a/addons/escoria-core/tools/script_analysis/esc_ashes_analyzer.gd b/addons/escoria-core/tools/script_analysis/esc_ashes_analyzer.gd new file mode 100644 index 0000000..2447a87 --- /dev/null +++ b/addons/escoria-core/tools/script_analysis/esc_ashes_analyzer.gd @@ -0,0 +1,52 @@ +@tool +extends Node +class_name ESCAshesAnalyzer + + +# TODO: Update this when we move from the .esc extension for Escoria scripts to .ash +const FILE_EXTENSION_ASHES = "esc" +const DIRECTORIES_TO_EXCLUDE = ["addons"] +const BASE_PROJECT_DIR = "res://" + + +var _compiler: ESCCompiler = ESCCompiler.new() + + +func analyze() -> void: + var files: Array[String] = _get_script_files_recursive(BASE_PROJECT_DIR) + + files.sort() + + for file in files: + _compiler.load_esc_file(file) + + +func _get_script_files_recursive(path: String, files: Array[String] = []) -> Array[String]: + var dir = DirAccess.open(path) + + if dir: + dir.list_dir_begin() + else: + ESCSafeLogging.log_warn(self, "Unable to open '%s'." % path) + return files + + var filename: String = dir.get_next() + + while not filename.is_empty(): + var filename_with_path: String = _make_full_path(dir.get_current_dir(), filename) + + if dir.current_is_dir() and not filename in DIRECTORIES_TO_EXCLUDE: + files = _get_script_files_recursive(filename_with_path, files) + else: + if filename.get_extension() == FILE_EXTENSION_ASHES: + files.append(filename_with_path) + + filename = dir.get_next() + + return files + + +func _make_full_path(directory: String, filename: String) -> String: + var separator: String = "" if directory.ends_with("/") else "/" + + return directory + separator + filename diff --git a/addons/escoria-core/tools/script_analysis/esc_ashes_analyzer.gd.uid b/addons/escoria-core/tools/script_analysis/esc_ashes_analyzer.gd.uid new file mode 100644 index 0000000..207977b --- /dev/null +++ b/addons/escoria-core/tools/script_analysis/esc_ashes_analyzer.gd.uid @@ -0,0 +1 @@ +uid://cs8hmitl4q8ec diff --git a/addons/escoria-core/tools/script_analysis/esc_static_analyzers.gd b/addons/escoria-core/tools/script_analysis/esc_static_analyzers.gd new file mode 100644 index 0000000..53aa7a9 --- /dev/null +++ b/addons/escoria-core/tools/script_analysis/esc_static_analyzers.gd @@ -0,0 +1,19 @@ +extends RefCounted +class_name ESCStaticAnalyzers + + +var _analyzers = [ + ESCExitSceneScriptAnalyzer.new() +] + +var _parsed_statements = [] + + +func _init(parsed_statements: Array) -> void: + _parsed_statements = parsed_statements + + +func run() -> void: + for analyzer in _analyzers: + analyzer.analyze(_parsed_statements) + analyzer.print_messages() diff --git a/addons/escoria-core/tools/script_analysis/esc_static_analyzers.gd.uid b/addons/escoria-core/tools/script_analysis/esc_static_analyzers.gd.uid new file mode 100644 index 0000000..edbcd9e --- /dev/null +++ b/addons/escoria-core/tools/script_analysis/esc_static_analyzers.gd.uid @@ -0,0 +1 @@ +uid://djdivn8o6k6aq diff --git a/addons/escoria-core/ui_library/fonts/caslonantique.tres b/addons/escoria-core/ui_library/fonts/caslonantique.tres new file mode 100644 index 0000000..19bcb83 --- /dev/null +++ b/addons/escoria-core/ui_library/fonts/caslonantique.tres @@ -0,0 +1,7 @@ +[gd_resource type="FontFile" load_steps=2 format=2] + +[ext_resource path="res://addons/escoria-core/ui_library/fonts/caslonantique.ttf" type="FontFile" id=1] + +[resource] +size = 21 +font_data = ExtResource( 1 ) diff --git a/addons/escoria-core/ui_library/fonts/caslonantique.ttf b/addons/escoria-core/ui_library/fonts/caslonantique.ttf new file mode 100644 index 0000000..eec6f63 Binary files /dev/null and b/addons/escoria-core/ui_library/fonts/caslonantique.ttf differ diff --git a/addons/escoria-core/ui_library/fonts/caslonantique.ttf.import b/addons/escoria-core/ui_library/fonts/caslonantique.ttf.import new file mode 100644 index 0000000..b23d333 --- /dev/null +++ b/addons/escoria-core/ui_library/fonts/caslonantique.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://dmfspabef0y2f" +path="res://.godot/imported/caslonantique.ttf-d88d3fe7dc88ba83bb382c882d7923fe.fontdata" + +[deps] + +source_file="res://addons/escoria-core/ui_library/fonts/caslonantique.ttf" +dest_files=["res://.godot/imported/caslonantique.ttf-d88d3fe7dc88ba83bb382c882d7923fe.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd b/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd new file mode 100644 index 0000000..b109c38 --- /dev/null +++ b/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd @@ -0,0 +1,178 @@ +## The inventory representation of an ESC item if pickable (only used by +## the inventory components) +extends TextureButton +class_name ESCInventoryButton + +## Signal emitted when the item was left clicked.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_id|`Variant`|Global ID of the clicked item|yes|[br] +## [br] +signal mouse_left_inventory_item(item_id) + +## Signal emitted when the item was right clicked.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_id|`Variant`|Global ID of the clicked item|yes|[br] +## [br] +signal mouse_right_inventory_item(item_id) + +## Signal emitted when the item was double clicked.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_id|`Variant`|Global ID of the clicked item|yes|[br] +## [br] +signal mouse_double_left_inventory_item(item_id) + +## Signal emitted when the item was focused.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_id|`Variant`|Global ID of the clicked item|yes|[br] +## [br] +signal inventory_item_focused(item_id) + +## Signal emitted when the item is not focused anymore.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +signal inventory_item_unfocused() + +## Global ID of the ESCItem that uses this ESCInventoryItem. +var global_id: String = "" + + + +## Initializes the inventory button with the given item.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |p_item|`ESCInventoryItem`|The ESCInventoryItem to represent.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _init(p_item: ESCInventoryItem) -> void: + global_id = p_item.global_id + texture_normal = p_item.texture_normal + texture_hover = p_item.texture_hovered + stretch_mode = TextureButton.STRETCH_KEEP_ASPECT + + + +## Updates the size of the inventory button every frame.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |_delta|`float`|The frame time delta (unused).|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _process(_delta: float) -> void: + size = ProjectSettings.get_setting("escoria/ui/inventory_item_size") + custom_minimum_size = ProjectSettings.get_setting( + "escoria/ui/inventory_item_size" + ) + + +## Connects input handlers for the inventory button.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _ready(): + gui_input.connect(_on_inventory_item_gui_input) + mouse_entered.connect(_on_inventory_item_mouse_enter) + mouse_exited.connect(_on_inventory_item_mouse_exit) + + + +## Handles the gui input and emits the respective signals.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |event|`InputEvent`|The event received.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_inventory_item_gui_input(event: InputEvent): + if event is InputEventMouseButton and event.is_pressed(): + if event.button_index == MOUSE_BUTTON_WHEEL_UP: + escoria.inputs_manager._on_mousewheel_action(-1) + get_viewport().set_input_as_handled() + elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN: + escoria.inputs_manager._on_mousewheel_action(1) + get_viewport().set_input_as_handled() + elif event.double_click: + if event.button_index == MOUSE_BUTTON_LEFT: + mouse_double_left_inventory_item.emit( + global_id, + event + ) + # Make sure fast right clicks in the inventory aren't ignored + elif event.button_index == MOUSE_BUTTON_RIGHT: + mouse_right_inventory_item.emit( + global_id, + event + ) + else: + if event.button_index == MOUSE_BUTTON_LEFT: + mouse_left_inventory_item.emit( + global_id, + event + ) + if event.button_index == MOUSE_BUTTON_RIGHT: + mouse_right_inventory_item.emit( + global_id, + event + ) + + +## Handles mouse entering the item and sends the respective signal.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_inventory_item_mouse_enter(): + inventory_item_focused.emit(global_id) + +## Handles mouse leaving the item and sends the respective signal.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_inventory_item_mouse_exit(): + inventory_item_unfocused.emit() diff --git a/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd.uid b/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd.uid new file mode 100644 index 0000000..3fdb883 --- /dev/null +++ b/addons/escoria-core/ui_library/inventory/esc_inventory_button.gd.uid @@ -0,0 +1 @@ +uid://c1hfi1dlwh3hq diff --git a/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd b/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd new file mode 100644 index 0000000..1e6dab8 --- /dev/null +++ b/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd @@ -0,0 +1,102 @@ + +## Inventory container handler that acts as a base for UIs inventory containers. +extends Control +class_name ESCInventoryContainer + +## Whether any item in the container is currently focused.[br] +var item_focused: bool = false + +## Whether the inventory container currently is empty.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns whether the inventory container currently is empty. Whether the container is empty or not. (`bool`) +func is_empty() -> bool: + return get_child_count() > 0 + +## Adds a new item into the container and returns the control generated for it so its events can be handled by the inputs manager.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item|`ESCInventoryItem`|Item to add.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns the button generated for the item. (`ESCInventoryButton`) +func add_item(inventory_item: ESCInventoryItem) -> ESCInventoryButton: + var button = ESCInventoryButton.new(inventory_item) + button.inventory_item_focused.connect(_on_inventory_item_focused) + button.inventory_item_unfocused.connect(_on_inventory_item_unfocused) + add_child(button) + return button + +## Removes an item from the container.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item|`ESCInventoryItem`|Item to remove.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func remove_item(inventory_item: ESCInventoryItem): + for c in get_children(): + if c is ESCInventoryButton and c.global_id == inventory_item.global_id: + remove_child(c) + c.queue_free() + + +## An Inventory button from the container, using an ESCInventoryItem.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |inventory_item|`ESCInventoryItem`|Inventory item to return the button node from.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns an Inventory button from the container, using an ESCInventoryItem. The inventory button node, or null if not found. (`ESCInventoryButton`) +func get_inventory_button(inventory_item: ESCInventoryItem) -> ESCInventoryButton: + var inventory_button = null + for c in get_children(): + if c.global_id == inventory_item.global_id: + inventory_button = c + break + return inventory_button + + +## Called when an inventory item is focused.[br] +## [br] +## #### Parameters[br] +## [br] +## | Name | Type | Description | Required? |[br] +## |:-----|:-----|:------------|:----------|[br] +## |item_id|`Variant`|The global ID of the focused item.|yes|[br] +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_inventory_item_focused(item_id): + item_focused = true + + +## Called when an inventory item is unfocused.[br] +## [br] +## #### Parameters[br] +## [br] +## None. +## [br] +## #### Returns[br] +## [br] +## Returns nothing. +func _on_inventory_item_unfocused(): + item_focused = false diff --git a/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd.uid b/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd.uid new file mode 100644 index 0000000..1ad5d75 --- /dev/null +++ b/addons/escoria-core/ui_library/inventory/esc_inventory_container.gd.uid @@ -0,0 +1 @@ +uid://c4syt26p7mg66 diff --git a/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd b/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd new file mode 100644 index 0000000..975076f --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd @@ -0,0 +1,11 @@ +class_name SaveGamesSorter + +static func sort_by_date_descending(a, b): + if Time.get_unix_time_from_datetime_dict(a.date) > Time.get_unix_time_from_datetime_dict(b.date): + return true + return false + +static func sort_by_date_ascending(a, b): + if Time.get_unix_time_from_datetime_dict(a.date) < Time.get_unix_time_from_datetime_dict(b.date): + return true + return false diff --git a/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd.uid b/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd.uid new file mode 100644 index 0000000..429faf6 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/SaveGamesSorter.gd.uid @@ -0,0 +1 @@ +uid://cx3y8rl2vtp8a diff --git a/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd b/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd new file mode 100644 index 0000000..c0919fc --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd @@ -0,0 +1,58 @@ +# A container for loading from a save slot +extends Control + + +# Emitted when the back button is pressed +signal back_button_pressed + + +# The scene to display a slot +@export var slot_ui_scene: PackedScene + + +# Load the savegames when loaded +func _ready(): + refresh_savegames() + + +# A slot was pressed. Load the game +# +# #### Parameters +# - slot_id: The slot that was pressed +func _on_slot_pressed(slot_id: int) -> void: + escoria.save_manager.load_game(slot_id) + + +# The back button was pressed +func _on_back_pressed(): + back_button_pressed.emit() + + +# Create the slots from the list of savegames +func refresh_savegames(): + # Clear all UI saves slots + for slot in $VBoxContainer/ScrollContainer/slots.get_children(): + $VBoxContainer/ScrollContainer/slots.remove_child(slot) + + # Get saves list + var saves_list = escoria.save_manager.get_saves_list() + if saves_list.values().is_empty(): + return + var saves_array: Array = saves_list.values() + saves_array.sort_custom(Callable(SaveGamesSorter, "sort_by_date_descending")) + + for save in saves_array: + var new_slot = slot_ui_scene.instantiate() + $VBoxContainer/ScrollContainer/slots.add_child( + new_slot + ) + + var datetime_string = "%02d/%02d/%02d %02d:%02d" % [ + save.date["day"], + save.date["month"], + save.date["year"], + save.date["hour"], + save.date["minute"], + ] + new_slot.set_slot_name_date(save["name"], datetime_string) + new_slot.connect("pressed", Callable(self, "_on_slot_pressed").bind(int(save["slotnumber"]))) diff --git a/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd.uid b/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd.uid new file mode 100644 index 0000000..1a2ed80 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/load/load_game.gd.uid @@ -0,0 +1 @@ +uid://yfabboc406rf diff --git a/addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn b/addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn new file mode 100644 index 0000000..58fbcf2 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn @@ -0,0 +1,44 @@ +[gd_scene load_steps=3 format=3 uid="uid://j3wkbyhedkpr"] + +[ext_resource type="PackedScene" uid="uid://ciysuxd81qucn" path="res://addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn" id="2"] +[ext_resource type="Script" uid="uid://yfabboc406rf" path="res://addons/escoria-core/ui_library/menus/load_save/load/load_game.gd" id="3"] + +[node name="load_game" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("3") +slot_ui_scene = ExtResource("2") + +[node name="Panel" type="ColorRect" parent="."] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color(0.270588, 0.270588, 0.270588, 1) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 0 +anchor_left = 0.3 +anchor_top = 0.3 +anchor_right = 0.7 +anchor_bottom = 0.7 +offset_left = -55.5 +offset_right = 55.5 + +[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="slots" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="back" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "OPTIONS_BACK" + +[connection signal="pressed" from="VBoxContainer/back" to="." method="_on_back_pressed"] diff --git a/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd new file mode 100644 index 0000000..9de5d90 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd @@ -0,0 +1,7 @@ +extends Button + + +func set_slot_name_date(p_name: String, p_date: String): + $VBoxContainer/slot_name.text = p_name + $VBoxContainer/date.text = p_date + diff --git a/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd.uid b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd.uid new file mode 100644 index 0000000..f48bf6e --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd.uid @@ -0,0 +1 @@ +uid://de0wab1dx7xfx diff --git a/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn new file mode 100644 index 0000000..79f3a74 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=2 format=3 uid="uid://ciysuxd81qucn"] + +[ext_resource type="Script" uid="uid://de0wab1dx7xfx" path="res://addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.gd" id="2"] + +[node name="slot" type="Button"] +offset_right = 665.0 +offset_bottom = 86.0 +custom_minimum_size = Vector2(0, 100) +size_flags_horizontal = 3 +script = ExtResource("2") +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +alignment = 1 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="slot_name" type="Label" parent="VBoxContainer"] +offset_top = 34.0 +offset_right = 665.0 +offset_bottom = 48.0 +text = "slot_name" +align = 1 + +[node name="date" type="Label" parent="VBoxContainer"] +offset_top = 52.0 +offset_right = 665.0 +offset_bottom = 66.0 +text = "date" +align = 1 diff --git a/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd new file mode 100644 index 0000000..6d7e88f --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd @@ -0,0 +1,14 @@ +extends Popup + +signal confirm_yes + +func _ready(): + $MarginContainer/VBoxContainer/HBoxContainer/yes.grab_focus() + +func _on_no_pressed(): + hide() + + +func _on_yes_pressed(): + confirm_yes.emit() + hide() diff --git a/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd.uid b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd.uid new file mode 100644 index 0000000..5662ea7 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd.uid @@ -0,0 +1 @@ +uid://m1jk0vg7pdn2 diff --git a/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn new file mode 100644 index 0000000..e345f01 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn @@ -0,0 +1,63 @@ +[gd_scene load_steps=2 format=3 uid="uid://c4ldxsprk70gi"] + +[ext_resource type="Script" uid="uid://m1jk0vg7pdn2" path="res://addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.gd" id="2"] + +[node name="overwrite_confirm_popup" type="Popup"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("2") + +[node name="MarginContainer" type="MarginContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +theme_override_constants/margin_right = 30 +theme_override_constants/margin_top = 30 +theme_override_constants/margin_left = 30 +theme_override_constants/margin_bottom = 30 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +offset_left = 30.0 +offset_top = 423.0 +offset_right = 1250.0 +offset_bottom = 477.0 +size_flags_vertical = 4 +theme_override_constants/separation = 20 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] +offset_right = 1220.0 +offset_bottom = 14.0 +text = "CONFIRM_OVERWRITE" + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +offset_top = 34.0 +offset_right = 1220.0 +offset_bottom = 54.0 +theme_override_constants/separation = 10 +alignment = 2 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="no" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +offset_left = 1187.0 +offset_right = 1220.0 +offset_bottom = 20.0 +text = "NO" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="yes" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +offset_left = 1144.0 +offset_right = 1177.0 +offset_bottom = 20.0 +text = "YES" +__meta__ = { +"_edit_use_anchors_": false +} + +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/no" to="." method="_on_no_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/yes" to="." method="_on_yes_pressed"] diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd b/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd new file mode 100644 index 0000000..33bbb13 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd @@ -0,0 +1,90 @@ +# A container for saving to a save slot +extends Control + +# Emitted when the back button is pressed +signal back_button_pressed + + +# The scene to display a slot +@export var slot_ui_scene: PackedScene + + +# The slot that was pressed +var _slot_pressed = null + + +# Load the savegames when loaded +func _ready(): + refresh_savegames() + + +# A slot was pressed, save the game +func _on_slot_pressed(p_slot_n: int): + _slot_pressed = p_slot_n + if escoria.save_manager.save_game_exists(p_slot_n): + $overwrite_confirm_popup.popup_centered_ratio(.3) + else: + $save_name_popup.popup_centered_ratio(.3) + + +# Create the slots from the list of savegames +func refresh_savegames(): + var _slots = $VBoxContainer/ScrollContainer/slots + for slot in _slots.get_children(): + _slots.remove_child(slot) + + var saves_list = escoria.save_manager.get_saves_list() + + var datetime = Time.get_datetime_dict_from_system() + var datetime_string = "%02d/%02d/%02d %02d:%02d" % [ + datetime["day"], + datetime["month"], + datetime["year"], + datetime["hour"], + datetime["minute"], + ] + var new_slot = slot_ui_scene.instantiate() + _slots.add_child(new_slot) + _slots.move_child(new_slot, 0) + new_slot.set_slot_name_date(tr("New save"), datetime_string) + new_slot.connect("pressed", Callable(self, "_on_slot_pressed").bind(saves_list.size()+1)) + + if saves_list.values().is_empty(): + return + var saves_array: Array = saves_list.values() + saves_array.sort_custom(Callable(SaveGamesSorter, "sort_by_date_descending")) + + for save in saves_array: + new_slot = slot_ui_scene.instantiate() + _slots.add_child(new_slot) + + datetime_string = "%02d/%02d/%02d %02d:%02d" % [ + save.date["day"], + save.date["month"], + save.date["year"], + save.date["hour"], + save.date["minute"], + ] + + new_slot.set_slot_name_date(save["name"], datetime_string) + new_slot.connect("pressed", Callable(self, "_on_slot_pressed").bind(int(save["slotnumber"]))) + + +# The back button was pressed +func _on_back_pressed(): + back_button_pressed.emit() + + +# The name for the save game was given, save the game. +# +# #### Parameters +# - p_savename: The name of the savegame entered +func _on_save_name_popup_savegame_name_ok(p_savename: String): + escoria.save_manager.save_game(_slot_pressed, p_savename) + refresh_savegames() + _slot_pressed = null + + +# Overwriting the savegame was confirmed, show the save name popup +func _on_overwrite_confirm_popup_confirm_yes(): + $save_name_popup.popup_centered_ratio(.3) diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd.uid b/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd.uid new file mode 100644 index 0000000..359837e --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/save_game.gd.uid @@ -0,0 +1 @@ +uid://crpi3w8bdekpc diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn b/addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn new file mode 100644 index 0000000..e9df2b4 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn @@ -0,0 +1,56 @@ +[gd_scene load_steps=5 format=3 uid="uid://cj440t8pub603"] + +[ext_resource type="PackedScene" uid="uid://ciysuxd81qucn" path="res://addons/escoria-core/ui_library/menus/load_save/load_save_slot/load_save_slot.tscn" id="1"] +[ext_resource type="Script" uid="uid://crpi3w8bdekpc" path="res://addons/escoria-core/ui_library/menus/load_save/save/save_game.gd" id="2"] +[ext_resource type="PackedScene" uid="uid://coiqm0xcq8f8o" path="res://addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn" id="4"] +[ext_resource type="PackedScene" uid="uid://c4ldxsprk70gi" path="res://addons/escoria-core/ui_library/menus/load_save/save/overwrite_confirm_popup.tscn" id="5"] + +[node name="save_game" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2") +slot_ui_scene = ExtResource("1") + +[node name="Panel" type="ColorRect" parent="."] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -1.0 +offset_bottom = -1.0 +color = Color(0.270588, 0.270588, 0.270588, 1) + +[node name="save_name_popup" parent="." instance=ExtResource("4")] +size = Vector2i(209, 185) + +[node name="overwrite_confirm_popup" parent="." instance=ExtResource("5")] +size = Vector2i(231, 134) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 0 +anchor_left = 0.3 +anchor_top = 0.3 +anchor_right = 0.7 +anchor_bottom = 0.7 +offset_left = -55.5 +offset_right = 55.5 + +[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="slots" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="back" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "OPTIONS_BACK" + +[connection signal="savegame_name_ok" from="save_name_popup" to="." method="_on_save_name_popup_savegame_name_ok"] +[connection signal="confirm_yes" from="overwrite_confirm_popup" to="." method="_on_overwrite_confirm_popup_confirm_yes"] +[connection signal="pressed" from="VBoxContainer/back" to="." method="_on_back_pressed"] diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd new file mode 100644 index 0000000..b631ef2 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd @@ -0,0 +1,21 @@ +extends Popup + +signal savegame_name_ok(savegame_name) +signal savegame_cancel + +func _on_cancel_pressed(): + savegame_cancel.emit() + hide() + +func _on_ok_pressed(): + if not $MarginContainer/VBoxContainer/LineEdit.text.is_empty(): + savegame_name_ok.emit($MarginContainer/VBoxContainer/LineEdit.text) + $MarginContainer/VBoxContainer/LineEdit.clear() + hide() + +func _input(event): + if event is InputEventKey: + if event.is_pressed() \ + and event.keycode == KEY_ENTER \ + or event.keycode == KEY_KP_ENTER: + _on_ok_pressed() diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd.uid b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd.uid new file mode 100644 index 0000000..41128a4 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd.uid @@ -0,0 +1 @@ +uid://blqbjhwtenth5 diff --git a/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn new file mode 100644 index 0000000..8d6fc57 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.tscn @@ -0,0 +1,71 @@ +[gd_scene load_steps=2 format=3 uid="uid://coiqm0xcq8f8o"] + +[ext_resource type="Script" uid="uid://blqbjhwtenth5" path="res://addons/escoria-core/ui_library/menus/load_save/save/save_name_popup.gd" id="2"] + +[node name="save_name_popup" type="Popup"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("2") +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="MarginContainer" type="MarginContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +theme_override_constants/margin_right = 30 +theme_override_constants/margin_top = 30 +theme_override_constants/margin_left = 30 +theme_override_constants/margin_bottom = 30 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +offset_left = 30.0 +offset_top = 401.0 +offset_right = 1250.0 +offset_bottom = 499.0 +size_flags_vertical = 4 +theme_override_constants/separation = 20 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] +offset_right = 1220.0 +offset_bottom = 14.0 +text = "ENTER_SAVE_NAME" + +[node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"] +offset_top = 34.0 +offset_right = 1220.0 +offset_bottom = 58.0 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +offset_top = 78.0 +offset_right = 1220.0 +offset_bottom = 98.0 +theme_override_constants/separation = 10 +alignment = 2 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="cancel" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +offset_left = 1118.0 +offset_right = 1179.0 +offset_bottom = 20.0 +text = "CANCEL" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="ok" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +offset_left = 1189.0 +offset_right = 1220.0 +offset_bottom = 20.0 +text = "OK" +__meta__ = { +"_edit_use_anchors_": false +} + +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/cancel" to="." method="_on_cancel_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/ok" to="." method="_on_ok_pressed"] diff --git a/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd b/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd new file mode 100644 index 0000000..dac988b --- /dev/null +++ b/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd @@ -0,0 +1,42 @@ +# A simple main menu +extends Control + + +# Start the game +func _on_new_game_pressed(): + escoria.new_game() + + +# Show the load slots +func _on_load_game_pressed(): + $main.hide() + $load_game.refresh_savegames() + $load_game.show() + + +# Show the options panel +func _on_options_pressed(): + $main.hide() + $options.show() + + +# Quit the game +func _on_quit_pressed(): + escoria.quit() + + +# Hide the options panel again +func _on_options_back_button_pressed(): + reset() + + +# Hide the load panel +func _on_load_game_back_button_pressed(): + reset() + + +# Resets the UI to initial state +func reset(): + $load_game.hide() + $options.hide() + $main.show() diff --git a/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd.uid b/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd.uid new file mode 100644 index 0000000..467bea5 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/main_menu/main_menu.gd.uid @@ -0,0 +1 @@ +uid://cfpa1dldiuvh8 diff --git a/addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn b/addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn new file mode 100644 index 0000000..b259fa1 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn @@ -0,0 +1,85 @@ +[gd_scene load_steps=6 format=3 uid="uid://u8an5av5fuxx"] + +[ext_resource type="Script" uid="uid://cfpa1dldiuvh8" path="res://addons/escoria-core/ui_library/menus/main_menu/main_menu.gd" id="1"] +[ext_resource type="Theme" uid="uid://b081yyinnj03t" path="res://addons/escoria-core/ui_library/theme.tres" id="2"] +[ext_resource type="Texture2D" uid="uid://bwplh73qf2twk" path="res://addons/escoria-core/design/escoria-logo-small.png" id="3"] +[ext_resource type="PackedScene" uid="uid://bhcfnds3yy2yv" path="res://addons/escoria-core/ui_library/menus/options/options.tscn" id="4"] +[ext_resource type="PackedScene" uid="uid://j3wkbyhedkpr" path="res://addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn" id="5"] + +[node name="main_menu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("2") +script = ExtResource("1") + +[node name="load_game" parent="." instance=ExtResource("5")] +visible = false +layout_mode = 1 + +[node name="options" parent="." instance=ExtResource("4")] +visible = false +layout_mode = 1 + +[node name="main" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ColorRect" type="ColorRect" parent="main"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0.270222, 0.270222, 0.270222, 1) + +[node name="main" type="VBoxContainer" parent="main"] +layout_mode = 0 +anchor_left = 0.5 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -308.0 +offset_right = 308.0 +theme_override_constants/separation = 100 +alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="main/main"] +layout_mode = 2 +texture = ExtResource("3") + +[node name="buttons" type="VBoxContainer" parent="main/main"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="new_game" type="Button" parent="main/main/buttons"] +custom_minimum_size = Vector2(0, 150) +layout_mode = 2 +size_flags_vertical = 3 +text = "NEW_GAME" + +[node name="load_game" type="Button" parent="main/main/buttons"] +layout_mode = 2 +text = "LOAD_GAME" + +[node name="options" type="Button" parent="main/main/buttons"] +layout_mode = 2 +text = "OPTIONS" + +[node name="quit" type="Button" parent="main/main/buttons"] +layout_mode = 2 +text = "QUIT" + +[connection signal="back_button_pressed" from="load_game" to="." method="_on_load_game_back_button_pressed"] +[connection signal="back_button_pressed" from="options" to="." method="_on_options_back_button_pressed"] +[connection signal="pressed" from="main/main/buttons/new_game" to="." method="_on_new_game_pressed"] +[connection signal="pressed" from="main/main/buttons/load_game" to="." method="_on_load_game_pressed"] +[connection signal="pressed" from="main/main/buttons/options" to="." method="_on_options_pressed"] +[connection signal="pressed" from="main/main/buttons/quit" to="." method="_on_quit_pressed"] diff --git a/addons/escoria-core/ui_library/menus/options/flags/de.png b/addons/escoria-core/ui_library/menus/options/flags/de.png new file mode 100644 index 0000000..4fe98a8 Binary files /dev/null and b/addons/escoria-core/ui_library/menus/options/flags/de.png differ diff --git a/addons/escoria-core/ui_library/menus/options/flags/de.png.import b/addons/escoria-core/ui_library/menus/options/flags/de.png.import new file mode 100644 index 0000000..f4b4dc8 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/options/flags/de.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://r6b3dp6ipv5k" +path="res://.godot/imported/de.png-eb10e96b44c694832a61fe6e6648faac.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/ui_library/menus/options/flags/de.png" +dest_files=["res://.godot/imported/de.png-eb10e96b44c694832a61fe6e6648faac.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/ui_library/menus/options/flags/en.png b/addons/escoria-core/ui_library/menus/options/flags/en.png new file mode 100644 index 0000000..0589284 Binary files /dev/null and b/addons/escoria-core/ui_library/menus/options/flags/en.png differ diff --git a/addons/escoria-core/ui_library/menus/options/flags/en.png.import b/addons/escoria-core/ui_library/menus/options/flags/en.png.import new file mode 100644 index 0000000..309ad2b --- /dev/null +++ b/addons/escoria-core/ui_library/menus/options/flags/en.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cxuxfcyqd81c2" +path="res://.godot/imported/en.png-e3d7ed70a1e59fdf7b47a2d8c96ff41f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/ui_library/menus/options/flags/en.png" +dest_files=["res://.godot/imported/en.png-e3d7ed70a1e59fdf7b47a2d8c96ff41f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/ui_library/menus/options/flags/es.png b/addons/escoria-core/ui_library/menus/options/flags/es.png new file mode 100644 index 0000000..7178ce4 Binary files /dev/null and b/addons/escoria-core/ui_library/menus/options/flags/es.png differ diff --git a/addons/escoria-core/ui_library/menus/options/flags/es.png.import b/addons/escoria-core/ui_library/menus/options/flags/es.png.import new file mode 100644 index 0000000..378230c --- /dev/null +++ b/addons/escoria-core/ui_library/menus/options/flags/es.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdw1c55s4iaf4" +path="res://.godot/imported/es.png-90c18482f305496440586f74957db9fb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/ui_library/menus/options/flags/es.png" +dest_files=["res://.godot/imported/es.png-90c18482f305496440586f74957db9fb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/ui_library/menus/options/flags/fr.png b/addons/escoria-core/ui_library/menus/options/flags/fr.png new file mode 100644 index 0000000..fcaf2cc Binary files /dev/null and b/addons/escoria-core/ui_library/menus/options/flags/fr.png differ diff --git a/addons/escoria-core/ui_library/menus/options/flags/fr.png.import b/addons/escoria-core/ui_library/menus/options/flags/fr.png.import new file mode 100644 index 0000000..a279aa7 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/options/flags/fr.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dfixfxtbgelrb" +path="res://.godot/imported/fr.png-6fb37573278571598430825ea325f0e3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-core/ui_library/menus/options/flags/fr.png" +dest_files=["res://.godot/imported/fr.png-6fb37573278571598430825ea325f0e3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-core/ui_library/menus/options/options.gd b/addons/escoria-core/ui_library/menus/options/options.gd new file mode 100644 index 0000000..32a9758 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/options/options.gd @@ -0,0 +1,190 @@ +# An options menu +extends Control + + +# The back button was pressed +signal back_button_pressed + + +# Custom setting key +const CUSTOM_SETTING: String = "a_custom_setting" + + +# The current settings +var backup_settings + + +# A list of languages already added to the language selection +var _loaded_languages: Array = [] + + +# The settings changed +@onready var changed = false + + +# Initialize the flags +func _ready() -> void: + var _flags_container: HBoxContainer = \ + $VBoxContainer/MarginContainer/options/flags + for child in _flags_container.get_children(): + _flags_container.remove_child(child) + child.queue_free() + + _loaded_languages = [] + + for lang in TranslationServer.get_loaded_locales(): + if not lang in _loaded_languages: + _loaded_languages.append(lang) + var _lang = TextureRect.new() + _lang.texture = load( + "res://addons/escoria-core/ui_library" + \ + "/menus/options/flags/%s.png" % lang + ) + _flags_container.add_child(_lang) + _lang.connect("gui_input", Callable(self, "_on_language_input").bind(lang)) + + +# Show the options +func show(): + backup_settings = escoria.settings_manager.get_settings() + initialize_options(backup_settings) + visible = true + + +# Set the sliders to the values of the settings +# +# #### Parameters +# - p_settings: The settings to use +func initialize_options(p_settings): + var _options = $VBoxContainer/MarginContainer/options + _options.get_node("general_volume").value = p_settings["master_volume"] + _options.get_node("sound_volume").value = p_settings["sfx_volume"] + _options.get_node("music_volume").value = p_settings["music_volume"] + _options.get_node("speech_volume").value = p_settings["speech_volume"] + _options.get_node("ambient_volume").value = p_settings["ambient_volume"] + _options.get_node("fullscreen").set_pressed_no_signal(p_settings["fullscreen"]) + + +# The language was changed +# +# #### Parameters +# - event: The input event from the flag +# - language: The language to set +func _on_language_input(event: InputEvent, language: String): + if event.is_pressed(): + TranslationServer.set_locale(language) + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.TEXT_LANG, + language + ) + changed = true + + +# Sound volume was changed +# +# #### Parameters +# - value: The new volume level +func _on_sound_volume_changed(value): + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SFX_VOLUME + ) != value: + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.SFX_VOLUME, + value + ) + escoria.settings_manager.apply_settings() + changed = true + + +# Music volume was changed +# +# #### Parameters +# - value: The new volume level +func _on_music_volume_changed(value): + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.MUSIC_VOLUME + ) != value: + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.MUSIC_VOLUME, + value + ) + escoria.settings_manager.apply_settings() + changed = true + + +# General volume was changed +# +# #### Parameters +# - value: The new volume level +func _on_general_volume_changed(value): + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.MASTER_VOLUME + ) != value: + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.MASTER_VOLUME, + value + ) + escoria.settings_manager.apply_settings() + changed = true + + +# Speech volume was changed +# +# #### Parameters +# - value: The new volume level +func _on_speech_volume_value_changed(value: float) -> void: + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_VOLUME + ) != value: + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.SPEECH_VOLUME, + value + ) + escoria.settings_manager.apply_settings() + changed = true + +# Ambient volume was changed +# +# #### Parameters +# - value: The new volume level +func _on_ambient_volume_value_changed(value: float) -> void: + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.AMBIENT_VOLUME + ) != value: + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.AMBIENT_VOLUME, + value + ) + escoria.settings_manager.apply_settings() + changed = true + + +# Fullscreen was changed +# +# #### Parameters +# - button_pressed: Fullscreen (true) or windowed (false) +func _on_fullscreen_toggled(button_pressed: bool) -> void: + if ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.FULLSCREEN + ) != button_pressed: + ESCProjectSettingsManager.set_setting( + ESCProjectSettingsManager.FULLSCREEN, + button_pressed + ) + escoria.settings_manager.apply_settings() + changed = true + + +# Save the settings +func _on_apply_pressed(): + escoria.settings_manager.custom_settings[CUSTOM_SETTING] = 100 + escoria.settings_manager.save_settings() + changed = false + back_button_pressed.emit() + + +# The back button was pressed +func _on_back_pressed(): + escoria.settings_manager.save_settings_resource_to_project_settings(backup_settings) + escoria.settings_manager.apply_settings() + back_button_pressed.emit() diff --git a/addons/escoria-core/ui_library/menus/options/options.gd.uid b/addons/escoria-core/ui_library/menus/options/options.gd.uid new file mode 100644 index 0000000..b7bb93d --- /dev/null +++ b/addons/escoria-core/ui_library/menus/options/options.gd.uid @@ -0,0 +1 @@ +uid://btyrwps5h2qn7 diff --git a/addons/escoria-core/ui_library/menus/options/options.tscn b/addons/escoria-core/ui_library/menus/options/options.tscn new file mode 100644 index 0000000..133315c --- /dev/null +++ b/addons/escoria-core/ui_library/menus/options/options.tscn @@ -0,0 +1,136 @@ +[gd_scene load_steps=3 format=3 uid="uid://bhcfnds3yy2yv"] + +[ext_resource type="Texture2D" uid="uid://r6b3dp6ipv5k" path="res://addons/escoria-core/ui_library/menus/options/flags/de.png" id="1"] +[ext_resource type="Script" uid="uid://btyrwps5h2qn7" path="res://addons/escoria-core/ui_library/menus/options/options.gd" id="4"] + +[node name="options" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("4") + +[node name="Panel" type="ColorRect" parent="."] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color(0.270588, 0.270588, 0.270588, 1) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 6 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="options" type="GridContainer" parent="VBoxContainer/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 6 +theme_override_constants/h_separation = 40 +columns = 2 + +[node name="label" type="Label" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +text = "OPTIONS_LANGUAGE" + +[node name="flags" type="HBoxContainer" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 30 +alignment = 1 + +[node name="TextureRect2" type="TextureRect" parent="VBoxContainer/MarginContainer/options/flags"] +layout_mode = 2 +texture = ExtResource("1") + +[node name="TextureRect3" type="TextureRect" parent="VBoxContainer/MarginContainer/options/flags"] +layout_mode = 2 +texture = ExtResource("1") + +[node name="label2" type="Label" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +text = "GENERAL_VOLUME" + +[node name="general_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.001 + +[node name="label3" type="Label" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +text = "SOUND_VOLUME" + +[node name="sound_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.001 + +[node name="label4" type="Label" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +text = "MUSIC_VOLUME" + +[node name="music_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.001 + +[node name="label5" type="Label" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +text = "SPEECH_VOLUME" + +[node name="speech_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.001 + +[node name="label6" type="Label" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +text = "AMBIENT_VOLUME" + +[node name="ambient_volume" type="HSlider" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.001 + +[node name="label7" type="Label" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 +text = "FULLSCREEN" + +[node name="fullscreen" type="CheckBox" parent="VBoxContainer/MarginContainer/options"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 20 +alignment = 1 + +[node name="back" type="Button" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "OPTIONS_BACK" + +[node name="apply" type="Button" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "APPLY" + +[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/general_volume" to="." method="_on_general_volume_changed"] +[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/sound_volume" to="." method="_on_sound_volume_changed"] +[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/music_volume" to="." method="_on_music_volume_changed"] +[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/speech_volume" to="." method="_on_speech_volume_value_changed"] +[connection signal="value_changed" from="VBoxContainer/MarginContainer/options/ambient_volume" to="." method="_on_ambient_volume_value_changed"] +[connection signal="toggled" from="VBoxContainer/MarginContainer/options/fullscreen" to="." method="_on_fullscreen_toggled"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/back" to="." method="_on_back_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/apply" to="." method="_on_apply_pressed"] diff --git a/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd new file mode 100644 index 0000000..e4f9c66 --- /dev/null +++ b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd @@ -0,0 +1,75 @@ +# A menu shown in game +extends Control + + +# Make the pause menu process in pause mode and hide it just to be sure +func _ready(): + self.process_mode = Node.PROCESS_MODE_ALWAYS + hide() + + +# Continue the game +func _on_continue_pressed(): + escoria.main.current_scene.game.unpause_game() + + +# Show the save slots +func _on_save_game_pressed(): + $VBoxContainer.hide() + $save_game.refresh_savegames() + $save_game.show() + + +# Show the load slots +func _on_load_game_pressed(): + $VBoxContainer.hide() + $load_game.refresh_savegames() + $load_game.show() + + +# Show the options menu +func _on_options_pressed(): + $VBoxContainer.hide() + $options.show() + + +# Quit the game +func _on_quit_pressed(): + escoria.quit() + + +# Hide the save slots after clicking back button +func _on_save_game_back_button_pressed(): + reset() + + +# Hide the load slots after clicking back button +func _on_load_game_back_button_pressed(): + reset() + + +# Hide options menu after clicking back button +func _on_options_back_button_pressed(): + reset() + + +# Set whether saving is enabled currently +# +# #### Parameters +# - p_enabled: Enable or disable saving +func set_save_enabled(p_enabled: bool): + $VBoxContainer/menuitems/save_game.disabled = !p_enabled + + +# Resets the UI to initial state +func reset(): + $save_game.hide() + $load_game.hide() + $options.hide() + $VBoxContainer.show() + + +func _on_new_game_pressed(): + #yield(escoria.new_game(), "completed") + escoria.new_game() + escoria.main.current_scene.game.unpause_game() diff --git a/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd.uid b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd.uid new file mode 100644 index 0000000..3f8bafb --- /dev/null +++ b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd.uid @@ -0,0 +1 @@ +uid://dvx5soxivafmw diff --git a/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn new file mode 100644 index 0000000..954191d --- /dev/null +++ b/addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn @@ -0,0 +1,94 @@ +[gd_scene load_steps=7 format=3 uid="uid://gfjgmbcafyyt"] + +[ext_resource type="Script" uid="uid://dvx5soxivafmw" path="res://addons/escoria-core/ui_library/menus/pause_menu/pause_menu.gd" id="1"] +[ext_resource type="PackedScene" uid="uid://bhcfnds3yy2yv" path="res://addons/escoria-core/ui_library/menus/options/options.tscn" id="2"] +[ext_resource type="Texture2D" uid="uid://bwplh73qf2twk" path="res://addons/escoria-core/design/escoria-logo-small.png" id="3"] +[ext_resource type="PackedScene" uid="uid://cj440t8pub603" path="res://addons/escoria-core/ui_library/menus/load_save/save/save_game.tscn" id="4"] +[ext_resource type="PackedScene" uid="uid://j3wkbyhedkpr" path="res://addons/escoria-core/ui_library/menus/load_save/load/load_game.tscn" id="5"] +[ext_resource type="Theme" uid="uid://b081yyinnj03t" path="res://addons/escoria-core/ui_library/theme.tres" id="6"] + +[node name="pause_menu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1") + +[node name="Panel" type="ColorRect" parent="."] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color(0.270588, 0.270588, 0.270588, 1) + +[node name="save_game" parent="." instance=ExtResource("4")] +visible = false +layout_mode = 1 +theme = ExtResource("6") + +[node name="load_game" parent="." instance=ExtResource("5")] +visible = false +layout_mode = 1 + +[node name="options" parent="." instance=ExtResource("2")] +visible = false +layout_mode = 1 + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 0 +anchor_left = 0.5 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -308.0 +offset_right = 308.0 +theme_override_constants/separation = 100 +alignment = 1 + +[node name="TextureRect" type="TextureRect" parent="VBoxContainer"] +layout_mode = 2 +texture = ExtResource("3") + +[node name="menuitems" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="continue" type="Button" parent="VBoxContainer/menuitems"] +custom_minimum_size = Vector2(0, 150) +layout_mode = 2 +size_flags_vertical = 3 +text = "CONTINUE_GAME" + +[node name="new_game" type="Button" parent="VBoxContainer/menuitems"] +layout_mode = 2 +size_flags_vertical = 3 +text = "NEW_GAME" + +[node name="save_game" type="Button" parent="VBoxContainer/menuitems"] +layout_mode = 2 +size_flags_vertical = 3 +text = "SAVE_GAME" + +[node name="load_game" type="Button" parent="VBoxContainer/menuitems"] +layout_mode = 2 +size_flags_vertical = 3 +text = "LOAD_GAME" + +[node name="options" type="Button" parent="VBoxContainer/menuitems"] +layout_mode = 2 +text = "OPTIONS" + +[node name="quit" type="Button" parent="VBoxContainer/menuitems"] +layout_mode = 2 +size_flags_vertical = 3 +text = "QUIT" + +[connection signal="back_button_pressed" from="save_game" to="." method="_on_save_game_back_button_pressed"] +[connection signal="back_button_pressed" from="load_game" to="." method="_on_load_game_back_button_pressed"] +[connection signal="back_button_pressed" from="options" to="." method="_on_options_back_button_pressed"] +[connection signal="pressed" from="VBoxContainer/menuitems/continue" to="." method="_on_continue_pressed"] +[connection signal="pressed" from="VBoxContainer/menuitems/new_game" to="." method="_on_new_game_pressed"] +[connection signal="pressed" from="VBoxContainer/menuitems/save_game" to="." method="_on_save_game_pressed"] +[connection signal="pressed" from="VBoxContainer/menuitems/load_game" to="." method="_on_load_game_pressed"] +[connection signal="pressed" from="VBoxContainer/menuitems/options" to="." method="_on_options_pressed"] +[connection signal="pressed" from="VBoxContainer/menuitems/quit" to="." method="_on_quit_pressed"] diff --git a/addons/escoria-core/ui_library/theme.tres b/addons/escoria-core/ui_library/theme.tres new file mode 100644 index 0000000..d21e9fd --- /dev/null +++ b/addons/escoria-core/ui_library/theme.tres @@ -0,0 +1,6 @@ +[gd_resource type="Theme" load_steps=2 format=3 uid="uid://b081yyinnj03t"] + +[ext_resource type="FontFile" path="res://addons/escoria-core/ui_library/fonts/caslonantique.tres" id="1"] + +[resource] +default_font = ExtResource("1") diff --git a/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd new file mode 100644 index 0000000..0443f91 --- /dev/null +++ b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd @@ -0,0 +1,14 @@ +extends CanvasLayer + +@onready var list = $VBoxContainer/hover_stack + +func update() -> void: + for e in list.get_children(): + list.remove_child(e) + e.queue_free() + if escoria.inputs_manager.hover_stack.is_empty(): + return + for e in escoria.inputs_manager.hover_stack.get_all(): + var l = Label.new() + l.text = e.global_id + list.add_child(l) diff --git a/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd.uid b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd.uid new file mode 100644 index 0000000..a06dc00 --- /dev/null +++ b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd.uid @@ -0,0 +1 @@ +uid://csscdvd15atps diff --git a/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn new file mode 100644 index 0000000..f368ce7 --- /dev/null +++ b/addons/escoria-core/ui_library/tools/hover_stack/hover_stack.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=2 format=3 uid="uid://ceu6y33rrbdr"] + +[ext_resource type="Script" uid="uid://csscdvd15atps" path="res://addons/escoria-core/ui_library/tools/hover_stack/hover_stack.gd" id="1"] + +[node name="hover_stack_layer" type="CanvasLayer"] +script = ExtResource("1") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +offset_top = 32.0 +offset_right = 167.0 +offset_bottom = 84.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="title" type="RichTextLabel" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +bbcode_enabled = true +text = "Hover stack" + +[node name="hover_stack" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 diff --git a/addons/escoria-core/ui_library/tools/room_select/room_select.gd b/addons/escoria-core/ui_library/tools/room_select/room_select.gd new file mode 100644 index 0000000..cc03b40 --- /dev/null +++ b/addons/escoria-core/ui_library/tools/room_select/room_select.gd @@ -0,0 +1,73 @@ +# A small utility to quickly switch to a room while developing +extends OptionButton + + +# The selected option +var _selected_id = 0 + + +# The path to available rooms +var _options_paths = [] + + +# Build up the list of rooms +func _ready(): + var rooms_folder = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.ROOM_SELECTOR_ROOM_DIR + ) + if rooms_folder == "" or \ + not ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.ENABLE_ROOM_SELECTOR + ): + return + var rooms_list: Array = [] + var path = ProjectSettings.globalize_path(rooms_folder) + if not OS.has_feature("editor"): + path = OS.get_executable_path().get_base_dir().path_join(path) + var dir = DirAccess.open(path) + if dir != null: + dir.list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547 + var file_name = dir.get_next() + while file_name != "": + if dir.current_is_dir(): + rooms_list.push_back(file_name) + file_name = dir.get_next() + + rooms_list.sort() + for room in rooms_list: + add_item(room) + _options_paths.push_back("%s/%s/%s.tscn" %[ + rooms_folder, + room, + room + ]) + + else: + escoria.logger.warn( + self, + "A problem occurred while opening rooms folder %s." % str(path) + ) + + +# Switch to the selected room +func _on_button_pressed(): + # When next room is loaded, we don't want to consider ESC_LAST_SCENE for + # automatic transitions. + # If FORCE_LAST_SCENE_NULL is True when change_scene starts: + # - ESC_LAST_SCENE is set to empty + escoria.globals_manager.set_global( + escoria.room_manager.GLOBAL_FORCE_LAST_SCENE_NULL, + true, + true + ) + + escoria.event_manager.interrupt() + escoria.room_manager.change_scene_to_file(_options_paths[_selected_id], false) + + +# A room was selected, store the selection +# +# #### Parameters +# - index: The index of the selected room in the paths list +func _on_option_item_selected(index): + _selected_id = index diff --git a/addons/escoria-core/ui_library/tools/room_select/room_select.gd.uid b/addons/escoria-core/ui_library/tools/room_select/room_select.gd.uid new file mode 100644 index 0000000..b36c503 --- /dev/null +++ b/addons/escoria-core/ui_library/tools/room_select/room_select.gd.uid @@ -0,0 +1 @@ +uid://b5uyabsv4jdxn diff --git a/addons/escoria-core/ui_library/tools/room_select/room_select.tscn b/addons/escoria-core/ui_library/tools/room_select/room_select.tscn new file mode 100644 index 0000000..3feb0d5 --- /dev/null +++ b/addons/escoria-core/ui_library/tools/room_select/room_select.tscn @@ -0,0 +1,25 @@ +[gd_scene load_steps=2 format=3 uid="uid://c5xhvh8pw1qba"] + +[ext_resource type="Script" uid="uid://b5uyabsv4jdxn" path="res://addons/escoria-core/ui_library/tools/room_select/room_select.gd" id="1"] + +[node name="room_select" type="HBoxContainer"] +offset_right = 63.0 +offset_bottom = 40.0005 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="option" type="OptionButton" parent="."] +offset_right = 29.0 +offset_bottom = 40.0 +size_flags_horizontal = 3 +script = ExtResource("1") + +[node name="button" type="Button" parent="."] +offset_left = 33.0 +offset_right = 63.0 +offset_bottom = 40.0 +text = "Go" + +[connection signal="item_selected" from="option" to="option" method="_on_option_item_selected"] +[connection signal="pressed" from="button" to="option" method="_on_button_pressed"] diff --git a/addons/escoria-dialog-simple/chooser/simple.gd b/addons/escoria-dialog-simple/chooser/simple.gd new file mode 100644 index 0000000..d7c0297 --- /dev/null +++ b/addons/escoria-dialog-simple/chooser/simple.gd @@ -0,0 +1,99 @@ +# A simple dialog chooser that shows selectable lines of text +# Supports timeout and avatar display +extends ESCDialogOptionsChooser + + +@export var color_normal = Color(1.0,1.0,1.0,1.0) # (Color, RGB) +@export var color_hover = Color(165.0,42.0,42.0, 1.0) # (Color, RGB) + + +var _no_more_options: bool = false + + +# Hide the chooser at the start just to be safe +func _ready() -> void: + hide_chooser() + process_mode = PROCESS_MODE_PAUSABLE + + +# Process the timeout display +func _process(delta: float) -> void: + if $MarginContainer.visible and self.dialog and self.dialog.timeout > 0: + $TimerProgress.value = ( + self.dialog.timeout - $Timer.time_left + ) / self.dialog.timeout * 100 + + +# Show the chooser +func show_chooser(): + var _vbox = $MarginContainer/ScrollContainer/VBoxContainer + for option_node in _vbox.get_children(): + _vbox.remove_child(option_node) + + _remove_avatar() + + for option in self.dialog.options: + if option.is_valid(): + var _option_node = Button.new() + _option_node.text = (option as ESCDialogOption).option + _option_node.flat = true + _option_node.add_theme_color_override("font_color", color_normal) + _option_node.add_theme_color_override("font_color_hover", color_hover) + _vbox.add_child(_option_node) + + _option_node.pressed.connect(_on_answer_selected.bind(option)) + + # If we've no options left, signify as much and start the timer with a + # very short interval so the appropriate signal can be fired. Note that + # we have to fire the signal AFTER this method returns as the caller + # is almost certainly yielding after this method returns. + if _vbox.get_child_count() == 0: + _no_more_options = true + $Timer.start(0.05) + return + + if self.dialog.avatar != "-": + $AvatarContainer.add_child( + ResourceLoader.load(self.dialog.avatar).instantiate() + ) + + $MarginContainer.show() + + if self.dialog.timeout > 0: + $Timer.start(self.dialog.timeout) + + +# Hide the chooser +func hide_chooser(): + $MarginContainer.hide() + + +# An option was choosen, emit the option +# +# #### Parameters +# - option: Option that was chosen +func _option_chosen(option: ESCDialogOption): + _remove_avatar() + $TimerProgress.value = 0 + option_chosen.emit(option) + + +# An option was chosen directly from the list +# +# #### Parameters +# - option: Option that was chosen +func _on_answer_selected(option: ESCDialogOption): + _option_chosen(option) + + +# The timeout came and a option was selected +func _on_Timer_timeout() -> void: + var option_chosen = null if _no_more_options else self.dialog.options[self.dialog.timeout_option - 1] + _no_more_options = false + _option_chosen(option_chosen) + + +# Remove the avatar +func _remove_avatar(): + if $AvatarContainer.get_child_count() > 0: + $AvatarContainer.remove_child($AvatarContainer.get_child(0)) diff --git a/addons/escoria-dialog-simple/chooser/simple.gd.uid b/addons/escoria-dialog-simple/chooser/simple.gd.uid new file mode 100644 index 0000000..05b63ec --- /dev/null +++ b/addons/escoria-dialog-simple/chooser/simple.gd.uid @@ -0,0 +1 @@ +uid://bfwgvr8mfsqvb diff --git a/addons/escoria-dialog-simple/chooser/simple.tscn b/addons/escoria-dialog-simple/chooser/simple.tscn new file mode 100644 index 0000000..0dcab9f --- /dev/null +++ b/addons/escoria-dialog-simple/chooser/simple.tscn @@ -0,0 +1,52 @@ +[gd_scene load_steps=4 format=3 uid="uid://bkygfxo5vhqsy"] + +[ext_resource type="Script" uid="uid://bfwgvr8mfsqvb" path="res://addons/escoria-dialog-simple/chooser/simple.gd" id="1"] + +[sub_resource type="Gradient" id="1"] +colors = PackedColorArray(1, 0, 0, 1, 1, 0, 0, 1) + +[sub_resource type="GradientTexture2D" id="2"] +gradient = SubResource("1") + +[node name="text_dialog_choice" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 0 +offset_left = 20.0 +offset_top = 20.0 +offset_right = 1280.0 +offset_bottom = 900.0 +mouse_filter = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 + +[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 20 + +[node name="Timer" type="Timer" parent="."] +one_shot = true + +[node name="TimerProgress" type="TextureProgressBar" parent="."] +custom_minimum_size = Vector2(0, 20) +layout_mode = 0 +anchor_right = 1.0 +nine_patch_stretch = true +texture_progress = SubResource("2") + +[node name="AvatarContainer" type="Node2D" parent="."] +position = Vector2(94, 68) + +[connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] diff --git a/addons/escoria-dialog-simple/esc_dialog_simple.gd b/addons/escoria-dialog-simple/esc_dialog_simple.gd new file mode 100644 index 0000000..6966159 --- /dev/null +++ b/addons/escoria-dialog-simple/esc_dialog_simple.gd @@ -0,0 +1,211 @@ +## A simple dialog manager for Escoria +extends ESCDialogManager + + +## State machine that governs how the dialog manager behaves +var state_machine = preload("res://addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd").new() + +# The currently running player +var _type_player: Node = null + + +var _preserved_type_player_type: String = "" + +# Reference to the dialog player +var _dialog_player: Node = null + +# Basic state tracking +var _is_saying: bool = false + +# Whether to preserve the next dialog box used by `say`, or, if already +# preserving a dialog box, whether to continue using that dialog box +var _should_preserve_dialog_box: bool = false + + +func _ready() -> void: + add_child(state_machine) + + +# Check whether a specific type is supported by the +# dialog plugin +# +# #### Parameters +# - type: required type +# **Returns** Whether the type is supported or not +func has_type(type: String) -> bool: + return true if type in ["floating", "avatar"] else false + + +# Check whether a specific chooser type is supported by the +# dialog plugin +# +# #### Parameters +# - type: required chooser type +# **Returns** Whether the type is supported or not +func has_chooser_type(type: String) -> bool: + return true if type == "simple" else false + + +# Instructs the dialog manager to preserve the next dialog box used by a `say` +# command until a call to `disable_preserve_dialog_box` is made. +# +# This method should be idempotent, i.e. if called after the first time and +# prior to `disable_preserve_dialog_box` being called, the result should be the +# same. +func enable_preserve_dialog_box() -> void: + _should_preserve_dialog_box = true + + +# Instructs the dialog manager to no longer preserve the currently-preserved +# dialog box or to not preserve the next dialog box used by a `say` command +# (this is the default state). +# +# This method should be idempotent, i.e. if called after the first time and +# prior to `enable_preserve_dialog_box` being called, the result should be the +# same. +func disable_preserve_dialog_box() -> void: + _should_preserve_dialog_box = false + + if is_instance_valid(_dialog_player) and _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + _preserved_type_player_type = "" + + +# Output a text said by the item specified by the global id. Emit +# `say_finished` after finishing displaying the text. +# +# #### Parameters +# - dialog_player: Node of the dialog player in the UI +# - global_id: Global id of the item that is speaking +# - text: Text to say, optional prefixed by a translation key separated +# by a ":" +# - type: Type of dialog box to use +# - *key*: Translation key +func say(dialog_player: Node, global_id: String, text: String, type: String, key: String): + _dialog_player = dialog_player + + _initialize_say_states(global_id, text, type, key) + + if _should_preserve_dialog_box: + # If the dialog box type doesn't match what's currently being reused (if anything), + # we want to remove the old one (if it exists) and then initialize and add the new dialog + # box type to the dialog player + if type != _preserved_type_player_type: + if is_instance_valid(_type_player) and _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + + _init_type_player(type) + + _preserved_type_player_type = type + else: + _init_type_player(type) + + state_machine._change_state("say") + + +func do_say(global_id: String, text: String) -> void: + # Only add_child here in order to prevent _type_player from running its _process method + # before we're ready, and only if it's necessary + if not _dialog_player.get_children().has(_type_player): + _dialog_player.add_child(_type_player) + + _type_player.say(global_id, text) + + +func _init_type_player(type: String) -> void: + if type == "floating": + _type_player = preload(\ + "res://addons/escoria-dialog-simple/types/floating.tscn"\ + ).instantiate() + else: + _type_player = preload(\ + "res://addons/escoria-dialog-simple/types/avatar.tscn"\ + ).instantiate() + + _type_player.say_finished.connect(_on_say_finished) + _type_player.say_visible.connect(_on_say_visible) + + +func _initialize_say_states(global_id: String, text: String, type: String, key: String) -> void: + state_machine.states_map["say"].initialize(self, global_id, text, type, key) + state_machine.states_map["finish"].initialize(_dialog_player) + state_machine.states_map["say_fast"].initialize(self) + state_machine.states_map["say_finish"].initialize(self) + state_machine.states_map["visible"].initialize(self) + state_machine.states_map["interrupt"].initialize(self) + + +func _on_say_finished(): + if not _should_preserve_dialog_box and _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + + _is_saying = false + + say_finished.emit() + + +func _on_say_visible(): + say_visible.emit() + + +# Present an option chooser to the player and sends the signal +# `option_chosen` with the chosen dialog option +# +# #### Parameters +# - dialog_player: Node of the dialog player in the UI +# - dialog: Information about the dialog to display +# - type: The dialog chooser type to use +func choose(dialog_player: Node, dialog: ESCDialog, type: String): + _dialog_player = dialog_player + + state_machine.states_map["choices"].initialize(dialog_player, self, dialog, type) + state_machine._change_state("choices") + + +func do_choose(dialog_player: Node, dialog: ESCDialog, type: String = "simple"): + var chooser + + if type == "simple" or type == "": + chooser = preload(\ + "res://addons/escoria-dialog-simple/chooser/simple.tscn"\ + ).instantiate() + + dialog_player.add_child(chooser) + chooser.set_dialog(dialog) + chooser.show_chooser() + + var option = await chooser.option_chosen + dialog_player.remove_child(chooser) + option_chosen.emit(option) + + +# Trigger running the dialogue faster +func speedup(): + if is_instance_valid(_type_player): + _type_player.speedup() + + +# Trigger an instant finish of the current dialog +func finish(): + if is_instance_valid(_type_player): + _type_player.finish() + + +# The say command has been interrupted, cancel the dialog display +func interrupt(): + if _dialog_player.get_children().has(_type_player): + ( + escoria.object_manager.get_object(escoria.object_manager.SPEECH).node\ + as ESCSpeechPlayer + ).set_state("off") + + if not _should_preserve_dialog_box and _dialog_player.get_children().has(_type_player): + _dialog_player.remove_child(_type_player) + + say_finished.emit() + + +# To be called if voice audio has finished. +func voice_audio_finished(): + if is_instance_valid(_type_player): + _type_player.voice_audio_finished() diff --git a/addons/escoria-dialog-simple/esc_dialog_simple.gd.uid b/addons/escoria-dialog-simple/esc_dialog_simple.gd.uid new file mode 100644 index 0000000..57fe82e --- /dev/null +++ b/addons/escoria-dialog-simple/esc_dialog_simple.gd.uid @@ -0,0 +1 @@ +uid://dgyquhdnffck5 diff --git a/addons/escoria-dialog-simple/esc_dialog_simple_settings.gd b/addons/escoria-dialog-simple/esc_dialog_simple_settings.gd new file mode 100644 index 0000000..a750876 --- /dev/null +++ b/addons/escoria-dialog-simple/esc_dialog_simple_settings.gd @@ -0,0 +1,24 @@ +extends Resource +class_name SimpleDialogSettings + + +const SETTINGS_ROOT = "escoria/dialog_simple" + +const AVATARS_PATH = "%s/avatars_path" % SETTINGS_ROOT +const TEXT_TIME_PER_LETTER_MS = "%s/text_time_per_letter_ms" % SETTINGS_ROOT +const TEXT_TIME_PER_LETTER_MS_DEFAULT_VALUE = 100 +const TEXT_TIME_PER_LETTER_MS_FAST = "%s/text_time_per_fast_letter_ms" % SETTINGS_ROOT +const TEXT_TIME_PER_LETTER_MS_FAST_DEFAULT_VALUE = 25 +const READING_SPEED_IN_WPM = "%s/reading_speed_in_wpm" % SETTINGS_ROOT +const READING_SPEED_IN_WPM_DEFAULT_VALUE = 200 +const CLEAR_TEXT_BY_CLICK_ONLY = "%s/clear_text_by_click_only" % SETTINGS_ROOT +const LEFT_CLICK_ACTION = "%s/left_click_action" % SETTINGS_ROOT + +const STOP_TALKING_ANIMATION_ON = "%s/stop_talking_animation_on" % SETTINGS_ROOT + +const LEFT_CLICK_ACTION_SPEED_UP = "Speed up" +const LEFT_CLICK_ACTION_INSTANT_FINISH = "Instant finish" +const LEFT_CLICK_ACTION_NOTHING = "None" + +const STOP_TALKING_ANIMATION_ON_END_OF_TEXT = "End of text" +const STOP_TALKING_ANIMATION_ON_END_OF_AUDIO = "End of audio" diff --git a/addons/escoria-dialog-simple/esc_dialog_simple_settings.gd.uid b/addons/escoria-dialog-simple/esc_dialog_simple_settings.gd.uid new file mode 100644 index 0000000..b8e5107 --- /dev/null +++ b/addons/escoria-dialog-simple/esc_dialog_simple_settings.gd.uid @@ -0,0 +1 @@ +uid://cw5gdwad3515m diff --git a/addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd b/addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd new file mode 100644 index 0000000..4c48f60 --- /dev/null +++ b/addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd @@ -0,0 +1,34 @@ +extends StateMachine +## Instanciation of this dialogs state machine implementation. + +# Constructor +func _init(): + _create_states() + _add_states_to_machine() + current_state_name = "idle" + # This line is very important here: it defines the initial state of the + # dialogs state machine. Since Escoria can't guess which state is the default one + # it has to be assigned here. If it happens to be null on initialize() call, + # an error is triggered. + START_STATE = states_map[current_state_name] + initialize(START_STATE) + + +# Creates the states for this state machine. +func _create_states() -> void: + states_map = { + "idle": preload("res://addons/escoria-dialog-simple/states/dialog_idle.gd").new(), + "say": preload("res://addons/escoria-dialog-simple/states/dialog_say.gd").new(), + "say_fast": preload("res://addons/escoria-dialog-simple/states/dialog_say_fast.gd").new(), + "say_finish": preload("res://addons/escoria-dialog-simple/states/dialog_say_finish.gd").new(), + "visible": preload("res://addons/escoria-dialog-simple/states/dialog_visible.gd").new(), + "finish": preload("res://addons/escoria-dialog-simple/states/dialog_finish.gd").new(), + "interrupt": preload("res://addons/escoria-dialog-simple/states/dialog_interrupt.gd").new(), + "choices": preload("res://addons/escoria-dialog-simple/states/dialog_choices.gd").new() + } + + +# Adds any created states into the state machine as children. +func _add_states_to_machine() -> void: + for key in states_map: + add_child(states_map[key]) diff --git a/addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd.uid b/addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd.uid new file mode 100644 index 0000000..13db836 --- /dev/null +++ b/addons/escoria-dialog-simple/esc_dialog_simple_state_machine.gd.uid @@ -0,0 +1 @@ +uid://ck878sot1akca diff --git a/addons/escoria-dialog-simple/plugin.cfg b/addons/escoria-dialog-simple/plugin.cfg new file mode 100644 index 0000000..70cad5a --- /dev/null +++ b/addons/escoria-dialog-simple/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Escoria Simple Dialogs" +description="Very basic dialogs for Escoria based games" +author="Escoria project" +version="0.1.0" +script="plugin.gd" diff --git a/addons/escoria-dialog-simple/plugin.gd b/addons/escoria-dialog-simple/plugin.gd new file mode 100644 index 0000000..fd552f8 --- /dev/null +++ b/addons/escoria-dialog-simple/plugin.gd @@ -0,0 +1,145 @@ +@tool +# A simple dialog manager for Escoria +extends EditorPlugin + +const MANAGER_CLASS = "res://addons/escoria-dialog-simple/esc_dialog_simple.gd" + + +var left_click_actions: PackedStringArray = [ + SimpleDialogSettings.LEFT_CLICK_ACTION_SPEED_UP, + SimpleDialogSettings.LEFT_CLICK_ACTION_INSTANT_FINISH, + SimpleDialogSettings.LEFT_CLICK_ACTION_NOTHING +] + +var stop_talking_animation_on_options: PackedStringArray = [ + SimpleDialogSettings.STOP_TALKING_ANIMATION_ON_END_OF_TEXT, + SimpleDialogSettings.STOP_TALKING_ANIMATION_ON_END_OF_AUDIO +] + + +# Override function to return the plugin name. +func _get_plugin_name(): + return "escoria-dialog-simple" + + +# Unregister ourselves +func _disable_plugin(): + print("Disabling plugin Escoria Dialog Simple") + ESCProjectSettingsManager.remove_setting( + ESCProjectSettingsManager.DEFAULT_DIALOG_TYPE + ) + + ESCProjectSettingsManager.remove_setting( + SimpleDialogSettings.AVATARS_PATH + ) + + ESCProjectSettingsManager.remove_setting( + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS + ) + + ESCProjectSettingsManager.remove_setting( + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST + ) + + ESCProjectSettingsManager.remove_setting( + SimpleDialogSettings.CLEAR_TEXT_BY_CLICK_ONLY + ) + + ESCProjectSettingsManager.remove_setting( + SimpleDialogSettings.READING_SPEED_IN_WPM + ) + + ESCProjectSettingsManager.remove_setting( + SimpleDialogSettings.LEFT_CLICK_ACTION + ) + + ESCProjectSettingsManager.remove_setting( + SimpleDialogSettings.STOP_TALKING_ANIMATION_ON + ) + + EscoriaPlugin.deregister_dialog_manager(MANAGER_CLASS) + + +# Add ourselves to the list of dialog managers +func _enable_plugin(): + print("Enabling plugin Escoria Dialog Simple") + + if EscoriaPlugin.register_dialog_manager(self, MANAGER_CLASS): + ESCProjectSettingsManager.register_setting( + ESCProjectSettingsManager.DEFAULT_DIALOG_TYPE, + "floating", + { + "type": TYPE_STRING + } + ) + + ESCProjectSettingsManager.register_setting( + SimpleDialogSettings.AVATARS_PATH, + "res://game/dialog_avatars", + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_DIR + } + ) + + ESCProjectSettingsManager.register_setting( + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS, + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_DEFAULT_VALUE, + { + "type": TYPE_FLOAT + } + ) + + ESCProjectSettingsManager.register_setting( + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST, + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST_DEFAULT_VALUE, + { + "type": TYPE_FLOAT + } + ) + + ESCProjectSettingsManager.register_setting( + SimpleDialogSettings.CLEAR_TEXT_BY_CLICK_ONLY, + false, + { + "type": TYPE_BOOL + } + ) + + ESCProjectSettingsManager.register_setting( + SimpleDialogSettings.READING_SPEED_IN_WPM, + SimpleDialogSettings.READING_SPEED_IN_WPM_DEFAULT_VALUE, + { + "type": TYPE_INT + } + ) + + var left_click_actions_string: String = ",".join(left_click_actions) + + ESCProjectSettingsManager.register_setting( + SimpleDialogSettings.LEFT_CLICK_ACTION, + SimpleDialogSettings.LEFT_CLICK_ACTION_SPEED_UP, + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_ENUM, + "hint_string": left_click_actions_string + } + ) + + var stop_talking_animation_on_options_string: String = ",".join(stop_talking_animation_on_options) + + ESCProjectSettingsManager.register_setting( + SimpleDialogSettings.STOP_TALKING_ANIMATION_ON, + SimpleDialogSettings.STOP_TALKING_ANIMATION_ON_END_OF_AUDIO, + { + "type": TYPE_STRING, + "hint": PROPERTY_HINT_ENUM, + "hint_string": stop_talking_animation_on_options_string + } + ) + + else: + get_editor_interface().set_plugin_enabled( + _get_plugin_name(), + false + ) diff --git a/addons/escoria-dialog-simple/plugin.gd.uid b/addons/escoria-dialog-simple/plugin.gd.uid new file mode 100644 index 0000000..af1685b --- /dev/null +++ b/addons/escoria-dialog-simple/plugin.gd.uid @@ -0,0 +1 @@ +uid://xixeu3ksbt73 diff --git a/addons/escoria-dialog-simple/states/dialog_choices.gd b/addons/escoria-dialog-simple/states/dialog_choices.gd new file mode 100644 index 0000000..3779a6d --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_choices.gd @@ -0,0 +1,44 @@ +extends State + + +# The owning dialog player. +var _dialog_player + +# The dialog to start. +var _dialog: ESCDialog +var _type: String = "simple" + +var _dialog_chooser_ui: ESCDialogManager = null + +var _ready_to_choose: bool + + +func initialize(dialog_player, dialog_chooser_ui: ESCDialogManager, dialog: ESCDialog, type: String) -> void: + _dialog_player = dialog_player + _dialog_chooser_ui = dialog_chooser_ui + _dialog = dialog + _type = type + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'choices'.") + + if _dialog.options.is_empty(): + escoria.logger.error( + self, + "Received dialog options array was empty." + ) + + _ready_to_choose = true + + +func update(_delta): + if _ready_to_choose: + _ready_to_choose = false + _dialog_chooser_ui.do_choose(_dialog_player, _dialog, _type) + var option = await _dialog_chooser_ui.option_chosen + + escoria.logger.trace(self, "Dialog State Machine: 'choices' -> 'idle'") + + finished.emit("idle") + _dialog_player.option_chosen.emit(option) diff --git a/addons/escoria-dialog-simple/states/dialog_choices.gd.uid b/addons/escoria-dialog-simple/states/dialog_choices.gd.uid new file mode 100644 index 0000000..2e5d12c --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_choices.gd.uid @@ -0,0 +1 @@ +uid://vmw64noavihm diff --git a/addons/escoria-dialog-simple/states/dialog_finish.gd b/addons/escoria-dialog-simple/states/dialog_finish.gd new file mode 100644 index 0000000..7dac600 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_finish.gd @@ -0,0 +1,19 @@ +extends State + + +# Owning dialog player +var _dialog_player + + +func initialize(dialog_player) -> void: + _dialog_player = dialog_player + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'finish'.") + + +func update(_delta): + escoria.logger.trace(self, "Dialog State Machine: 'finish' -> 'idle'") + finished.emit("idle") + _dialog_player.say_finished.emit() diff --git a/addons/escoria-dialog-simple/states/dialog_finish.gd.uid b/addons/escoria-dialog-simple/states/dialog_finish.gd.uid new file mode 100644 index 0000000..e9cc6da --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_finish.gd.uid @@ -0,0 +1 @@ +uid://drpcjgaq0gd8j diff --git a/addons/escoria-dialog-simple/states/dialog_idle.gd b/addons/escoria-dialog-simple/states/dialog_idle.gd new file mode 100644 index 0000000..d53d89e --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_idle.gd @@ -0,0 +1,5 @@ +extends State + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'idle'.") diff --git a/addons/escoria-dialog-simple/states/dialog_idle.gd.uid b/addons/escoria-dialog-simple/states/dialog_idle.gd.uid new file mode 100644 index 0000000..1a0adf1 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_idle.gd.uid @@ -0,0 +1 @@ +uid://3huotinfsd51 diff --git a/addons/escoria-dialog-simple/states/dialog_interrupt.gd b/addons/escoria-dialog-simple/states/dialog_interrupt.gd new file mode 100644 index 0000000..83fb707 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_interrupt.gd @@ -0,0 +1,24 @@ +extends State + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + + +func initialize(dialog_manager: ESCDialogManager) -> void: + _dialog_manager = dialog_manager + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'interrupt'.") + + if _dialog_manager != null: + if not _dialog_manager.say_finished.is_connected(_on_say_finished): + _dialog_manager.say_finished.connect(_on_say_finished) + + _dialog_manager.interrupt() + + +func _on_say_finished() -> void: + escoria.logger.trace(self, "Dialog State Machine: 'interrupt' -> 'finish'") + finished.emit("finish") diff --git a/addons/escoria-dialog-simple/states/dialog_interrupt.gd.uid b/addons/escoria-dialog-simple/states/dialog_interrupt.gd.uid new file mode 100644 index 0000000..3cce4b7 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_interrupt.gd.uid @@ -0,0 +1 @@ +uid://cicognkh6ct0i diff --git a/addons/escoria-dialog-simple/states/dialog_say.gd b/addons/escoria-dialog-simple/states/dialog_say.gd new file mode 100644 index 0000000..c46aea0 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_say.gd @@ -0,0 +1,172 @@ +extends State + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + +# Character that is talking +var _character: String + +# UI to use for the dialog +var _type: String + +# Translation key +var _key: String = "" + +# Text to say +var _text: String + +var _ready_to_say: bool + +# flag for whether the dialog manager has started to "say" to ensure that it has +# prior to exiting this state as other states need this to happen to continue +# (other states rely on the setup that the dialog manager does) +var _say_started: bool + +var _stop_talking_animation_on_option: String + + +func initialize(dialog_manager: ESCDialogManager, character: String, text: String, type: String, key: String) -> void: + _dialog_manager = dialog_manager + _character = character + _text = text + _type = type + _key = key + _stop_talking_animation_on_option = \ + ESCProjectSettingsManager.get_setting(SimpleDialogSettings.STOP_TALKING_ANIMATION_ON) + + +func handle_input(_event): + if _event is InputEventMouseButton and _event.pressed: + if escoria.inputs_manager.input_mode != \ + escoria.inputs_manager.INPUT_NONE and \ + _dialog_manager != null: + + var left_click_action = ESCProjectSettingsManager.get_setting(SimpleDialogSettings.LEFT_CLICK_ACTION) + + _handle_left_click_action(left_click_action) + + +func _handle_left_click_action(left_click_action: String) -> void: + match left_click_action: + SimpleDialogSettings.LEFT_CLICK_ACTION_SPEED_UP: + if _dialog_manager.say_visible.is_connected(_on_say_visible): + _dialog_manager.say_visible.disconnect(_on_say_visible) + + escoria.logger.trace(self, "Dialog State Machine: 'say' -> 'say_fast'") + finished.emit("say_fast") + SimpleDialogSettings.LEFT_CLICK_ACTION_INSTANT_FINISH: + if _dialog_manager.say_visible.is_connected(_on_say_visible): + _dialog_manager.say_visible.disconnect(_on_say_visible) + + escoria.logger.trace(self, "Dialog State Machine: 'say' -> 'say_finish'") + finished.emit("say_finish") + + get_viewport().set_input_as_handled() + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'say'.") + + _say_started = false + + if not _dialog_manager.say_visible.is_connected(_on_say_visible): + _dialog_manager.say_visible.connect(_on_say_visible) + + if _key and not _key.is_empty(): + var _speech_resource = _get_voice_file(_key) + + if _speech_resource == "": + escoria.logger.warn( + self, + "Unable to find voice file with key '%s'." % _key + ) + else: + ( + escoria.object_manager.get_object(escoria.object_manager.SPEECH).node\ + as ESCSpeechPlayer + ).set_state(_speech_resource) + + if _stop_talking_animation_on_option == SimpleDialogSettings.STOP_TALKING_ANIMATION_ON_END_OF_AUDIO: + if not ( + escoria.object_manager.get_object(escoria.object_manager.SPEECH).node\ + as ESCSpeechPlayer + ).stream.finished.is_connected(_on_audio_finished): + + ( + escoria.object_manager.get_object(escoria.object_manager.SPEECH).node\ + as ESCSpeechPlayer + ).stream.finished.connect(_on_audio_finished) + + var translated_text: String = tr(_key) + + # Only update the text if the translated text was found; otherwise, raise + # a warning and use the original, untranslated text. + if translated_text == _key: + escoria.logger.warn( + self, + "Unable to find translation key '%s'. Using untranslated text." % _key + ) + else: + _text = translated_text + + _ready_to_say = true + + +func exit() -> void: + if not _say_started: + _dialog_manager.do_say(_character, _text) + _say_started = true + + +func update(_delta): + if _ready_to_say: + _dialog_manager.do_say(_character, _text) + _say_started = true + _ready_to_say = false + + +# Find the matching voice output file for the given key +# +# #### Parameters +# +# - key: Text key provided +# - start: Starting folder to search for voices +# +# **Returns** The path to the matching voice file +func _get_voice_file(key: String, start: String = "") -> String: + if start == "": + start = ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_FOLDER + ) + var _dir = DirAccess.open(start) + if _dir != null: + _dir.list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547 + var file_name = _dir.get_next() + while file_name != "": + if _dir.current_is_dir(): + var _voice_file = _get_voice_file( + key, + start.path_join(file_name) + ) + if _voice_file != "": + return _voice_file + else: + if file_name == "%s.%s.import" % [ + key, + ESCProjectSettingsManager.get_setting( + ESCProjectSettingsManager.SPEECH_EXTENSION + ) + ]: + return start.path_join(file_name.trim_suffix(".import")) + file_name = _dir.get_next() + return "" + + +func _on_say_visible() -> void: + escoria.logger.trace(self, "Dialog State Machine: 'say' -> 'visible'") + finished.emit("visible") + + +func _on_audio_finished() -> void: + _dialog_manager.voice_audio_finished() diff --git a/addons/escoria-dialog-simple/states/dialog_say.gd.uid b/addons/escoria-dialog-simple/states/dialog_say.gd.uid new file mode 100644 index 0000000..c51fafb --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_say.gd.uid @@ -0,0 +1 @@ +uid://u44euoojg2al diff --git a/addons/escoria-dialog-simple/states/dialog_say_fast.gd b/addons/escoria-dialog-simple/states/dialog_say_fast.gd new file mode 100644 index 0000000..14d9f05 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_say_fast.gd @@ -0,0 +1,29 @@ +extends State + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + + +func initialize(dialog_manager: ESCDialogManager) -> void: + _dialog_manager = dialog_manager + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'say_fast'.") + + if escoria.inputs_manager.input_mode != \ + escoria.inputs_manager.INPUT_NONE and \ + _dialog_manager != null: + + if not _dialog_manager.say_visible.is_connected(_on_say_visible): + _dialog_manager.say_visible.connect(_on_say_visible) + + _dialog_manager.speedup() + else: + escoria.logger.error(self, "Illegal state.") + + +func _on_say_visible() -> void: + escoria.logger.trace(self, "Dialog State Machine: 'say_fast' -> 'visible'") + finished.emit("visible") diff --git a/addons/escoria-dialog-simple/states/dialog_say_fast.gd.uid b/addons/escoria-dialog-simple/states/dialog_say_fast.gd.uid new file mode 100644 index 0000000..5954fcd --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_say_fast.gd.uid @@ -0,0 +1 @@ +uid://cqeelw42dgxp8 diff --git a/addons/escoria-dialog-simple/states/dialog_say_finish.gd b/addons/escoria-dialog-simple/states/dialog_say_finish.gd new file mode 100644 index 0000000..0e5d8aa --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_say_finish.gd @@ -0,0 +1,29 @@ +extends State + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + + +func initialize(dialog_manager: ESCDialogManager) -> void: + _dialog_manager = dialog_manager + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'say_finish'.") + + if escoria.inputs_manager.input_mode != \ + escoria.inputs_manager.INPUT_NONE and \ + _dialog_manager != null: + + if not _dialog_manager.say_visible.is_connected(_on_say_visible): + _dialog_manager.say_visible.connect(_on_say_visible) + + _dialog_manager.finish() + else: + escoria.logger.error(self, "Illegal state.") + + +func _on_say_visible() -> void: + escoria.logger.trace(self, "Dialog State Machine: 'say_finish' -> 'visible'") + finished.emit("visible") diff --git a/addons/escoria-dialog-simple/states/dialog_say_finish.gd.uid b/addons/escoria-dialog-simple/states/dialog_say_finish.gd.uid new file mode 100644 index 0000000..c2bf455 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_say_finish.gd.uid @@ -0,0 +1 @@ +uid://cckwcr7x1kao diff --git a/addons/escoria-dialog-simple/states/dialog_visible.gd b/addons/escoria-dialog-simple/states/dialog_visible.gd new file mode 100644 index 0000000..d885cb3 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_visible.gd @@ -0,0 +1,34 @@ +extends State + + +# Reference to the currently playing dialog manager +var _dialog_manager: ESCDialogManager = null + + +func initialize(dialog_manager: ESCDialogManager) -> void: + _dialog_manager = dialog_manager + + +func enter(): + escoria.logger.trace(self, "Dialog State Machine: Entered 'visible'.") + + if not _dialog_manager.say_finished.is_connected(_on_say_finished): + _dialog_manager.say_finished.connect(_on_say_finished) + + +func handle_input(_event): + if _event is InputEventMouseButton and _event.pressed: + if escoria.inputs_manager.input_mode != \ + escoria.inputs_manager.INPUT_NONE: + + if _dialog_manager.say_finished.is_connected(_on_say_finished): + _dialog_manager.say_finished.disconnect(_on_say_finished) + + finished.emit("interrupt") + get_viewport().set_input_as_handled() + + +# Handles the end of a say function after it has emitted say_finished. +func _on_say_finished(): + escoria.logger.trace(self, "Dialog State Machine: 'visible' -> 'finish'") + finished.emit("finish") diff --git a/addons/escoria-dialog-simple/states/dialog_visible.gd.uid b/addons/escoria-dialog-simple/states/dialog_visible.gd.uid new file mode 100644 index 0000000..860ace8 --- /dev/null +++ b/addons/escoria-dialog-simple/states/dialog_visible.gd.uid @@ -0,0 +1 @@ +uid://b7hcll3bfcp8u diff --git a/addons/escoria-dialog-simple/types/avatar.gd b/addons/escoria-dialog-simple/types/avatar.gd new file mode 100644 index 0000000..ce0e254 --- /dev/null +++ b/addons/escoria-dialog-simple/types/avatar.gd @@ -0,0 +1,239 @@ +# A dialog GUI showing a dialog box and character portraits +extends Window + + +# Signal emitted when text has been said +signal say_finished + +# Signal emitted when text has just become fully visible +signal say_visible + + +# The text speed per character for normal display +var _text_time_per_character: float + +# The text speed per character if the dialog line is skipped +var _fast_text_time_per_character: float + +# The reading speed to be used in determining the length of time text remains +# on the screen. +var _reading_speed_in_wpm: int + +# Used to extract words from lines of text. +var _word_regex: RegEx = RegEx.new() + +# Whether the current dialog is speeding up +var _is_speeding_up: bool = false + +# The current line of text being displayed. +var _current_line: String + + +# The node holding the avatar +@onready var avatar_node = $Panel/MarginContainer/HSplitContainer/VBoxContainer\ + /avatar + +# The node showing the text +@onready var text_node = $Panel/MarginContainer/HSplitContainer/text + +# The tween node for text animations +@onready var tween: Tween3 = Tween3.new(self) + +# Whether the dialog manager is paused +@onready var is_paused: bool = true + +# Build up the UI +func _ready(): + _text_time_per_character = ProjectSettings.get_setting( + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS + ) + + if _text_time_per_character < 0: + escoria.logger.warn( + self, + "%s setting must be a non-negative number. Will use default value of %s." % + [ + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS, + escoria.TEXT_TIME_PER_LETTER_MS_DEFAULT_VALUE + ] + ) + + _text_time_per_character = escoria.TEXT_TIME_PER_LETTER_MS_DEFAULT_VALUE + + _fast_text_time_per_character = ProjectSettings.get_setting( + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST + ) + + if _fast_text_time_per_character < 0: + escoria.logger.warn( + self, + "%s setting must be a non-negative number. Will use default value of %s." % + [ + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST, + escoria.TEXT_TIME_PER_LETTER_MS_FAST_DEFAULT_VALUE + ] + ) + + _fast_text_time_per_character = escoria.TEXT_TIME_PER_LETTER_MS_FAST_DEFAULT_VALUE + + _reading_speed_in_wpm = ProjectSettings.get_setting( + SimpleDialogSettings.READING_SPEED_IN_WPM + ) + + if _reading_speed_in_wpm <= 0: + escoria.logger.warn( + self, + "%s setting must be a positive number. Will use default value of %s." % + [ + SimpleDialogSettings.READING_SPEED_IN_WPM, + escoria.READING_SPEED_IN_WPM_DEFAULT_VALUE + ] + ) + + _reading_speed_in_wpm = escoria.READING_SPEED_IN_WPM_DEFAULT_VALUE + + _word_regex.compile("\\S+") + + text_node.bbcode_enabled = true + tween.finished.connect(_on_dialog_line_typed.bind("", "")) + + escoria.paused.connect(_on_paused) + escoria.resumed.connect(_on_resumed) + + tree_exited.connect(_on_tree_exited) + + +# Switch the current character +# +# #### Parameters +# - name: The name of the current character +func set_current_character(name: String): + if ProjectSettings.get_setting("escoria/dialog_simple/avatars_path").is_empty(): + escoria.logger.warn(self, "Unable to load avatar '%s': Avatar path not specified" % name) + return + + var avatar = "%s/%s.tres" % [ + ProjectSettings.get_setting("escoria/dialog_simple/avatars_path"), + name + ] + if ResourceLoader.exists(avatar): + avatar_node.texture = ResourceLoader.load(avatar) + + if avatar_node.texture is AnimatedTexture: + avatar_node.texture.current_frame = 0 + avatar_node.texture.pause = false + else: + escoria.logger.warn(self, "Unable to load avatar '%s': Resource not found in path '%s'" % + [name, ProjectSettings.get_setting("escoria/dialog_simple/avatars_path")]) + + +# Make a character say something +# +# #### Parameters +# - character: The global id of the character speaking +# - line: Line to say +func say(character: String, line: String): + _current_line = line + + _is_speeding_up = false + + popup_centered() + set_current_character(character) + + text_node.text = tr(line) + + text_node.visible_ratio = 0.0 + var time_show_full_text = _text_time_per_character / 1000 * len(line) + + tween.reset() + + tween.interpolate_property(text_node, "visible_ratio", + 0.0, 1.0, time_show_full_text, + Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) + tween.play() + + +# Called by the dialog player when the +func speedup(): + if not _is_speeding_up: + _is_speeding_up = true + var time_show_full_text = _fast_text_time_per_character / 1000 * len(_current_line) + tween.reset() + tween.interpolate_property(text_node, "visible_ratio", + text_node.visible_ratio, 1.0, time_show_full_text, + Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) + tween.play() + + +# Called by the dialog player when user wants to finish dialogue immediately. +func finish(): + tween.reset() + tween.interpolate_property(text_node, "visible_ratio", + text_node.visible_ratio, 1.0, 0.0) + tween.play() + + +# To be called if voice audio has finished. +func voice_audio_finished(): + if avatar_node and avatar_node.texture: + avatar_node.texture.current_frame = 0 + avatar_node.texture.pause = true + + +# The dialog line was printed, start the waiting time and then finish +# the dialog +func _on_dialog_line_typed(object, key): + if avatar_node.texture is AnimatedTexture: + avatar_node.texture.current_frame = 0 + avatar_node.texture.pause = true + + text_node.visible_characters = -1 + + var time_to_disappear: float = _calculate_time_to_disappear() + + if not $Timer.timeout.is_connected(_on_dialog_finished): + $Timer.timeout.connect(_on_dialog_finished) + + $Timer.start(time_to_disappear) + + say_visible.emit() + + +func _calculate_time_to_disappear() -> float: + return (_get_number_of_words() / _reading_speed_in_wpm as float) * 60 + + +func _get_number_of_words() -> int: + return _word_regex.search_all(text_node.get_text()).size() + + +# Ending the dialog +func _on_dialog_finished(): + $Timer.stop() + + # Only trigger to clear the text if we aren't limiting the clearing trigger to a click. + if not ESCProjectSettingsManager.get_setting(SimpleDialogSettings.CLEAR_TEXT_BY_CLICK_ONLY): + say_finished.emit() + + +# Handler managing pause notification from Escoria +func _on_paused(): + if tween.is_running(): + is_paused = true + tween.stop() + + +# Handler managing resume notification from Escoria +func _on_resumed(): + if not tween.is_running(): + # We can't rely on "show()" to make an invisible popup reappear, as per the docs for + # CanvasItem. Instead, we need to use one of the popup_* methods. + if is_inside_tree(): + popup_centered() + + is_paused = false + tween.resume() + + +func _on_tree_exited(): + queue_free() diff --git a/addons/escoria-dialog-simple/types/avatar.gd.uid b/addons/escoria-dialog-simple/types/avatar.gd.uid new file mode 100644 index 0000000..8b66ed8 --- /dev/null +++ b/addons/escoria-dialog-simple/types/avatar.gd.uid @@ -0,0 +1 @@ +uid://cfkvypxfuu2mt diff --git a/addons/escoria-dialog-simple/types/avatar.tscn b/addons/escoria-dialog-simple/types/avatar.tscn new file mode 100644 index 0000000..6136f5f --- /dev/null +++ b/addons/escoria-dialog-simple/types/avatar.tscn @@ -0,0 +1,53 @@ +[gd_scene load_steps=2 format=3 uid="uid://cp75ofyuetxux"] + +[ext_resource type="Script" uid="uid://cfkvypxfuu2mt" path="res://addons/escoria-dialog-simple/types/avatar.gd" id="1"] + +[node name="dialog_box" type="Window"] +position = Vector2i(0, 36) +size = Vector2i(510, 180) +visible = false +unresizable = true +borderless = true +popup_window = true +script = ExtResource("1") + +[node name="Timer" type="Timer" parent="."] + +[node name="Panel" type="Panel" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="MarginContainer" type="MarginContainer" parent="Panel"] +layout_mode = 0 +anchor_right = 1.0 +anchor_bottom = 1.0 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="HSplitContainer" type="HSplitContainer" parent="Panel/MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 35 +dragger_visibility = 1 + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel/MarginContainer/HSplitContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.3 + +[node name="avatar" type="TextureRect" parent="Panel/MarginContainer/HSplitContainer/VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +stretch_mode = 4 + +[node name="text" type="RichTextLabel" parent="Panel/MarginContainer/HSplitContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +bbcode_enabled = true +text = "Here be some text" diff --git a/addons/escoria-dialog-simple/types/floating.gd b/addons/escoria-dialog-simple/types/floating.gd new file mode 100644 index 0000000..1ca78d6 --- /dev/null +++ b/addons/escoria-dialog-simple/types/floating.gd @@ -0,0 +1,287 @@ +# A dialog UI using a label above the head of the character +extends RichTextLabel + + +# Signal emitted when text has been said +signal say_finished + +# Signal emitted when text has just become fully visible +signal say_visible + + +# The text speed per character for normal display +var _text_time_per_character: float + +# The text speed per character if the dialog line is skipped +var _fast_text_time_per_character: float + +# The reading speed to be used in determining the length of time text remains +# on the screen. +var _reading_speed_in_wpm: int + +# Used to extract words from lines of text. +var _word_regex: RegEx = RegEx.new() + + +# Current character speaking, to keep track of reference for animation purposes +var _current_character + +# Whether the current dialog is speeding up +var _is_speeding_up: bool = false + +# The current line of text being displayed. +var _current_line: String + + +# Tween node for text animation +@onready var tween: Tween3 = Tween3.new(self) + +# The node showing the text +@onready var text_node: RichTextLabel = self + +# Whether the dialog manager is paused +@onready var is_paused: bool = true + +var dialog_location_node = null + +# Enable bbcode and catch the signal when a tween completed +func _ready(): + _text_time_per_character = ProjectSettings.get_setting( + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS + ) + + if _text_time_per_character < 0: + escoria.logger.warn( + self, + "%s setting must be a non-negative number. Will use default value of %s." % + [ + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS, + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_DEFAULT_VALUE + ] + ) + + _text_time_per_character = SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_DEFAULT_VALUE + + _fast_text_time_per_character = ProjectSettings.get_setting( + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST + ) + + if _fast_text_time_per_character < 0: + escoria.logger.warn( + self, + "%s setting must be a non-negative number. Will use default value of %s." % + [ + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST, + SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST_DEFAULT_VALUE + ] + ) + + _fast_text_time_per_character = SimpleDialogSettings.TEXT_TIME_PER_LETTER_MS_FAST_DEFAULT_VALUE + + _reading_speed_in_wpm = ProjectSettings.get_setting( + SimpleDialogSettings.READING_SPEED_IN_WPM + ) + + if _reading_speed_in_wpm <= 0: + escoria.logger.warn( + self, + "%s setting must be a positive number. Will use default value of %s." % + [ + SimpleDialogSettings.READING_SPEED_IN_WPM, + SimpleDialogSettings.READING_SPEED_IN_WPM_DEFAULT_VALUE + ] + ) + + _reading_speed_in_wpm = SimpleDialogSettings.READING_SPEED_IN_WPM_DEFAULT_VALUE + + _word_regex.compile("\\S+") + + bbcode_enabled = true + + tween.finished.connect(_on_dialog_line_typed.bind("", "")) + + tree_exiting.connect(_on_tree_exiting) + + escoria.paused.connect(_on_paused) + escoria.resumed.connect(_on_resumed) + + _current_line = "" + + +func _process(delta): + if _current_character.is_inside_tree() and \ + is_instance_valid(dialog_location_node): + # Position the RichTextLabel on the character's dialog position, if any. + position = dialog_location_node.get_global_transform_with_canvas().origin + position.x -= size.x / 2 + + _account_for_margin_x() + + _account_for_margin_y() + + +# Make a character say something +# +# #### Parameters +# - character: The global id of the character speaking +# - line: Line to say +func say(character: String, line: String) : + _current_line = line + + show() + + _is_speeding_up = false + + # Position the RichTextLabel on the character's dialog position, if any. + _current_character = escoria.object_manager.get_object(character).node + + var dialog_location_count:int = 0 + + for c in escoria.object_manager.get_object(character).node.get_children(): + if c is Marker2D: + # Identify any Postion2D nodes + if c is ESCDialogLocation: + dialog_location_count += 1 + dialog_location_node = c + + if dialog_location_count > 1: + escoria.logger.warn( + self, + "Multiple ESCDialogLocation nodes found " + + "object %s. Last one will be used." % _current_character) + + # Set text color to color set in the actor + var text_color = _current_character.dialog_color + var text_color_html = text_color.to_html(false) + + text_node.text = "[center][color=#" + text_color_html + "]" \ + .format([text_color_html]) + tr(line) + "[/color][center]" + + if _current_character.is_inside_tree() and \ + is_instance_valid(dialog_location_node): + position = dialog_location_node.get_global_transform_with_canvas().origin + + position.x -= size.x / 2 + else: + position.x = 0 + size.x = ProjectSettings.get_setting("display/window/size/viewport_width") + + _account_for_margin_x() + + _account_for_margin_y() + + _current_character.start_talking() + + text_node.visible_ratio = 0.0 + var time_show_full_text = _text_time_per_character / 1000 * len(_current_line) + + tween.reset() + + tween.interpolate_property(text_node, "visible_ratio", + 0.0, 1.0, time_show_full_text, + Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) + tween.play() + set_process(true) + + +# Called by the dialog player when user wants to finish dialogue fast. +func speedup(): + if not _is_speeding_up: + _is_speeding_up = true + var time_show_full_text = _fast_text_time_per_character / 1000 * len(_current_line) + + tween.reset() + + tween.interpolate_property(text_node, "visible_ratio", + text_node.visible_ratio, 1.0, time_show_full_text, + Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) + tween.play() + + +# Called by the dialog player when user wants to finish dialogue immediately. +func finish(): + tween.reset() + + tween.interpolate_property(text_node, "visible_ratio", + text_node.visible_ratio, 1.0, 0.0) + tween.play() + + +# To be called if voice audio has finished. +func voice_audio_finished(): + _stop_character_talking() + + +# The dialog line was printed, start the waiting time and then finish +# the dialog +func _on_dialog_line_typed(object, key): + _stop_character_talking() + text_node.visible_characters = -1 + + var time_to_disappear: float = _calculate_time_to_disappear() + $Timer.start(time_to_disappear) + $Timer.timeout.connect(_on_dialog_finished) + + say_visible.emit() + + +func _calculate_time_to_disappear() -> float: + return (_get_number_of_words() / _reading_speed_in_wpm as float) * 60 + + +func _get_number_of_words() -> int: + return _word_regex.search_all(text_node.get_text()).size() + + +# Ending the dialog +func _on_dialog_finished(): + # Only trigger to clear the text if we aren't limiting the clearing trigger to a click. + if not ESCProjectSettingsManager.get_setting(SimpleDialogSettings.CLEAR_TEXT_BY_CLICK_ONLY): + say_finished.emit() + + +# Handler managing pause notification from Escoria +func _on_paused(): + if tween.is_running(): + is_paused = true + tween.stop() + + +# Handler managing resume notification from Escoria +func _on_resumed(): + if not tween.is_running(): + is_paused = false + tween.resume() + + + # Handler to deal with this node being removed +func _on_tree_exiting() -> void: + _stop_character_talking() + + +func _stop_character_talking(): + # Make the speaking item animation stop talking, if it is still alive + if is_instance_valid(_current_character) and _current_character != null: + _current_character.stop_talking() + + +func _account_for_margin_x() -> void: + if position.x < 0: + position.x = 0 + + var screen_margin_x = position.x + size.x - \ + ProjectSettings.get("display/window/size/viewport_width") + + if screen_margin_x > 0: + position.x -= screen_margin_x + + +func _account_for_margin_y() -> void: + if position.y < 0: + position.y = 0 + + var screen_margin_y = position.y + size.y - \ + ProjectSettings.get("display/window/size/viewport_height") + + if screen_margin_y > 0: + position.y -= screen_margin_y diff --git a/addons/escoria-dialog-simple/types/floating.gd.uid b/addons/escoria-dialog-simple/types/floating.gd.uid new file mode 100644 index 0000000..62a7e80 --- /dev/null +++ b/addons/escoria-dialog-simple/types/floating.gd.uid @@ -0,0 +1 @@ +uid://jhr1gt0fmm0u diff --git a/addons/escoria-dialog-simple/types/floating.tscn b/addons/escoria-dialog-simple/types/floating.tscn new file mode 100644 index 0000000..f3ee470 --- /dev/null +++ b/addons/escoria-dialog-simple/types/floating.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://duatpb2fc36db"] + +[ext_resource type="Script" uid="uid://jhr1gt0fmm0u" path="res://addons/escoria-dialog-simple/types/floating.gd" id="1"] + +[node name="dialog_label" type="RichTextLabel"] +offset_right = 643.0 +offset_bottom = 60.0 +bbcode_enabled = true +text = "Here be some text." +script = ExtResource("1") + +[node name="Timer" type="Timer" parent="."] diff --git a/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.tres b/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.tres new file mode 100644 index 0000000..f64ce3b --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.tres @@ -0,0 +1,7 @@ +[gd_resource type="FontFile" load_steps=2 format=2] + +[ext_resource path="res://addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf" type="FontFile" id=1] + +[resource] +size = 21 +font_data = ExtResource( 1 ) diff --git a/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf b/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf new file mode 100644 index 0000000..eec6f63 Binary files /dev/null and b/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf differ diff --git a/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf.import b/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf.import new file mode 100644 index 0000000..6799af1 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://bl733mjk2noo8" +path="res://.godot/imported/caslonantique.ttf-2e335cc87aa9bb7b4f1f86dffaccc876.fontdata" + +[deps] + +source_file="res://addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.ttf" +dest_files=["res://.godot/imported/caslonantique.ttf-2e335cc87aa9bb7b4f1f86dffaccc876.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/escoria-ui-keyboard-9verbs/game.gd b/addons/escoria-ui-keyboard-9verbs/game.gd new file mode 100644 index 0000000..0e55973 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/game.gd @@ -0,0 +1,404 @@ +extends ESCGame + + +const VERB_CLOSE = "close" +const VERB_GIVE = "give" +const VERB_LOOK = "look" +const VERB_OPEN = "open" +const VERB_PICKUP = "pickup" +const VERB_PULL = "pull" +const VERB_PUSH = "push" +const VERB_TALK = "talk" +const VERB_USE = "use" + + +""" +Implement methods to react to inputs. + +- left_click_on_bg(position: Vector2) +- right_click_on_bg(position: Vector2) +- left_double_click_on_bg(position: Vector2) + +- element_focused(element_id: String) +- element_unfocused() + +- left_click_on_item(item_global_id: String, event: InputEvent) +- right_click_on_item(item_global_id: String, event: InputEvent) +- left_double_click_on_item(item_global_id: String, event: InputEvent) + +- left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) +- right_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) +- left_double_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) +- inventory_item_focused(inventory_item_global_id: String) +- inventory_item_unfocused() +- open_inventory() +- close_inventory() + +- mousewheel_action(direction: int) + +- hide_ui() +- show_ui() + +- pause_game() +- unpause_game() +- show_main_menu() +- hide_main_menu() + +- apply_custom_settings() + +- _on_event_done(event_name: String) +""" + + +@onready var verbs_menu = $ui/Control/panel_down/VBoxContainer/HBoxContainer\ + /VerbsMargin/verbs_menu +@onready var tooltip = $ui/Control/panel_down/VBoxContainer/MarginContainer\ + /tooltip +@onready var room_select = $ui/Control/panel_down/VBoxContainer/HBoxContainer\ + /MainMargin/VBoxContainer/room_select +@onready var inventory_ui = $ui/Control/panel_down/VBoxContainer/HBoxContainer\ + /InventoryMargin/inventory_ui +const input_map = preload("res://addons/escoria-ui-keyboard-9verbs/input_map.gd") + +func _enter_tree(): + var room_selector_parent = $ui/Control/panel_down/VBoxContainer\ + /HBoxContainer/MainMargin/VBoxContainer + + if ProjectSettings.get_setting("escoria/debug/enable_room_selector") and \ + room_selector_parent.get_node_or_null("room_select") == null: + room_selector_parent.add_child( + preload( + "res://addons/escoria-core/ui_library/tools/room_select" +\ + "/room_select.tscn" + ).instantiate() + ) + + var input_handler = Callable(self, "_process_input") + escoria.inputs_manager.register_custom_input_handler(input_handler) + input_map.add_actions_to_input_map() + + +func _exit_tree(): + escoria.inputs_manager.register_custom_input_handler(null) + input_map.erase_actions_from_input_map() + + +func _process_input(event: InputEvent, is_default_state: bool) -> bool: + if not is_default_state: + return false + elif event.is_action_pressed(input_map.ACTION_SET_VERB_OPEN): + verbs_menu.on_action_selected(VERB_OPEN) + return true + elif event.is_action_pressed(input_map.ACTION_SET_VERB_PICKUP): + verbs_menu.on_action_selected(VERB_PICKUP) + return true + elif event.is_action_pressed(input_map.ACTION_SET_VERB_PUSH): + verbs_menu.on_action_selected(VERB_PUSH) + return true + elif event.is_action_pressed(input_map.ACTION_SET_VERB_CLOSE): + verbs_menu.on_action_selected(VERB_CLOSE) + return true + elif event.is_action_pressed(input_map.ACTION_SET_VERB_LOOK): + verbs_menu.on_action_selected(VERB_LOOK) + return true + elif event.is_action_pressed(input_map.ACTION_SET_VERB_PULL): + verbs_menu.on_action_selected(VERB_PULL) + return true + elif event.is_action_pressed(input_map.ACTION_SET_VERB_GIVE): + verbs_menu.on_action_selected(VERB_GIVE) + return true + elif event.is_action_pressed(input_map.ACTION_SET_VERB_USE): + verbs_menu.on_action_selected(VERB_USE) + return true + elif event.is_action_pressed(input_map.ACTION_SET_VERB_TALK): + verbs_menu.on_action_selected(VERB_TALK) + return true + else: + return false + + +## BACKGROUND ## + +func left_click_on_bg(position: Vector2) -> void: + if escoria.main.current_scene.player: + escoria.action_manager.do( + escoria.action_manager.ACTION.BACKGROUND_CLICK, + [escoria.main.current_scene.player.global_id, position], + true + ) + escoria.action_manager.clear_current_action() + escoria.action_manager.clear_current_tool() + tooltip.clear() + verbs_menu.unselect_actions() + + +func right_click_on_bg(position: Vector2) -> void: + if escoria.main.current_scene.player: + escoria.action_manager.do( + escoria.action_manager.ACTION.BACKGROUND_CLICK, + [escoria.main.current_scene.player.global_id, position], + true + ) + escoria.action_manager.clear_current_action() + escoria.action_manager.clear_current_tool() + tooltip.clear() + verbs_menu.unselect_actions() + + +func left_double_click_on_bg(position: Vector2) -> void: + if escoria.main.current_scene.player: + escoria.action_manager.do( + escoria.action_manager.ACTION.BACKGROUND_CLICK, + [escoria.main.current_scene.player.global_id, position, true], + true + ) + escoria.action_manager.clear_current_action() + verbs_menu.unselect_actions() + + +## ITEM FOCUS ## + +func element_focused(element_id: String) -> void: + var target_obj = escoria.object_manager.get_object(element_id).node + + match escoria.action_manager.action_state: + # Don't change the tooltip if an action input is completed + # (ie verb+item(+target)) because the action is now being executed + # and the tooltip is already set because the item was focused + # (see element_focused() and inventory_item_focused()) + ESCActionManager.ACTION_INPUT_STATE.COMPLETED: + return + + ESCActionManager.ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM, \ + ESCActionManager.ACTION_INPUT_STATE.AWAITING_ITEM: + tooltip.set_target(target_obj.tooltip_name) + + ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM: + tooltip.set_target2(target_obj.tooltip_name) + + +func element_unfocused() -> void: + match escoria.action_manager.action_state: + # Don't change the tooltip if an action input is completed + # (ie verb+item(+target)) because the action is now being executed + # and the tooltip is already set because the item was focused + # (see element_focused() and inventory_item_focused()) + ESCActionManager.ACTION_INPUT_STATE.COMPLETED: + return + + ESCActionManager.ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM, \ + ESCActionManager.ACTION_INPUT_STATE.AWAITING_ITEM: + tooltip.set_target("") + verbs_menu.unselect_actions() + + ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM: + tooltip.set_target2("") + + + +## ITEMS ## +func left_click_on_item(item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_LEFT_CLICK, + [item_global_id, event], + true + ) + + var target_obj = escoria.object_manager.get_object( + item_global_id + ).node + + match escoria.action_manager.action_state: + # Don't change the tooltip if an action input is completed + # (ie verb+item(+target)) because the action is now being executed + # and the tooltip is already set because the item was focused + # (see element_focused() and inventory_item_focused()) + ESCActionManager.ACTION_INPUT_STATE.COMPLETED: + return + + # Just clicked on the item + ESCActionManager.ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM, \ + ESCActionManager.ACTION_INPUT_STATE.AWAITING_ITEM: + tooltip.set_target(target_obj.tooltip_name) + + # Clicked on item and now we're awaiting a target item + # This means we clicked the tool and we now need a target + ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM: + tooltip.set_target(target_obj.tooltip_name, true) + + + +func right_click_on_item(item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.set_current_action(verbs_menu.selected_action) + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_RIGHT_CLICK, + [item_global_id, event], + true + ) + + +func left_double_click_on_item(item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_LEFT_CLICK, + [item_global_id, event], + true + ) + + +## INVENTORY ## +func left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_LEFT_CLICK, + [inventory_item_global_id, event] + ) + + var target_obj = escoria.object_manager.get_object( + inventory_item_global_id + ).node + + match escoria.action_manager.action_state: + # Don't change the tooltip if an action input is completed + # (ie verb+item(+target)) because the action is now being executed + # and the tooltip is already set because the item was focused + # (see element_focused() and inventory_item_focused()) + ESCActionManager.ACTION_INPUT_STATE.COMPLETED: + return + + # Just clicked on the inventory item: do nothing special + ESCActionManager.ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM, \ + ESCActionManager.ACTION_INPUT_STATE.AWAITING_ITEM: + return + + # Clicked on inventory item and now we're awaiting a target item + # This means we clicked the tool and we now need a target + ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM: + tooltip.set_target(target_obj.tooltip_name, true) + + +func right_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.set_current_action(verbs_menu.selected_action) + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_RIGHT_CLICK, + [inventory_item_global_id, event] + ) + + +func left_double_click_on_inventory_item(_inventory_item_global_id: String, _event: InputEvent) -> void: + pass + + +func inventory_item_focused(inventory_item_global_id: String) -> void: + var target_obj = escoria.object_manager.get_object( + inventory_item_global_id + ).node + + match escoria.action_manager.action_state: + # Don't change the tooltip if an action input is completed + # (ie verb+item(+target)) because the action is now being executed + # and the tooltip is already set because the item was focused + # (see element_focused() and inventory_item_focused()) + ESCActionManager.ACTION_INPUT_STATE.COMPLETED: + return + + ESCActionManager.ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM, \ + ESCActionManager.ACTION_INPUT_STATE.AWAITING_ITEM: + tooltip.set_target(target_obj.tooltip_name) + + # Hovering an ESCItem highlights its default action + if escoria.action_manager.current_action != VERB_USE and target_obj is ESCItem: + verbs_menu.set_by_name(target_obj.default_action) + + ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM: + tooltip.set_target2(target_obj.tooltip_name) + + +func inventory_item_unfocused() -> void: + + match escoria.action_manager.action_state: + ESCActionManager.ACTION_INPUT_STATE.COMPLETED: + # Don't change the tooltip if an action input is completed + # (ie verb+item(+target)) because the action is now being executed + return + + ESCActionManager.ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM, \ + ESCActionManager.ACTION_INPUT_STATE.AWAITING_ITEM: + tooltip.set_target("") + verbs_menu.unselect_actions() + + ESCActionManager.ACTION_INPUT_STATE.AWAITING_TARGET_ITEM: + tooltip.set_target2("") + + +func open_inventory(): + pass + + +func close_inventory(): + pass + + +func mousewheel_action(_direction: int): + pass + + +func hide_ui(): + $ui/Control.hide() + verbs_menu.hide() + if ProjectSettings.get("escoria/debug/enable_room_selector") == true: + room_select.hide() + inventory_ui.hide() + tooltip.hide() + + +func show_ui(): + $ui/Control.show() + verbs_menu.show() + if ProjectSettings.get("escoria/debug/enable_room_selector") == true: + room_select.show() + inventory_ui.show() + tooltip.show() + +func hide_main_menu(): + if get_node(main_menu).visible: + get_node(main_menu).hide() + show_ui() + +func show_main_menu(): + if not get_node(main_menu).visible: + hide_ui() + get_node(main_menu).reset() + get_node(main_menu).show() + +func unpause_game(): + if get_node(pause_menu).visible: + get_node(pause_menu).hide() + escoria.object_manager.get_object(ESCObjectManager.CAMERA).node.current = true + escoria.object_manager.get_object(ESCObjectManager.SPEECH).node.resume() + escoria.main.current_scene.game.show_ui() + escoria.main.current_scene.show() + escoria.set_game_paused(false) + +func pause_game(): + if not get_node(pause_menu).visible: + get_node(pause_menu).reset() + get_node(pause_menu).set_save_enabled(escoria.save_manager.save_enabled) + get_node(pause_menu).show() + escoria.object_manager.get_object(ESCObjectManager.CAMERA).node.current = false + escoria.object_manager.get_object(ESCObjectManager.SPEECH).node.pause() + escoria.main.current_scene.game.hide_ui() + escoria.main.current_scene.hide() + escoria.set_game_paused(true) + + +func _on_MenuButton_pressed() -> void: + pause_game() + + +func _on_action_finished() -> void: + verbs_menu.unselect_actions() + tooltip.clear() + +func _on_event_done(_return_code: int, _event_name: String): + if _return_code == ESCExecution.RC_OK: + escoria.action_manager.clear_current_action() + verbs_menu.unselect_actions() diff --git a/addons/escoria-ui-keyboard-9verbs/game.gd.uid b/addons/escoria-ui-keyboard-9verbs/game.gd.uid new file mode 100644 index 0000000..f400c73 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/game.gd.uid @@ -0,0 +1 @@ +uid://bb1iia3lec7ja diff --git a/addons/escoria-ui-keyboard-9verbs/game.tscn b/addons/escoria-ui-keyboard-9verbs/game.tscn new file mode 100644 index 0000000..6fa40b4 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/game.tscn @@ -0,0 +1,133 @@ +[gd_scene load_steps=11 format=3 uid="uid://bygij2fuc74p5"] + +[ext_resource type="PackedScene" uid="uid://yv8r2xh0jvft" path="res://addons/escoria-ui-keyboard-9verbs/tooltip/action_target_tooltip.tscn" id="1"] +[ext_resource type="PackedScene" uid="uid://b5yss2p8c83cp" path="res://addons/escoria-ui-keyboard-9verbs/inventory/inventory_ui.tscn" id="2"] +[ext_resource type="PackedScene" uid="uid://c7fwywnj0glve" path="res://addons/escoria-ui-keyboard-9verbs/verbs_menu.tscn" id="3"] +[ext_resource type="Script" uid="uid://dfl7khtlretr7" path="res://addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd" id="4"] +[ext_resource type="Script" uid="uid://bb1iia3lec7ja" path="res://addons/escoria-ui-keyboard-9verbs/game.gd" id="5"] +[ext_resource type="PackedScene" uid="uid://dmw5gicuenj53" path="res://addons/escoria-core/game/scenes/camera_player/camera.tscn" id="6"] +[ext_resource type="PackedScene" uid="uid://u8an5av5fuxx" path="res://addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn" id="7"] +[ext_resource type="PackedScene" uid="uid://gfjgmbcafyyt" path="res://addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn" id="9"] +[ext_resource type="Theme" uid="uid://bcxggcvu105e5" path="res://addons/escoria-ui-keyboard-9verbs/theme.tres" id="10"] + +[sub_resource type="StyleBoxFlat" id="1"] +bg_color = Color(0.6, 0.6, 0.6, 0.5) + +[node name="game" type="Node2D"] +script = ExtResource("5") +main_menu = NodePath("ui/main_menu") +pause_menu = NodePath("ui/pause_menu") +ui_parent_control_node = NodePath("ui/Control") + +[node name="dialog_layer" type="CanvasLayer" parent="."] + +[node name="ESCDialogPlayer" type="Control" parent="dialog_layer"] +layout_mode = 3 +anchors_preset = 0 +theme = ExtResource("10") +script = ExtResource("4") +metadata/_custom_type_script = "uid://dfl7khtlretr7" + +[node name="ui" type="CanvasLayer" parent="."] + +[node name="Control" type="Control" parent="ui"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +theme = ExtResource("10") + +[node name="panel_down" type="PanelContainer" parent="ui/Control"] +layout_mode = 0 +anchor_top = 0.7 +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("1") + +[node name="VBoxContainer" type="VBoxContainer" parent="ui/Control/panel_down"] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="ui/Control/panel_down/VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_top = 10 + +[node name="tooltip" parent="ui/Control/panel_down/VBoxContainer/MarginContainer" instance=ExtResource("1")] +custom_minimum_size = Vector2(0, 20) +layout_mode = 2 +text = "Test" +color = Color(1, 1, 1, 1) + +[node name="HSeparator" type="HSeparator" parent="ui/Control/panel_down/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="HBoxContainer" type="HBoxContainer" parent="ui/Control/panel_down/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="VerbsMargin" type="MarginContainer" parent="ui/Control/panel_down/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="verbs_menu" parent="ui/Control/panel_down/VBoxContainer/HBoxContainer/VerbsMargin" instance=ExtResource("3")] +layout_mode = 2 + +[node name="MainMargin" type="MarginContainer" parent="ui/Control/panel_down/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="VBoxContainer" type="VBoxContainer" parent="ui/Control/panel_down/VBoxContainer/HBoxContainer/MainMargin"] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="ui/Control/panel_down/VBoxContainer/HBoxContainer/MainMargin/VBoxContainer"] +custom_minimum_size = Vector2(100, 40) +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 + +[node name="MenuButton" type="Button" parent="ui/Control/panel_down/VBoxContainer/HBoxContainer/MainMargin/VBoxContainer/MarginContainer"] +layout_mode = 2 +text = "Menu" + +[node name="InventoryMargin" type="MarginContainer" parent="ui/Control/panel_down/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="inventory_ui" parent="ui/Control/panel_down/VBoxContainer/HBoxContainer/InventoryMargin" instance=ExtResource("2")] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="hover_stack" type="Label" parent="ui"] +offset_left = 1085.0 +offset_top = 2.81912 +offset_right = 1283.0 +offset_bottom = 107.819 + +[node name="main_menu" parent="ui" instance=ExtResource("7")] +visible = false + +[node name="pause_menu" parent="ui" instance=ExtResource("9")] +visible = false +theme = ExtResource("10") + +[node name="camera" parent="." instance=ExtResource("6")] + +[connection signal="pressed" from="ui/Control/panel_down/VBoxContainer/HBoxContainer/MainMargin/VBoxContainer/MarginContainer/MenuButton" to="." method="_on_MenuButton_pressed"] diff --git a/addons/escoria-ui-keyboard-9verbs/input_map.gd b/addons/escoria-ui-keyboard-9verbs/input_map.gd new file mode 100644 index 0000000..8e819c8 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/input_map.gd @@ -0,0 +1,60 @@ +const ACTION_SET_VERB_OPEN = "set_action_verb_open" +const ACTION_SET_VERB_PICKUP = "set_action_verb_pickup" +const ACTION_SET_VERB_PUSH = "set_action_verb_push" +const ACTION_SET_VERB_CLOSE = "set_action_verb_close" +const ACTION_SET_VERB_LOOK = "set_action_verb_look" +const ACTION_SET_VERB_PULL = "set_action_verb_pull" +const ACTION_SET_VERB_GIVE = "set_action_verb_give" +const ACTION_SET_VERB_USE = "set_action_verb_use" +const ACTION_SET_VERB_TALK = "set_action_verb_talk" + +""" +The keyboard shortcuts are chosen to match the geometric layout in the +9verb UI (example below assumes QWERTY, but implementation should work +for non-QWERTY, as well): + +``` +open | pickup | push -> Q | W | E +close | look | pull -> A | S | D +give | use | talk -> Z | X | C +``` +""" + + +# Implemented as an array of arrays rather than a dict because dict +# does not have an items() method to enumerate entries together: +# https://github.com/godotengine/godot-proposals/issues/1965 +const action_to_scancode = [ + [ACTION_SET_VERB_OPEN, KEY_Q], + [ACTION_SET_VERB_PICKUP, KEY_W], + [ACTION_SET_VERB_PUSH, KEY_E], + + [ACTION_SET_VERB_CLOSE, KEY_A], + [ACTION_SET_VERB_LOOK, KEY_S], + [ACTION_SET_VERB_PULL, KEY_D], + + [ACTION_SET_VERB_GIVE, KEY_Z], + [ACTION_SET_VERB_USE, KEY_X], + [ACTION_SET_VERB_TALK, KEY_C], +] + + +static func add_actions_to_input_map() -> void: + for entry in action_to_scancode: + var action = entry[0] + var keycode = entry[1] + var event = InputEventKey.new() + # Based on https://github.com/godotengine/godot/pull/18020, + # `physical_scancode` seems like a more appropriate property than + # `scancode` in order to support non-QWERTY keyboard layouts while + # preserving the geometric pattern of the shortcuts. + event.physical_keycode = keycode + InputMap.add_action(action) + InputMap.action_add_event(action, event) + + +static func erase_actions_from_input_map() -> void: + for entry in action_to_scancode: + var action = entry[0] + InputMap.action_erase_events(action) + InputMap.erase_action(action) diff --git a/addons/escoria-ui-keyboard-9verbs/input_map.gd.uid b/addons/escoria-ui-keyboard-9verbs/input_map.gd.uid new file mode 100644 index 0000000..9fa83a4 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/input_map.gd.uid @@ -0,0 +1 @@ +uid://bq3fqtmv6rbiq diff --git a/addons/escoria-ui-keyboard-9verbs/inventory/inventory_ui.tscn b/addons/escoria-ui-keyboard-9verbs/inventory/inventory_ui.tscn new file mode 100644 index 0000000..8ade917 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/inventory/inventory_ui.tscn @@ -0,0 +1,31 @@ +[gd_scene load_steps=3 format=3 uid="uid://b5yss2p8c83cp"] + +[ext_resource type="Script" uid="uid://b1l4nnky23hwo" path="res://addons/escoria-core/game/scenes/inventory/inventory_ui.gd" id="1"] +[ext_resource type="Script" uid="uid://c4syt26p7mg66" path="res://addons/escoria-core/ui_library/inventory/esc_inventory_container.gd" id="3"] + +[node name="inventory_ui" type="PanelContainer"] +offset_right = 600.0 +offset_bottom = 175.0 +script = ExtResource("1") +__meta__ = { +"_edit_use_anchors_": false +} +inventory_ui_container = NodePath("ScrollContainer/GridContainer") + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +offset_left = 7.0 +offset_top = 7.0 +offset_right = 593.0 +offset_bottom = 168.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="GridContainer" type="GridContainer" parent="ScrollContainer"] +offset_right = 586.0 +offset_bottom = 161.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/v_separation = 16 +theme_override_constants/h_separation = 16 +columns = 4 +script = ExtResource("3") diff --git a/addons/escoria-ui-keyboard-9verbs/plugin.cfg b/addons/escoria-ui-keyboard-9verbs/plugin.cfg new file mode 100644 index 0000000..2a005b1 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Escoria 9 Verbs UI + keyboard" +description="Classical LucasArts style 9-verbs UI for the Escoria Framework with keyboard control" +author="Escoria project" +version="0.1.0" +script="plugin.gd" diff --git a/addons/escoria-ui-keyboard-9verbs/plugin.gd b/addons/escoria-ui-keyboard-9verbs/plugin.gd new file mode 100644 index 0000000..c075745 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/plugin.gd @@ -0,0 +1,24 @@ +@tool +# Plugin script to initialize Escoria 9-verbs with keyboard plugin +extends EditorPlugin + + +# Override function to return the plugin name. +func _get_plugin_name(): + return "escoria-ui-keyboard-9verbs" + + +# Deregister UI +func _disable_plugin() -> void: + print("Disabling plugin Escoria UI 9-verbs with keyboard.") + EscoriaPlugin.deregister_ui("res://addons/escoria-ui-keyboard-9verbs/game.tscn") + + +# Register UI with Escoria +func _enable_plugin(): + print("Enabling plugin Escoria UI 9-verbs with keyboard.") + if not EscoriaPlugin.register_ui(self, "res://addons/escoria-ui-keyboard-9verbs/game.tscn"): + get_editor_interface().set_plugin_enabled( + _get_plugin_name(), + false + ) diff --git a/addons/escoria-ui-keyboard-9verbs/plugin.gd.uid b/addons/escoria-ui-keyboard-9verbs/plugin.gd.uid new file mode 100644 index 0000000..c3ac821 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/plugin.gd.uid @@ -0,0 +1 @@ +uid://nrcailyukdvy diff --git a/addons/escoria-ui-keyboard-9verbs/theme.tres b/addons/escoria-ui-keyboard-9verbs/theme.tres new file mode 100644 index 0000000..a01dee5 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/theme.tres @@ -0,0 +1,6 @@ +[gd_resource type="Theme" load_steps=2 format=3 uid="uid://bcxggcvu105e5"] + +[ext_resource type="FontFile" path="res://addons/escoria-ui-keyboard-9verbs/fonts/caslonantique.tres" id="1"] + +[resource] +default_font = ExtResource("1") diff --git a/addons/escoria-ui-keyboard-9verbs/tooltip/action_target_tooltip.tscn b/addons/escoria-ui-keyboard-9verbs/tooltip/action_target_tooltip.tscn new file mode 100644 index 0000000..ab5d677 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/tooltip/action_target_tooltip.tscn @@ -0,0 +1,14 @@ +[gd_scene load_steps=2 format=3 uid="uid://yv8r2xh0jvft"] + +[ext_resource type="Script" uid="uid://bxwtb3w78gtbe" path="res://addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd" id="1"] + +[node name="tooltip" type="RichTextLabel"] +anchor_right = 1.0 +anchor_bottom = 1.0 +bbcode_enabled = true +text = "[center][/center]" +scroll_active = false +script = ExtResource("1") +__meta__ = { +"_edit_use_anchors_": false +} diff --git a/addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd b/addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd new file mode 100644 index 0000000..8fcd477 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd @@ -0,0 +1,34 @@ +extends ESCTooltip + +@export var prepositions = {"use": "with", "give": "to"} + +func update_tooltip_text(): + text = "[center]" + text += "[color=#" + color.to_html(false) + "]" + if !current_action.is_empty(): + text += current_action + "\t" + text += current_target + + if waiting_for_target2 and current_target2.is_empty(): + current_prep = prepositions.get(current_action, current_prep) + text += "\t" + current_prep + " \t" + + if !current_target2.is_empty(): + text += "\t" + current_prep + " \t" + current_target2 + + text += "[/color]" + text += "[/center]" + +# push_align(RichTextLabel.ALIGN_CENTER) +# if !current_action.empty(): +# add_text(current_action + "\t") +# +# add_text(current_target) +# +# if waiting_for_target2 and current_target2.empty(): +# add_text("\t" + current_prep) +# +# if !current_target2.empty(): +# add_text("\t" + current_prep + "\t" + current_target2) +# +# pop() diff --git a/addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd.uid b/addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd.uid new file mode 100644 index 0000000..b715479 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/tooltip/tooltip_action_target.gd.uid @@ -0,0 +1 @@ +uid://bxwtb3w78gtbe diff --git a/addons/escoria-ui-keyboard-9verbs/verbs_menu.gd b/addons/escoria-ui-keyboard-9verbs/verbs_menu.gd new file mode 100644 index 0000000..22c417c --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/verbs_menu.gd @@ -0,0 +1,32 @@ +extends Control + +""" +This script is out of Escoria's scope. It controls the UI reaction to an +UI event (eg right click) to change the cursor accordingly. +""" + +var selected_action + +func _ready(): + for but in get_children(): + but.connect("pressed", Callable(self, "on_action_selected").bind(but.name)) + but.toggle_mode = true + +func on_action_selected(action: String): + if escoria.inputs_manager.input_mode != escoria.inputs_manager.INPUT_ALL: + unselect_actions() + return + + escoria.action_manager.set_current_action(action) + + for but in get_children(): + but.set_pressed(but.get_name() == action) + +func unselect_actions(): + for but in get_children(): + but.set_pressed(false) + +func set_by_name(action_name: String): + selected_action = action_name + for but in get_children(): + but.set_pressed(but.get_name() == action_name) diff --git a/addons/escoria-ui-keyboard-9verbs/verbs_menu.gd.uid b/addons/escoria-ui-keyboard-9verbs/verbs_menu.gd.uid new file mode 100644 index 0000000..b1b44f0 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/verbs_menu.gd.uid @@ -0,0 +1 @@ +uid://bkkwcatncswop diff --git a/addons/escoria-ui-keyboard-9verbs/verbs_menu.tscn b/addons/escoria-ui-keyboard-9verbs/verbs_menu.tscn new file mode 100644 index 0000000..07b5885 --- /dev/null +++ b/addons/escoria-ui-keyboard-9verbs/verbs_menu.tscn @@ -0,0 +1,125 @@ +[gd_scene load_steps=2 format=3 uid="uid://c7fwywnj0glve"] + +[ext_resource type="Script" uid="uid://bkkwcatncswop" path="res://addons/escoria-ui-keyboard-9verbs/verbs_menu.gd" id="1"] + +[node name="actions" type="GridContainer"] +offset_right = 493.0 +offset_bottom = 263.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +columns = 3 +script = ExtResource("1") +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="open" type="Button" parent="."] +offset_right = 161.0 +offset_bottom = 85.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Open" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="pickup" type="Button" parent="."] +offset_left = 165.0 +offset_right = 326.0 +offset_bottom = 85.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Pick up" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="push" type="Button" parent="."] +offset_left = 330.0 +offset_right = 491.0 +offset_bottom = 85.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Push" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="close" type="Button" parent="."] +offset_top = 89.0 +offset_right = 161.0 +offset_bottom = 174.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Close" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="look" type="Button" parent="."] +offset_left = 165.0 +offset_top = 89.0 +offset_right = 326.0 +offset_bottom = 174.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Look at" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="pull" type="Button" parent="."] +offset_left = 330.0 +offset_top = 89.0 +offset_right = 491.0 +offset_bottom = 174.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Pull" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="give" type="Button" parent="."] +offset_top = 178.0 +offset_right = 161.0 +offset_bottom = 263.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Give" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="use" type="Button" parent="."] +offset_left = 165.0 +offset_top = 178.0 +offset_right = 326.0 +offset_bottom = 263.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Use" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="talk" type="Button" parent="."] +offset_left = 330.0 +offset_top = 178.0 +offset_right = 491.0 +offset_bottom = 263.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +toggle_mode = true +text = "Talk" +__meta__ = { +"_edit_use_anchors_": false +} diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_dialog.png b/addons/escoria-ui-simplemouse/cursors/cursor_dialog.png new file mode 100644 index 0000000..5de7062 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_dialog.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_dialog.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_dialog.png.import new file mode 100644 index 0000000..7235b40 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_dialog.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://exe50m0nxnqc" +path="res://.godot/imported/cursor_dialog.png-ffd9ad0d614e016d2d58a2bbe6a1b748.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_dialog.png" +dest_files=["res://.godot/imported/cursor_dialog.png-ffd9ad0d614e016d2d58a2bbe6a1b748.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_examine.png b/addons/escoria-ui-simplemouse/cursors/cursor_examine.png new file mode 100644 index 0000000..d0ce025 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_examine.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_examine.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_examine.png.import new file mode 100644 index 0000000..99b32a6 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_examine.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://biu6w0ho0enq1" +path="res://.godot/imported/cursor_examine.png-ac3f57b64c49a5ec22ff8275facde947.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_examine.png" +dest_files=["res://.godot/imported/cursor_examine.png-ac3f57b64c49a5ec22ff8275facde947.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_foot.png b/addons/escoria-ui-simplemouse/cursors/cursor_foot.png new file mode 100644 index 0000000..0f43a95 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_foot.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_foot.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_foot.png.import new file mode 100644 index 0000000..96c5589 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_foot.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b76xq3pacb0lh" +path="res://.godot/imported/cursor_foot.png-207c7831377bbcab0c0f85c99074fbbb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_foot.png" +dest_files=["res://.godot/imported/cursor_foot.png-207c7831377bbcab0c0f85c99074fbbb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_glasses.png b/addons/escoria-ui-simplemouse/cursors/cursor_glasses.png new file mode 100644 index 0000000..ef6ca77 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_glasses.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_glasses.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_glasses.png.import new file mode 100644 index 0000000..23455b2 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_glasses.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cyoycildjmd8u" +path="res://.godot/imported/cursor_glasses.png-3800536a4d1e7d66a97c2221b8980dd7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_glasses.png" +dest_files=["res://.godot/imported/cursor_glasses.png-3800536a4d1e7d66a97c2221b8980dd7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_gun.png b/addons/escoria-ui-simplemouse/cursors/cursor_gun.png new file mode 100644 index 0000000..906ebe1 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_gun.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_gun.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_gun.png.import new file mode 100644 index 0000000..f5a4167 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_gun.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c6mygqklivhon" +path="res://.godot/imported/cursor_gun.png-330b9026b9a5608686af56c5ac6ac6da.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_gun.png" +dest_files=["res://.godot/imported/cursor_gun.png-330b9026b9a5608686af56c5ac6ac6da.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_hand.png b/addons/escoria-ui-simplemouse/cursors/cursor_hand.png new file mode 100644 index 0000000..d8234a0 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_hand.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_hand.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_hand.png.import new file mode 100644 index 0000000..55e926d --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_hand.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b0w311qw1j4e5" +path="res://.godot/imported/cursor_hand.png-2f9f28edbc1b07d4c0aadf071fe7ccdf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_hand.png" +dest_files=["res://.godot/imported/cursor_hand.png-2f9f28edbc1b07d4c0aadf071fe7ccdf.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_heal.png b/addons/escoria-ui-simplemouse/cursors/cursor_heal.png new file mode 100644 index 0000000..c333906 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_heal.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_heal.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_heal.png.import new file mode 100644 index 0000000..7de2cd2 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_heal.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cja7yb4its3wr" +path="res://.godot/imported/cursor_heal.png-e4aabd685df339b1c5e03521e3beda14.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_heal.png" +dest_files=["res://.godot/imported/cursor_heal.png-e4aabd685df339b1c5e03521e3beda14.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_lock.png b/addons/escoria-ui-simplemouse/cursors/cursor_lock.png new file mode 100644 index 0000000..14b5889 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_lock.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_lock.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_lock.png.import new file mode 100644 index 0000000..1acf3c0 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_lock.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bqdgn4ktijuqx" +path="res://.godot/imported/cursor_lock.png-45d1522c723726ae0cf94c6455c466a2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_lock.png" +dest_files=["res://.godot/imported/cursor_lock.png-45d1522c723726ae0cf94c6455c466a2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_pen.png b/addons/escoria-ui-simplemouse/cursors/cursor_pen.png new file mode 100644 index 0000000..36d746d Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_pen.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_pen.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_pen.png.import new file mode 100644 index 0000000..161ab97 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_pen.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c30joglrpnw0k" +path="res://.godot/imported/cursor_pen.png-86f4b8f622ae7a5d10fe19a6595e95e0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_pen.png" +dest_files=["res://.godot/imported/cursor_pen.png-86f4b8f622ae7a5d10fe19a6595e95e0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_punch.png b/addons/escoria-ui-simplemouse/cursors/cursor_punch.png new file mode 100644 index 0000000..2a29cf7 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_punch.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_punch.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_punch.png.import new file mode 100644 index 0000000..3a974d4 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_punch.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cv582lppeol0a" +path="res://.godot/imported/cursor_punch.png-ba6bd21dd7c2f0d20960c247b6910e3b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_punch.png" +dest_files=["res://.godot/imported/cursor_punch.png-ba6bd21dd7c2f0d20960c247b6910e3b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_shield.png b/addons/escoria-ui-simplemouse/cursors/cursor_shield.png new file mode 100644 index 0000000..5b0094e Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_shield.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_shield.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_shield.png.import new file mode 100644 index 0000000..29db1d1 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_shield.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://4b7tteqt355y" +path="res://.godot/imported/cursor_shield.png-eaeed515b587cd2d3c3f446f4f31655d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_shield.png" +dest_files=["res://.godot/imported/cursor_shield.png-eaeed515b587cd2d3c3f446f4f31655d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_tool.png b/addons/escoria-ui-simplemouse/cursors/cursor_tool.png new file mode 100644 index 0000000..64aa1c0 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_tool.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_tool.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_tool.png.import new file mode 100644 index 0000000..3fae9c0 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_tool.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c7j8a558rhduw" +path="res://.godot/imported/cursor_tool.png-5453a787785351f0d494452f366f95cb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_tool.png" +dest_files=["res://.godot/imported/cursor_tool.png-5453a787785351f0d494452f366f95cb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_wait.png b/addons/escoria-ui-simplemouse/cursors/cursor_wait.png new file mode 100644 index 0000000..dfd7db7 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/cursor_wait.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/cursor_wait.png.import b/addons/escoria-ui-simplemouse/cursors/cursor_wait.png.import new file mode 100644 index 0000000..27d81a8 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/cursor_wait.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://g4h8g3jaku73" +path="res://.godot/imported/cursor_wait.png-062a6b60db614adf339564dc7f09860e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/cursor_wait.png" +dest_files=["res://.godot/imported/cursor_wait.png-062a6b60db614adf339564dc7f09860e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_e.png b/addons/escoria-ui-simplemouse/cursors/new/arrow_e.png new file mode 100644 index 0000000..8591db5 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/arrow_e.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_e.png.import b/addons/escoria-ui-simplemouse/cursors/new/arrow_e.png.import new file mode 100644 index 0000000..6c13c26 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/arrow_e.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b46kn0ifqhiu4" +path="res://.godot/imported/arrow_e.png-153d8ed01ab017977346cbaeb6bd77f5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/arrow_e.png" +dest_files=["res://.godot/imported/arrow_e.png-153d8ed01ab017977346cbaeb6bd77f5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_n.png b/addons/escoria-ui-simplemouse/cursors/new/arrow_n.png new file mode 100644 index 0000000..059ca19 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/arrow_n.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_n.png.import b/addons/escoria-ui-simplemouse/cursors/new/arrow_n.png.import new file mode 100644 index 0000000..0f3f5b5 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/arrow_n.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqr3h7xk82lu1" +path="res://.godot/imported/arrow_n.png-ad952b7bc2277035795d332fb9387346.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/arrow_n.png" +dest_files=["res://.godot/imported/arrow_n.png-ad952b7bc2277035795d332fb9387346.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png b/addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png new file mode 100644 index 0000000..c8108d7 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png.import b/addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png.import new file mode 100644 index 0000000..666fb93 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d1xtyuq3mkyhs" +path="res://.godot/imported/arrow_ne.png-3c9c30c17064d47df3b94b92863cdada.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/arrow_ne.png" +dest_files=["res://.godot/imported/arrow_ne.png-3c9c30c17064d47df3b94b92863cdada.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png b/addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png new file mode 100644 index 0000000..af7857a Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png.import b/addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png.import new file mode 100644 index 0000000..b0484f6 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ck702an45ceeg" +path="res://.godot/imported/arrow_nw.png-d1957ff69d080b1ce932d439d674ba55.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/arrow_nw.png" +dest_files=["res://.godot/imported/arrow_nw.png-d1957ff69d080b1ce932d439d674ba55.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_s.png b/addons/escoria-ui-simplemouse/cursors/new/arrow_s.png new file mode 100644 index 0000000..b292774 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/arrow_s.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_s.png.import b/addons/escoria-ui-simplemouse/cursors/new/arrow_s.png.import new file mode 100644 index 0000000..f3e6ec5 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/arrow_s.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://kln6xwmvi36j" +path="res://.godot/imported/arrow_s.png-1c62e51a0ca3cff5b0b5cb2ba95e4e63.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/arrow_s.png" +dest_files=["res://.godot/imported/arrow_s.png-1c62e51a0ca3cff5b0b5cb2ba95e4e63.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_se.png b/addons/escoria-ui-simplemouse/cursors/new/arrow_se.png new file mode 100644 index 0000000..1759309 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/arrow_se.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_se.png.import b/addons/escoria-ui-simplemouse/cursors/new/arrow_se.png.import new file mode 100644 index 0000000..6a166d1 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/arrow_se.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c770jgijcd0m" +path="res://.godot/imported/arrow_se.png-5d407cac7dd44a609126d8fa48c18986.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/arrow_se.png" +dest_files=["res://.godot/imported/arrow_se.png-5d407cac7dd44a609126d8fa48c18986.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png b/addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png new file mode 100644 index 0000000..d2fd7e5 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png.import b/addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png.import new file mode 100644 index 0000000..3d6306f --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bql3dj8ht3s7a" +path="res://.godot/imported/arrow_sw.png-b415ae4b17895a81de3c1f6cb37e0801.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/arrow_sw.png" +dest_files=["res://.godot/imported/arrow_sw.png-b415ae4b17895a81de3c1f6cb37e0801.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_w.png b/addons/escoria-ui-simplemouse/cursors/new/arrow_w.png new file mode 100644 index 0000000..255f476 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/arrow_w.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/arrow_w.png.import b/addons/escoria-ui-simplemouse/cursors/new/arrow_w.png.import new file mode 100644 index 0000000..ca6f4a5 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/arrow_w.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bj476r0j38y5f" +path="res://.godot/imported/arrow_w.png-16c6b65b599c5498385418c7e1619695.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/arrow_w.png" +dest_files=["res://.godot/imported/arrow_w.png-16c6b65b599c5498385418c7e1619695.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/hand_open.png b/addons/escoria-ui-simplemouse/cursors/new/hand_open.png new file mode 100644 index 0000000..fbab5c4 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/hand_open.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/hand_open.png.import b/addons/escoria-ui-simplemouse/cursors/new/hand_open.png.import new file mode 100644 index 0000000..b8adaa7 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/hand_open.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqgt2k4sw4au2" +path="res://.godot/imported/hand_open.png-bfc5aac8497ebc91131befd204560050.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/hand_open.png" +dest_files=["res://.godot/imported/hand_open.png-bfc5aac8497ebc91131befd204560050.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/line_cross.png b/addons/escoria-ui-simplemouse/cursors/new/line_cross.png new file mode 100644 index 0000000..e47b9be Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/line_cross.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/line_cross.png.import b/addons/escoria-ui-simplemouse/cursors/new/line_cross.png.import new file mode 100644 index 0000000..d12b017 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/line_cross.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cvcptstwu1byf" +path="res://.godot/imported/line_cross.png-1c8a70e1d36cd838c73dcc7d2793d6fd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/line_cross.png" +dest_files=["res://.godot/imported/line_cross.png-1c8a70e1d36cd838c73dcc7d2793d6fd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/look_b.png b/addons/escoria-ui-simplemouse/cursors/new/look_b.png new file mode 100644 index 0000000..7caa0f0 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/look_b.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/look_b.png.import b/addons/escoria-ui-simplemouse/cursors/new/look_b.png.import new file mode 100644 index 0000000..e63dadc --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/look_b.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bki5oinr3dhfu" +path="res://.godot/imported/look_b.png-76c2d529030d0cdb88c3bde0d6962736.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/look_b.png" +dest_files=["res://.godot/imported/look_b.png-76c2d529030d0cdb88c3bde0d6962736.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png b/addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png new file mode 100644 index 0000000..fab50e2 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png.import b/addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png.import new file mode 100644 index 0000000..cef47af --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://blycbkrub7pae" +path="res://.godot/imported/message_dots_round.png-6766f963529a5ea00e87ffe13c77b138.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png" +dest_files=["res://.godot/imported/message_dots_round.png-6766f963529a5ea00e87ffe13c77b138.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/target_a.png b/addons/escoria-ui-simplemouse/cursors/new/target_a.png new file mode 100644 index 0000000..b4662c4 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/target_a.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/target_a.png.import b/addons/escoria-ui-simplemouse/cursors/new/target_a.png.import new file mode 100644 index 0000000..275647a --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/target_a.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bjhpqqbhp4ab1" +path="res://.godot/imported/target_a.png-809052aeb3e2ec18fbc9e837b45b7801.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/target_a.png" +dest_files=["res://.godot/imported/target_a.png-809052aeb3e2ec18fbc9e837b45b7801.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png b/addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png new file mode 100644 index 0000000..43c6893 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png.import b/addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png.import new file mode 100644 index 0000000..2f4b7e1 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c8rnd6u3w72wu" +path="res://.godot/imported/tool_wrench.png-80086a1423b610f5017380ab1d2e216a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png" +dest_files=["res://.godot/imported/tool_wrench.png-80086a1423b610f5017380ab1d2e216a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/new/zoom.png b/addons/escoria-ui-simplemouse/cursors/new/zoom.png new file mode 100644 index 0000000..16c5029 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/new/zoom.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/new/zoom.png.import b/addons/escoria-ui-simplemouse/cursors/new/zoom.png.import new file mode 100644 index 0000000..93f3c35 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/new/zoom.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ch21wk18ks1o1" +path="res://.godot/imported/zoom.png-d6c43d42455b6f8d63f1372305558a73.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/new/zoom.png" +dest_files=["res://.godot/imported/zoom.png-d6c43d42455b6f8d63f1372305558a73.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png new file mode 100644 index 0000000..f482508 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png.import new file mode 100644 index 0000000..adfc7e4 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dwqyllb0birai" +path="res://.godot/imported/cursor_examine.png-650fb292182c4a1e8cbc295fb4d211d5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_examine.png" +dest_files=["res://.godot/imported/cursor_examine.png-650fb292182c4a1e8cbc295fb4d211d5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png new file mode 100644 index 0000000..0980003 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png.import new file mode 100644 index 0000000..9b938d5 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b7oq4lga45kro" +path="res://.godot/imported/cursor_foot.png-48d06263592d162c9232be2662fe0693.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_foot.png" +dest_files=["res://.godot/imported/cursor_foot.png-48d06263592d162c9232be2662fe0693.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png new file mode 100644 index 0000000..ef6ca77 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png.import new file mode 100644 index 0000000..f096aee --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dcq23gkw44a6r" +path="res://.godot/imported/cursor_glasses.png-d7e8ec51a70fd03bfff9400e24c820b7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_glasses.png" +dest_files=["res://.godot/imported/cursor_glasses.png-d7e8ec51a70fd03bfff9400e24c820b7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png new file mode 100644 index 0000000..906ebe1 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png.import new file mode 100644 index 0000000..c71b428 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bxrhoc7ifw7fs" +path="res://.godot/imported/cursor_gun.png-880ffd9071eb2d9b93b6a5c309c9679a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_gun.png" +dest_files=["res://.godot/imported/cursor_gun.png-880ffd9071eb2d9b93b6a5c309c9679a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png new file mode 100644 index 0000000..242d461 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png.import new file mode 100644 index 0000000..55b7ef6 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c4ro1ft0jmq1k" +path="res://.godot/imported/cursor_hand.png-256a0d4e3bf443dd569c1ed635874657.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_hand.png" +dest_files=["res://.godot/imported/cursor_hand.png-256a0d4e3bf443dd569c1ed635874657.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png new file mode 100644 index 0000000..c333906 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png.import new file mode 100644 index 0000000..c673eb2 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://o61q065r3p3b" +path="res://.godot/imported/cursor_heal.png-346d534ee7403a7908388c8c4e4f3de4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_heal.png" +dest_files=["res://.godot/imported/cursor_heal.png-346d534ee7403a7908388c8c4e4f3de4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png new file mode 100644 index 0000000..14b5889 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png.import new file mode 100644 index 0000000..8f90473 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://s0ki1jjaxf6w" +path="res://.godot/imported/cursor_lock.png-d927cf5ad32750d4db3864d743bad460.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_lock.png" +dest_files=["res://.godot/imported/cursor_lock.png-d927cf5ad32750d4db3864d743bad460.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png new file mode 100644 index 0000000..aeb1e2a Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png.import new file mode 100644 index 0000000..19c50a9 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://doexbnkksen2o" +path="res://.godot/imported/cursor_pen.png-98ba2555d6922476db19b5d739207448.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_pen.png" +dest_files=["res://.godot/imported/cursor_pen.png-98ba2555d6922476db19b5d739207448.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png new file mode 100644 index 0000000..2a29cf7 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png.import new file mode 100644 index 0000000..6000e83 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://qrgl8ctbhv" +path="res://.godot/imported/cursor_punch.png-16c0efa6fffa8f186f50a2f9787c33df.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_punch.png" +dest_files=["res://.godot/imported/cursor_punch.png-16c0efa6fffa8f186f50a2f9787c33df.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png new file mode 100644 index 0000000..5b0094e Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png.import new file mode 100644 index 0000000..67a98da --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bby2kfr3h8wki" +path="res://.godot/imported/cursor_shield.png-cd8a8d4d1dacda9efcf960bd1672d60c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_shield.png" +dest_files=["res://.godot/imported/cursor_shield.png-cd8a8d4d1dacda9efcf960bd1672d60c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png new file mode 100644 index 0000000..f794ce2 Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png.import new file mode 100644 index 0000000..83b2228 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://3aomi43b3cvr" +path="res://.godot/imported/cursor_tool.png-0a254466811dcee8a0c54758959ea51c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_tool.png" +dest_files=["res://.godot/imported/cursor_tool.png-0a254466811dcee8a0c54758959ea51c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png b/addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png new file mode 100644 index 0000000..880b83e Binary files /dev/null and b/addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png differ diff --git a/addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png.import b/addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png.import new file mode 100644 index 0000000..cecc2b4 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bb05k385eyibe" +path="res://.godot/imported/cursor_wait.png-c54a9147c43a14db47fb55def6c6da34.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/cursor_wait.png" +dest_files=["res://.godot/imported/cursor_wait.png-c54a9147c43a14db47fb55def6c6da34.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg b/addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg new file mode 100644 index 0000000..62ad320 --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg @@ -0,0 +1,1824 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg.import b/addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg.import new file mode 100644 index 0000000..dc7512d --- /dev/null +++ b/addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dhxf150she145" +path="res://.godot/imported/pointers4.svg-c4792b4ac14ea7bb68fd8bc9d52aab7a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/cursors/originals/pointers4.svg" +dest_files=["res://.godot/imported/pointers4.svg-c4792b4ac14ea7bb68fd8bc9d52aab7a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-ui-simplemouse/fonts/caslonantique.tres b/addons/escoria-ui-simplemouse/fonts/caslonantique.tres new file mode 100644 index 0000000..dbfb5a3 --- /dev/null +++ b/addons/escoria-ui-simplemouse/fonts/caslonantique.tres @@ -0,0 +1,7 @@ +[gd_resource type="FontFile" load_steps=2 format=2] + +[ext_resource path="res://addons/escoria-ui-simplemouse/fonts/caslonantique.ttf" type="FontFile" id=1] + +[resource] +size = 21 +font_data = ExtResource( 1 ) diff --git a/addons/escoria-ui-simplemouse/fonts/caslonantique.ttf b/addons/escoria-ui-simplemouse/fonts/caslonantique.ttf new file mode 100644 index 0000000..eec6f63 Binary files /dev/null and b/addons/escoria-ui-simplemouse/fonts/caslonantique.ttf differ diff --git a/addons/escoria-ui-simplemouse/fonts/caslonantique.ttf.import b/addons/escoria-ui-simplemouse/fonts/caslonantique.ttf.import new file mode 100644 index 0000000..d68c768 --- /dev/null +++ b/addons/escoria-ui-simplemouse/fonts/caslonantique.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://8xmm6vqyi7v5" +path="res://.godot/imported/caslonantique.ttf-554a9dbaffd95ff933abe0b7adebba50.fontdata" + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/fonts/caslonantique.ttf" +dest_files=["res://.godot/imported/caslonantique.ttf-554a9dbaffd95ff933abe0b7adebba50.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/addons/escoria-ui-simplemouse/game.gd b/addons/escoria-ui-simplemouse/game.gd new file mode 100644 index 0000000..3730d78 --- /dev/null +++ b/addons/escoria-ui-simplemouse/game.gd @@ -0,0 +1,485 @@ +extends ESCGame + + +const VERB_USE = "use" +const VERB_WALK = "walk" + + +""" +Implement methods to react to inputs. + +- left_click_on_bg(position: Vector2) +- right_click_on_bg(position: Vector2) +- left_double_click_on_bg(position: Vector2) + +- element_focused(element_id: String) +- element_unfocused() + +- left_click_on_item(item_global_id: String, event: InputEvent) +- right_click_on_item(item_global_id: String, event: InputEvent) +- left_double_click_on_item(item_global_id: String, event: InputEvent) + +- left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) +- right_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) +- left_double_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) +- inventory_item_focused(inventory_item_global_id: String) +- inventory_item_unfocused() +- open_inventory() +- close_inventory() + +- mousewheel_action(direction: int) + +- hide_ui() +- show_ui() + +- pause_game() +- unpause_game() +- show_main_menu() +- hide_main_menu() + +- apply_custom_settings() + +- _on_event_done(event_name: String) +""" + +# Value to use for `device` argument to various `Input.get_joy` methods. +const JOY_DEVICE = 0 + +# See https://docs.godotengine.org/en/stable/tutorials/inputs/controllers_gamepads_joysticks.html?#dead-zone +const DEADZONE = 0.2 + +# Multiplier to apply to axis when it exceeds DEADZONE. +const AXIS_WEIGHT = 50.0 + +# JOY_BUTTON_X corresponds to the "X" button on an XBox controller +# or the Square button on a Playstation controller. These appear to +# map to the "primary action," in practice, so we treat it like a left click. +const PRIMARY_ACTION_BUTTON = JOY_BUTTON_X + +# JOY_BUTTON_Y corresponds to the "Y" button on an XBox controller +# or the Triangle button on a Playstation controller. These appear to +# map to the "secondary action," in practice, so we treat it like a right click. +const CHANGE_VERB_BUTTON = JOY_BUTTON_Y + +# Input action for use by InputMap +const ESC_UI_CHANGE_VERB_ACTION = "esc_change_verb" + +# true when a gamepad is connected. +var _is_gamepad_connected = false + +# Tracks the mouse's current position onscreen. +var _current_mouse_pos: Vector2 = Vector2.ZERO + +var targeted_node: Node + + +func _ready(): + hide_ui() + $ui/tooltip.connect("tooltip_size_updated", Callable(self, "update_tooltip_following_mouse_position")) + +func _enter_tree(): + initialize_esc_game() + + var room_selector_parent = $ui/HBoxContainer/VBoxContainer + + if ESCProjectSettingsManager.get_setting(ESCProjectSettingsManager.ENABLE_ROOM_SELECTOR) \ + and room_selector_parent.get_node_or_null("room_select") == null: + + room_selector_parent.add_child( + preload( + "res://addons/escoria-core/ui_library/tools/room_select" +\ + "/room_select.tscn" + ).instantiate() + ) + + var input_handler = Callable(self._process_input) + escoria.inputs_manager.register_custom_input_handler(input_handler) + + _is_gamepad_connected = Input.is_joy_known(JOY_DEVICE) + if _is_gamepad_connected: + _on_gamepad_connected() + + Input.connect("joy_connection_changed", Callable(self, "_on_joy_connection_changed")) + + +func _exit_tree(): + escoria.inputs_manager.register_custom_input_handler(null) + Input.disconnect("joy_connection_changed", Callable(self, "_on_joy_connection_changed")) + if _is_gamepad_connected: + _on_gamepad_disconnected() + + +func _input(event: InputEvent) -> void: + super._input(event) + if escoria.get_escoria().is_ready_for_inputs(): + if event is InputEventMouseMotion: + _current_mouse_pos = get_global_mouse_position() + update_tooltip_following_mouse_position() + + +# https://github.com/godotengine/godot-demo-projects/blob/3.4-585455e/misc/joypads/joypads.gd +# was informative in wiring up the gamepad properly. + +func _on_gamepad_connected(): + set_physics_process(true) + + var primary_event = InputEventJoypadButton.new() + primary_event.button_index = PRIMARY_ACTION_BUTTON + InputMap.add_action(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION) + InputMap.action_add_event(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION, primary_event) + + var verb_event = InputEventJoypadButton.new() + verb_event.button_index = CHANGE_VERB_BUTTON + InputMap.add_action(ESC_UI_CHANGE_VERB_ACTION) + InputMap.action_add_event(ESC_UI_CHANGE_VERB_ACTION, verb_event) + + +func _on_gamepad_disconnected(): + InputMap.action_erase_events(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION) + InputMap.erase_action(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION) + + InputMap.action_erase_events(ESC_UI_CHANGE_VERB_ACTION) + InputMap.erase_action(ESC_UI_CHANGE_VERB_ACTION) + + set_physics_process(false) + _is_gamepad_connected = false + + +func _on_joy_connection_changed(device: int, connected: bool) -> void: + if device != JOY_DEVICE: + return + elif connected: + _on_gamepad_connected() + else: + _on_gamepad_disconnected() + + +func _process(_delta) -> void: + if !_is_gamepad_connected: + return + + var x = Input.get_joy_axis(JOY_DEVICE, JOY_AXIS_LEFT_X) + var y = Input.get_joy_axis(JOY_DEVICE, JOY_AXIS_LEFT_Y) + var delta_x = int(x * AXIS_WEIGHT) if abs(x) > DEADZONE else 0 + var delta_y = int(y * AXIS_WEIGHT) if abs(y) > DEADZONE else 0 + if delta_x or delta_y: + var direction: Vector2 + direction.x = delta_x + direction.y = delta_y + escoria.logger.trace(self, "gamepad direction: %s" % [direction]) + var viewport = get_viewport() + viewport.warp_mouse(viewport.get_mouse_position() + direction) + + +func _process_input(event: InputEvent, is_default_state: bool) -> bool: + if not is_default_state: + # ESCBackground is not guaranteed to be set, as we may be on + # the "New Game" screen. + return false + elif _is_gamepad_connected and event is InputEventJoypadButton: + escoria.logger.trace(self, "InputEventJoypadButton: %s" % [event.as_text()]) + if event.is_action_pressed(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION): + # Admittedly, this breaks abstraction barriers and is completely + # inappropriate, but it's what works right now. + escoria.inputs_manager._on_left_click_on_bg(get_global_mouse_position()) + return true + elif event.is_action_pressed(ESC_UI_CHANGE_VERB_ACTION): + mousewheel_action(1) + return true + return false + + +## BACKGROUND ## + +func left_click_on_bg(position: Vector2) -> void: + if escoria.main.current_scene.player: + escoria.action_manager.do( + escoria.action_manager.ACTION.BACKGROUND_CLICK, + [escoria.main.current_scene.player.global_id, position], + true + ) + $mouse_layer/verbs_menu.set_by_name(VERB_WALK) + $mouse_layer/verbs_menu.clear_tool_texture() + $mouse_layer/verbs_menu.action_manually_changed = false + close_inventory() + +func right_click_on_bg(position: Vector2) -> void: + left_double_click_on_bg(position) + +func left_double_click_on_bg(position: Vector2) -> void: + if escoria.main.current_scene.player: + escoria.action_manager.do( + escoria.action_manager.ACTION.BACKGROUND_CLICK, + [escoria.main.current_scene.player.global_id, position, true], + true + ) + $mouse_layer/verbs_menu.set_by_name(VERB_WALK) + $mouse_layer/verbs_menu.clear_tool_texture() + $mouse_layer/verbs_menu.action_manually_changed = false + close_inventory() + +## ITEM/HOTSPOT FOCUS ## + +func element_focused(element_id: String) -> void: + var target_obj: ESCItem = escoria.object_manager.get_object(element_id).node + + # This code is commented to demonstrate how to implement a simple hover + # behaviour on an item. + #if target_obj.has_method("get_sprite") and target_obj.get_sprite().texture: + #targeted_node = target_obj.get_sprite() + #targeted_node.modulate = Color.GRAY + + $ui/tooltip.set_target(target_obj.tooltip_name) + + if escoria.action_manager.current_action != VERB_USE \ + and escoria.action_manager.current_tool == null \ + and target_obj is ESCItem: + + if target_obj.is_exit: + if element_id.contains("_l_"): + $mouse_layer/verbs_menu.set_by_name("exit_left", "walk") + elif element_id.contains("_r_"): + $mouse_layer/verbs_menu.set_by_name("exit_right", "walk") + else: + $mouse_layer/verbs_menu.set_by_name("walk") + elif not $mouse_layer/verbs_menu.action_manually_changed: + $mouse_layer/verbs_menu.set_by_name(target_obj.default_action) + +func element_unfocused() -> void: + $ui/tooltip.set_target("") + if not $mouse_layer/verbs_menu.action_manually_changed: + $mouse_layer/verbs_menu.set_by_name("walk") + + + # This code is commented to demonstrate how to implement a simple unhover + # behaviour on an item. + #if targeted_node != null: + #targeted_node.modulate = Color.WHITE + +## ITEMS ## +func left_click_on_item(item_global_id: String, event: InputEvent) -> void: + var target_obj = escoria.object_manager.get_object(item_global_id).node + + # current_action will be empty if an event completes between when you stop + # moving the mouse and when you click. + if escoria.action_manager.current_action == "": + if target_obj is ESCItem: + $mouse_layer/verbs_menu.set_by_name( + target_obj.default_action + ) + + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_LEFT_CLICK, + [item_global_id, event], + true + ) + + $mouse_layer/verbs_menu.clear_tool_texture() + close_inventory() + + +func right_click_on_item(item_global_id: String, event: InputEvent) -> void: + element_focused(item_global_id) + var object = escoria.object_manager.get_object(item_global_id) + if object != null: + $mouse_layer/verbs_menu.set_by_name("look") + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_RIGHT_CLICK, + [item_global_id, event], + true + ) + +func left_double_click_on_item(item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_LEFT_CLICK, + [item_global_id, event], + true + ) + + +## INVENTORY ## +func left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void: + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_LEFT_CLICK, + [inventory_item_global_id, event] + ) + + if escoria.action_manager.current_action == VERB_USE: + var object = escoria.object_manager.get_object( + inventory_item_global_id + ) + var item = object.node + if item.has_method("get_sprite") and item.get_sprite().texture: + $mouse_layer/verbs_menu.set_tool_texture( + item.get_sprite().texture + ) + elif item.inventory_item.texture_normal: + $mouse_layer/verbs_menu.set_tool_texture( + item.inventory_item.texture_normal + ) + escoria.action_manager.current_tool = object + + if escoria.action_manager.current_target != null: + $mouse_layer/verbs_menu.clear_tool_texture() + $mouse_layer/verbs_menu.set_by_name(VERB_WALK) + +func right_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void: + element_focused(inventory_item_global_id) + var object = escoria.object_manager.get_object(inventory_item_global_id) + if object != null: + escoria.action_manager.set_current_action("look") + escoria.action_manager.do( + escoria.action_manager.ACTION.ITEM_RIGHT_CLICK, + [inventory_item_global_id, event], + true + ) + + +func left_double_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void: + pass + + +func inventory_item_focused(inventory_item_global_id: String) -> void: + if not $mouse_layer/verbs_menu.action_manually_changed: + var item_node: ESCItem = escoria.object_manager.get_object( + inventory_item_global_id + ).node + $ui/tooltip.set_target(item_node.tooltip_name) + $mouse_layer/verbs_menu.set_by_name(item_node.default_action_inventory) + + +func inventory_item_unfocused() -> void: + $ui/tooltip.clear() + if escoria.action_manager.current_action == VERB_WALK: + $mouse_layer/verbs_menu.set_by_name(VERB_WALK) + if $mouse_layer/verbs_menu.action_manually_changed: + $mouse_layer/verbs_menu.action_manually_changed = false + elif not $mouse_layer/verbs_menu.action_manually_changed \ + and escoria.action_manager.current_tool == null: + $mouse_layer/verbs_menu.set_by_name(VERB_WALK) + + +func open_inventory(): + $ui/inventory_ui.show_inventory() + + +func close_inventory(): + $ui/inventory_ui.hide_inventory() + + +func mousewheel_action(direction: int): + $mouse_layer/verbs_menu.iterate_actions_cursor(direction) + + +func hide_ui(): + $ui/inventory_ui.propagate_call("set_visible", [false], true) + $ui/tooltip.propagate_call("set_visible", [false], true) + $ui/HBoxContainer/VBoxContainer.visible = false + $ui/HBoxContainer/VBoxContainer/MenuButton.visible = false + + +func show_ui(): + $ui/inventory_ui.propagate_call("set_visible", [true], true) + $ui/tooltip.propagate_call("set_visible", [true], true) + $ui/HBoxContainer/VBoxContainer.visible = true + $ui/HBoxContainer/VBoxContainer/MenuButton.visible = true + + +func hide_main_menu(): + show_ui() + escoria.current_state = escoria.GAME_STATE.DEFAULT + if get_node(main_menu).visible: + get_node(main_menu).hide() + +func show_main_menu(): + if escoria.current_state == escoria.GAME_STATE.PAUSED: + return + escoria.current_state = escoria.GAME_STATE.PAUSED + hide_ui() + if not get_node(main_menu).visible: + get_node(main_menu).reset() + get_node(main_menu).show() + +func unpause_game(): + show_ui() + escoria.current_state = escoria.GAME_STATE.DEFAULT + if get_node(pause_menu).visible: + get_node(pause_menu).hide() + escoria.object_manager.get_object(ESCObjectManager.SPEECH).node.resume() + escoria.main.current_scene.game.show_ui() + escoria.main.current_scene.show() + escoria.set_game_paused(false) + +func pause_game(): + if escoria.current_state == escoria.GAME_STATE.PAUSED: + return + show_ui() + escoria.current_state = escoria.GAME_STATE.PAUSED + if not get_node(pause_menu).visible: + get_node(main_menu).reset() + get_node(pause_menu).reset() + get_node(pause_menu).set_save_enabled( + escoria.save_manager.save_enabled + ) + get_node(pause_menu).show() + escoria.object_manager.get_object(ESCObjectManager.SPEECH).node.pause() + escoria.main.current_scene.game.hide_ui() + escoria.main.current_scene.hide() + escoria.set_game_paused(true) + + +func apply_custom_settings(custom_settings: Dictionary): + if custom_settings.has("a_custom_setting"): + escoria.logger.info( + self, + "custom setting value loaded: %s." + % str(custom_settings["a_custom_setting"]) + ) + + +func get_custom_data() -> Dictionary: + return { + "ui_type": "simplemouse" + } + + +# Update the tooltip +func update_tooltip_following_mouse_position(): + _current_mouse_pos = get_viewport().get_mouse_position() + var corrected_position = _current_mouse_pos \ + + Vector2(32, -tooltip_node.size.y/2) + + # clamp TOP + if tooltip_node.tooltip_distance_to_edge_top(_current_mouse_pos) <= mouse_tooltip_margin: + corrected_position.y = mouse_tooltip_margin + + # clamp BOTTOM + if tooltip_node.tooltip_distance_to_edge_bottom(_current_mouse_pos + tooltip_node.size) <= mouse_tooltip_margin: + corrected_position.y = escoria.game_size.y - mouse_tooltip_margin - tooltip_node.size.y + + # clamp LEFT + if tooltip_node.tooltip_distance_to_edge_left(_current_mouse_pos - tooltip_node.size/2) <= mouse_tooltip_margin: + corrected_position.x = mouse_tooltip_margin + + # clamp RIGHT + if tooltip_node.tooltip_distance_to_edge_right(_current_mouse_pos + tooltip_node.size/2) <= mouse_tooltip_margin: + corrected_position.x = escoria.game_size.x - mouse_tooltip_margin - tooltip_node.size.x + + tooltip_node.position = corrected_position + tooltip_node.offset_from_cursor + + +func _on_action_finished(): + $mouse_layer/verbs_menu.clear_tool_texture() + $mouse_layer/verbs_menu.iterate_actions_cursor(0) + + +func _on_event_done(_return_code: int, _event_name: String): + if _return_code == ESCExecution.RC_OK: + escoria.action_manager.clear_current_action() + $ui/tooltip.set_target("") + $mouse_layer/verbs_menu.set_by_name(VERB_WALK) + + +func _on_room_ready(): + $mouse_layer/verbs_menu.set_by_name(VERB_WALK) + $mouse_layer/verbs_menu.action_manually_changed = false diff --git a/addons/escoria-ui-simplemouse/game.gd.uid b/addons/escoria-ui-simplemouse/game.gd.uid new file mode 100644 index 0000000..db8b4ac --- /dev/null +++ b/addons/escoria-ui-simplemouse/game.gd.uid @@ -0,0 +1 @@ +uid://dqrptsg8qv2o7 diff --git a/addons/escoria-ui-simplemouse/game.tscn b/addons/escoria-ui-simplemouse/game.tscn new file mode 100644 index 0000000..267f82c --- /dev/null +++ b/addons/escoria-ui-simplemouse/game.tscn @@ -0,0 +1,90 @@ +[gd_scene format=3 uid="uid://u646id5k2hvg"] + +[ext_resource type="PackedScene" uid="uid://ciy2hn83a3rim" path="res://addons/escoria-ui-simplemouse/inventory/inventory_ui.tscn" id="1"] +[ext_resource type="Script" uid="uid://dfl7khtlretr7" path="res://addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd" id="2"] +[ext_resource type="PackedScene" uid="uid://dmw5gicuenj53" path="res://addons/escoria-core/game/scenes/camera_player/camera.tscn" id="3"] +[ext_resource type="PackedScene" uid="uid://0qms6fx5siad" path="res://addons/escoria-ui-simplemouse/verbs_mouseicons.tscn" id="4"] +[ext_resource type="Script" uid="uid://dqrptsg8qv2o7" path="res://addons/escoria-ui-simplemouse/game.gd" id="5"] +[ext_resource type="PackedScene" uid="uid://b3hru28v8y36i" path="res://addons/escoria-ui-simplemouse/tooltip/target_tooltip.tscn" id="6"] +[ext_resource type="PackedScene" uid="uid://u8an5av5fuxx" path="res://addons/escoria-core/ui_library/menus/main_menu/main_menu.tscn" id="7"] +[ext_resource type="PackedScene" uid="uid://gfjgmbcafyyt" path="res://addons/escoria-core/ui_library/menus/pause_menu/pause_menu.tscn" id="8"] +[ext_resource type="FontFile" path="res://addons/escoria-ui-simplemouse/fonts/caslonantique.tres" id="8_c4xbd"] +[ext_resource type="Theme" uid="uid://cujvdn6gu1j74" path="res://addons/escoria-ui-simplemouse/theme.tres" id="9"] + +[node name="game" type="Node2D" unique_id=786292577] +script = ExtResource("5") +main_menu = NodePath("ui/main_menu") +pause_menu = NodePath("ui/pause_menu") +editor_debug_mode = 1 + +[node name="camera" parent="." unique_id=1900634094 instance=ExtResource("3")] + +[node name="dialog_layer" type="CanvasLayer" parent="." unique_id=463489275] +layer = 3 + +[node name="ESCDialogPlayer" type="Control" parent="dialog_layer" unique_id=341263763] +layout_mode = 3 +anchors_preset = 0 +theme = ExtResource("9") +script = ExtResource("2") +metadata/_custom_type_script = "uid://dfl7khtlretr7" + +[node name="ui" type="CanvasLayer" parent="." unique_id=506331918] + +[node name="tooltip" parent="ui" unique_id=1830597808 instance=ExtResource("6")] +offset_right = 1.0 +mouse_filter = 2 +theme_override_fonts/normal_font = ExtResource("8_c4xbd") +theme_override_font_sizes/normal_font_size = 21 +text = "[center][color=#000000][/color][/center]" +fit_content = true + +[node name="inventory_ui" parent="ui" unique_id=436402962 instance=ExtResource("1")] + +[node name="HBoxContainer" type="HBoxContainer" parent="ui" unique_id=8246514] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 11.0 +offset_top = 621.0 +offset_right = -442.0 +offset_bottom = -107.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="ui/HBoxContainer" unique_id=669657222] +layout_mode = 2 +mouse_filter = 2 + +[node name="MenuButton" type="Button" parent="ui/HBoxContainer/VBoxContainer" unique_id=878421894] +layout_mode = 2 +text = "Menu" + +[node name="pause_menu" parent="ui" unique_id=828994872 instance=ExtResource("8")] +visible = false +theme = ExtResource("9") + +[node name="main_menu" parent="ui" unique_id=1361358924 instance=ExtResource("7")] +visible = false + +[node name="mouse_layer" type="CanvasLayer" parent="." unique_id=820408640] +layer = 2 + +[node name="verbs_menu" parent="mouse_layer" unique_id=50442657 instance=ExtResource("4")] +anchors_preset = 0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 156.0 +offset_top = 810.0 +offset_right = 156.0 +offset_bottom = 900.0 +grow_horizontal = 1 +grow_vertical = 1 +size_flags_horizontal = 2 +size_flags_vertical = 3 +size_flags_stretch_ratio = 3.0 + +[connection signal="pressed" from="ui/HBoxContainer/VBoxContainer/MenuButton" to="." method="_on_MenuButton_pressed"] diff --git a/addons/escoria-ui-simplemouse/images/frame.png b/addons/escoria-ui-simplemouse/images/frame.png new file mode 100644 index 0000000..2831220 Binary files /dev/null and b/addons/escoria-ui-simplemouse/images/frame.png differ diff --git a/addons/escoria-ui-simplemouse/images/frame.png.import b/addons/escoria-ui-simplemouse/images/frame.png.import new file mode 100644 index 0000000..55e9466 --- /dev/null +++ b/addons/escoria-ui-simplemouse/images/frame.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://co272dy0sfx2g" +path="res://.godot/imported/frame.png-1d3e8273dff4a5d93c4b276f04368db9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/images/frame.png" +dest_files=["res://.godot/imported/frame.png-1d3e8273dff4a5d93c4b276f04368db9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/images/inventory_bg.png b/addons/escoria-ui-simplemouse/images/inventory_bg.png new file mode 100644 index 0000000..0efa8a0 Binary files /dev/null and b/addons/escoria-ui-simplemouse/images/inventory_bg.png differ diff --git a/addons/escoria-ui-simplemouse/images/inventory_bg.png.import b/addons/escoria-ui-simplemouse/images/inventory_bg.png.import new file mode 100644 index 0000000..0ab5ab6 --- /dev/null +++ b/addons/escoria-ui-simplemouse/images/inventory_bg.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnm5mo81sxjce" +path="res://.godot/imported/inventory_bg.png-7eb21dfcffc15b56c54bdb44fca1adc2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/images/inventory_bg.png" +dest_files=["res://.godot/imported/inventory_bg.png-7eb21dfcffc15b56c54bdb44fca1adc2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/images/inventory_icon.png b/addons/escoria-ui-simplemouse/images/inventory_icon.png new file mode 100644 index 0000000..7f8f637 Binary files /dev/null and b/addons/escoria-ui-simplemouse/images/inventory_icon.png differ diff --git a/addons/escoria-ui-simplemouse/images/inventory_icon.png.import b/addons/escoria-ui-simplemouse/images/inventory_icon.png.import new file mode 100644 index 0000000..548a0ab --- /dev/null +++ b/addons/escoria-ui-simplemouse/images/inventory_icon.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dg6l601bc6ux0" +path="res://.godot/imported/inventory_icon.png-2bb7d7b5303fb2a8513194a276c7ade4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/images/inventory_icon.png" +dest_files=["res://.godot/imported/inventory_icon.png-2bb7d7b5303fb2a8513194a276c7ade4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/images/inventory_panel.png b/addons/escoria-ui-simplemouse/images/inventory_panel.png new file mode 100644 index 0000000..c206cdc Binary files /dev/null and b/addons/escoria-ui-simplemouse/images/inventory_panel.png differ diff --git a/addons/escoria-ui-simplemouse/images/inventory_panel.png.import b/addons/escoria-ui-simplemouse/images/inventory_panel.png.import new file mode 100644 index 0000000..80e77c2 --- /dev/null +++ b/addons/escoria-ui-simplemouse/images/inventory_panel.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c122ce0t7qtoe" +path="res://.godot/imported/inventory_panel.png-287f1cfb120c8e8aa3a655706cbfd61a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/images/inventory_panel.png" +dest_files=["res://.godot/imported/inventory_panel.png-287f1cfb120c8e8aa3a655706cbfd61a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg b/addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg new file mode 100644 index 0000000..ef9d3ab --- /dev/null +++ b/addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg @@ -0,0 +1,53 @@ + + + + + + + + + + diff --git a/addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg.import b/addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg.import new file mode 100644 index 0000000..1342480 --- /dev/null +++ b/addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://crdj5w3sdmdkp" +path="res://.godot/imported/inventory_panel.svg-6089edd254ccbef7c9d678190e2ee9db.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-ui-simplemouse/images/originals/inventory_panel.svg" +dest_files=["res://.godot/imported/inventory_panel.svg-6089edd254ccbef7c9d678190e2ee9db.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/escoria-ui-simplemouse/inventory/inventory_ui.gd b/addons/escoria-ui-simplemouse/inventory/inventory_ui.gd new file mode 100644 index 0000000..14cf871 --- /dev/null +++ b/addons/escoria-ui-simplemouse/inventory/inventory_ui.gd @@ -0,0 +1,88 @@ +extends ESCInventory + + +# Whether the inventory is visible currently +var inventory_visible: bool = false + +var hovering_inventory_item: bool = false + +var _tween: Tween3 + +var being_closed: bool +var being_opened: bool + +@onready var inventory_scene = $panel + +# Height of the title part of the inventory that stays visible when inventory +# is hidden. To be computed depending on the resolution of the game, hardcoded +# here for demonstration purpose. +const _inventory_title_height: int = 34 + +func _ready() -> void: + super._ready() + _tween = Tween3.new(self) + # Hide inventory by default + hide_inventory() + + +func show_inventory(): + if being_opened: + return + being_opened = true + _tween.stop() + _tween.reset() + var start_pos_y = inventory_scene.position.y + var end_pos_y = get_viewport_rect().size.y - inventory_scene.size.y + _tween.interpolate_property( + inventory_scene, + "position:y", + start_pos_y, + end_pos_y, + 0.4, + Tween.TransitionType.TRANS_CUBIC + ) + _tween.play() + await _tween.finished + _tween.stop() + inventory_visible = true + being_opened = false + +func hide_inventory(): + if being_closed: + return + being_closed = true + _tween.stop() + _tween.reset() + var start_pos_y = inventory_scene.position.y + var end_pos_y = get_viewport_rect().size.y - _inventory_title_height + _tween.interpolate_property( + inventory_scene, + "position:y", + start_pos_y, + end_pos_y, + 0.4, + Tween.TransitionType.TRANS_CUBIC + ) + _tween.play() + await _tween.finished + _tween.stop() + inventory_visible = false + being_closed = false + + +func detector_in(): + if not inventory_visible: + inventory_scene.get_node("MarginContainer/ScrollContainer/container").item_focused = false + show_inventory() + $panel/detector_out.show() + + +func detector_out(): + if inventory_visible: + hide_inventory() + $panel/detector_out.hide() + + + +func _on_detector_out_gui_input(event): + detector_out() diff --git a/addons/escoria-ui-simplemouse/inventory/inventory_ui.gd.uid b/addons/escoria-ui-simplemouse/inventory/inventory_ui.gd.uid new file mode 100644 index 0000000..17140e9 --- /dev/null +++ b/addons/escoria-ui-simplemouse/inventory/inventory_ui.gd.uid @@ -0,0 +1 @@ +uid://cmyyk7qinkift diff --git a/addons/escoria-ui-simplemouse/inventory/inventory_ui.tscn b/addons/escoria-ui-simplemouse/inventory/inventory_ui.tscn new file mode 100644 index 0000000..f873643 --- /dev/null +++ b/addons/escoria-ui-simplemouse/inventory/inventory_ui.tscn @@ -0,0 +1,90 @@ +[gd_scene load_steps=4 format=3 uid="uid://ciy2hn83a3rim"] + +[ext_resource type="Script" uid="uid://cmyyk7qinkift" path="res://addons/escoria-ui-simplemouse/inventory/inventory_ui.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://yegfvvcmtdnp" path="res://addons/escoria-ui-simplemouse/images/inventory_panel.png" id="2_grkag"] +[ext_resource type="Script" uid="uid://c4syt26p7mg66" path="res://addons/escoria-core/ui_library/inventory/esc_inventory_container.gd" id="3"] + +[node name="inventory_ui" type="Control"] +custom_minimum_size = Vector2(90, 90) +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 0 +size_flags_vertical = 3 +mouse_filter = 2 +script = ExtResource("1") +inventory_ui_container = NodePath("panel/MarginContainer/ScrollContainer/container") + +[node name="panel" type="TextureRect" parent="."] +custom_minimum_size = Vector2(0, 160) +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_top = 866.0 +offset_bottom = 1060.0 +grow_horizontal = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +texture = ExtResource("2_grkag") + +[node name="Label" type="Label" parent="panel"] +layout_mode = 2 +offset_left = 75.0 +offset_top = 6.0 +offset_right = 315.0 +offset_bottom = 29.0 +text = "INVENTORY_LABEL" +horizontal_alignment = 1 + +[node name="MarginContainer" type="MarginContainer" parent="panel"] +layout_mode = 2 +offset_top = 34.0 +offset_right = 1280.0 +offset_bottom = 193.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 +metadata/_edit_lock_ = true + +[node name="ScrollContainer" type="ScrollContainer" parent="panel/MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 2 + +[node name="container" type="HBoxContainer" parent="panel/MarginContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 2 +theme_override_constants/separation = 20 +script = ExtResource("3") + +[node name="detector_in" type="Control" parent="panel"] +anchors_preset = 0 +offset_right = 1281.0 +offset_bottom = 34.0 +mouse_filter = 1 + +[node name="detector_out" type="Control" parent="panel"] +layout_mode = 1 +anchor_top = -4.469 +anchor_right = 1.0 +anchor_bottom = 0.18 +offset_top = -0.013916 +offset_bottom = -36.92 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 + +[connection signal="mouse_entered" from="panel/detector_in" to="." method="detector_in"] +[connection signal="gui_input" from="panel/detector_out" to="." method="_on_detector_out_gui_input"] +[connection signal="mouse_entered" from="panel/detector_out" to="." method="detector_out"] diff --git a/addons/escoria-ui-simplemouse/plugin.cfg b/addons/escoria-ui-simplemouse/plugin.cfg new file mode 100644 index 0000000..652ec10 --- /dev/null +++ b/addons/escoria-ui-simplemouse/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Escoria Simple Mouse UI" +description="Simple Mouse UI for the Escoria Framework" +author="Escoria project" +version="1.0.0" +script="plugin.gd" diff --git a/addons/escoria-ui-simplemouse/plugin.gd b/addons/escoria-ui-simplemouse/plugin.gd new file mode 100644 index 0000000..7479843 --- /dev/null +++ b/addons/escoria-ui-simplemouse/plugin.gd @@ -0,0 +1,24 @@ +@tool +# Plugin script to initialize Escoria simple mouse UI +extends EditorPlugin + + +# Override function to return the plugin name. +func _get_plugin_name(): + return "escoria-ui-simplemouse" + + +# Deregister UI +func _disable_plugin(): + print("Disabling plugin Escoria UI Simple Mouse.") + EscoriaPlugin.deregister_ui("res://addons/escoria-ui-simplemouse/game.tscn") + + +# Register UI with Escoria +func _enable_plugin(): + print("Enabling plugin Escoria UI Simple Mouse.") + if not EscoriaPlugin.register_ui(self, "res://addons/escoria-ui-simplemouse/game.tscn"): + get_editor_interface().set_plugin_enabled( + _get_plugin_name(), + false + ) diff --git a/addons/escoria-ui-simplemouse/plugin.gd.uid b/addons/escoria-ui-simplemouse/plugin.gd.uid new file mode 100644 index 0000000..14104e8 --- /dev/null +++ b/addons/escoria-ui-simplemouse/plugin.gd.uid @@ -0,0 +1 @@ +uid://bfql57p6jmut7 diff --git a/addons/escoria-ui-simplemouse/theme.tres b/addons/escoria-ui-simplemouse/theme.tres new file mode 100644 index 0000000..47fee1a --- /dev/null +++ b/addons/escoria-ui-simplemouse/theme.tres @@ -0,0 +1,6 @@ +[gd_resource type="Theme" load_steps=2 format=3 uid="uid://cujvdn6gu1j74"] + +[ext_resource type="FontFile" path="res://addons/escoria-ui-simplemouse/fonts/caslonantique.tres" id="1"] + +[resource] +default_font = ExtResource("1") diff --git a/addons/escoria-ui-simplemouse/tooltip/target_tooltip.tscn b/addons/escoria-ui-simplemouse/tooltip/target_tooltip.tscn new file mode 100644 index 0000000..9430ef1 --- /dev/null +++ b/addons/escoria-ui-simplemouse/tooltip/target_tooltip.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=3 uid="uid://b3hru28v8y36i"] + +[ext_resource type="Script" uid="uid://cytpwmod6bdw6" path="res://addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd" id="2"] + +[node name="tooltip" type="RichTextLabel"] +bbcode_enabled = true +text = "[center][color=#ffffff][/color][/center]" +scroll_active = false +script = ExtResource("2") +color = Color(1, 1, 1, 1) +offset_from_cursor = Vector2(0, 0) diff --git a/addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd b/addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd new file mode 100644 index 0000000..c975b30 --- /dev/null +++ b/addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd @@ -0,0 +1,28 @@ +extends ESCTooltip + + +signal tooltip_size_updated + + +func update_tooltip_text(): + # Need to update size of bbcode rect before updating the text itself otherwise on the + # first frame the text is wider than the default of 0 and ends up being really tall + # and setting the wrong vertical margin for the tooltip + update_size() + + # We signal this here since the processing in this class happens AFTER input + # processing. We signal here to avoid "lagging" behind a frame since + # tooltips are presently dependent on the size of the bounding box around + # the rendered string. + tooltip_size_updated.emit() + + text = "[center]" + text += "[color=#" + color.to_html(false) + "]" + text += current_target + text += "[/color]" + text += "[/center]" +# push_align(RichTextLabel.ALIGN_CENTER) +# push_color(color) +# append_bbcode(current_target) +# pop() +# pop() diff --git a/addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd.uid b/addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd.uid new file mode 100644 index 0000000..a2c3b02 --- /dev/null +++ b/addons/escoria-ui-simplemouse/tooltip/tooltip_target.gd.uid @@ -0,0 +1 @@ +uid://cytpwmod6bdw6 diff --git a/addons/escoria-ui-simplemouse/verbs_mouseicons.gd b/addons/escoria-ui-simplemouse/verbs_mouseicons.gd new file mode 100644 index 0000000..0413fe6 --- /dev/null +++ b/addons/escoria-ui-simplemouse/verbs_mouseicons.gd @@ -0,0 +1,54 @@ +extends Control + + +var current_cursor_id: int = 0 +@onready var cursors: Array = $actions.get_children() + +@onready var action_manually_changed = false + + +func _ready(): + if !Engine.is_editor_hint(): + current_cursor_id = cursors.size() + set_by_name("walk") + set_process(false) + +func _process(delta): + $mouse_position.global_position = get_global_mouse_position() + + +func iterate_actions_cursor(direction: int): + current_cursor_id += direction + if current_cursor_id > 4: + current_cursor_id = 0 + elif current_cursor_id < 0: + current_cursor_id = 4 + set_by_name(cursors[current_cursor_id].name) + if $mouse_position/tool.texture != null: + clear_tool_texture() + action_manually_changed = true + + +func set_by_name(name: String, force_verb: String = "") -> void: + for i in cursors.size(): + if cursors[i].name == name: + current_cursor_id = i + break + + Input.set_custom_mouse_cursor( + cursors[current_cursor_id].texture, + Input.CURSOR_ARROW, + Vector2(32,32) + ) + if force_verb.is_empty(): + escoria.action_manager.set_current_action(cursors[current_cursor_id].name) + else: + escoria.action_manager.set_current_action(force_verb) + +func set_tool_texture(texture: Texture2D): + set_process(true) + $mouse_position/tool.texture = texture + +func clear_tool_texture(): + $mouse_position/tool.texture = null + set_process(false) diff --git a/addons/escoria-ui-simplemouse/verbs_mouseicons.gd.uid b/addons/escoria-ui-simplemouse/verbs_mouseicons.gd.uid new file mode 100644 index 0000000..0d752ae --- /dev/null +++ b/addons/escoria-ui-simplemouse/verbs_mouseicons.gd.uid @@ -0,0 +1 @@ +uid://d2mlyj6637kqo diff --git a/addons/escoria-ui-simplemouse/verbs_mouseicons.tscn b/addons/escoria-ui-simplemouse/verbs_mouseicons.tscn new file mode 100644 index 0000000..aab2453 --- /dev/null +++ b/addons/escoria-ui-simplemouse/verbs_mouseicons.tscn @@ -0,0 +1,88 @@ +[gd_scene load_steps=9 format=3 uid="uid://0qms6fx5siad"] + +[ext_resource type="Script" uid="uid://d2mlyj6637kqo" path="res://addons/escoria-ui-simplemouse/verbs_mouseicons.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://dy1v07dsq0gv6" path="res://addons/escoria-ui-simplemouse/cursors/new/target_a.png" id="2_pg8p6"] +[ext_resource type="Texture2D" uid="uid://kntfqvpgma13" path="res://addons/escoria-ui-simplemouse/cursors/new/look_b.png" id="3_iryjo"] +[ext_resource type="Texture2D" uid="uid://2tj2uhyd8un1" path="res://addons/escoria-ui-simplemouse/cursors/new/hand_open.png" id="4_63dmd"] +[ext_resource type="Texture2D" uid="uid://c6dy0a2st8l6k" path="res://addons/escoria-ui-simplemouse/cursors/new/tool_wrench.png" id="5_8bddv"] +[ext_resource type="Texture2D" uid="uid://dsvbjwnblrs51" path="res://addons/escoria-ui-simplemouse/cursors/new/message_dots_round.png" id="6_u34a3"] +[ext_resource type="Texture2D" uid="uid://bmah30wxobv25" path="res://addons/escoria-ui-simplemouse/cursors/new/arrow_w.png" id="7_p2p4e"] +[ext_resource type="Texture2D" uid="uid://bm7mm8uhe2dey" path="res://addons/escoria-ui-simplemouse/cursors/new/arrow_e.png" id="8_5phyn"] + +[node name="verbs_menu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1") + +[node name="actions" type="HBoxContainer" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -6.0 +offset_top = -1.0 +offset_right = -6.0 +offset_bottom = -1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="walk" type="TextureRect" parent="actions"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +texture = ExtResource("2_pg8p6") + +[node name="look" type="TextureRect" parent="actions"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +texture = ExtResource("3_iryjo") + +[node name="pickup" type="TextureRect" parent="actions"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +texture = ExtResource("4_63dmd") + +[node name="use" type="TextureRect" parent="actions"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +texture = ExtResource("5_8bddv") + +[node name="talk" type="TextureRect" parent="actions"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +texture = ExtResource("6_u34a3") + +[node name="exit_left" type="TextureRect" parent="actions"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +texture = ExtResource("7_p2p4e") + +[node name="exit_right" type="TextureRect" parent="actions"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +texture = ExtResource("8_5phyn") + +[node name="mouse_position" type="Control" parent="."] +anchors_preset = 0 +mouse_filter = 2 + +[node name="tool" type="TextureRect" parent="mouse_position"] +layout_mode = 0 +offset_left = 48.0 +offset_top = -16.0 +offset_right = 88.0 +offset_bottom = 24.0 +mouse_filter = 2 diff --git a/addons/escoria-wizard/BackgroundItem.gd b/addons/escoria-wizard/BackgroundItem.gd new file mode 100644 index 0000000..57aadb7 --- /dev/null +++ b/addons/escoria-wizard/BackgroundItem.gd @@ -0,0 +1,139 @@ +@tool +extends MarginContainer + +var source_image:Image +var image_stream_texture:CompressedTexture2D +var image_has_been_loaded:bool +var image_size:Vector2 + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + background_item_reset() + + +func background_item_reset() -> void: + $Content/GridContainer/ItemName.text = "replace_me" + $Content/GridContainer/ItemGlobalID.text = "" + $Content/GridContainer/StartsInnteractiveCheckBox.button_pressed = true + if $Content/GridContainer/DefaultActionOption.get_item_count() > 0: + $Content/GridContainer/DefaultActionOption.clear() + for option_list in ["look", "pick up", "open", "close", "use", "push", "pull", "talk"]: + $Content/GridContainer/DefaultActionOption.add_item(option_list) + $Content/GridContainer/DefaultActionOption.selected = 0 + image_size = Vector2.ZERO + image_has_been_loaded = false + + +func background_on_ItemName_text_changed(new_text: String) -> void: + $Content/GridContainer/ItemGlobalID.text = new_text + + +func load_button_pressed() -> void: + $LoadObjectGraphic/LoadObjectFileDialog.popup() + + +func LoadObjectFileDialog_file_selected(path: String) -> void: + image_stream_texture = load(path) + + $Content/GridContainer/Preview/Preview.texture = image_stream_texture + + var preview_size = $Content/GridContainer/Preview/Preview.size + + # Calculate the scale to make the preview as big as possible in the preview window depending on + # the height to width ratio of the frame + image_size = image_stream_texture.get_size() + var preview_scale = Vector2.ONE + preview_scale.x = preview_size.x / image_size.x + preview_scale.y = preview_size.y / image_size.y + + if preview_scale.y > preview_scale.x: + $Content/GridContainer/Preview/Preview.scale = Vector2(preview_scale.x, preview_scale.x) + else: + $Content/GridContainer/Preview/Preview.scale = Vector2(preview_scale.y, preview_scale.y) + + $Content/GridContainer/ImageSize.text = "(%s, %s)" % [image_size.x, image_size.y] + + $Content/GridContainer/ImagePath.text = path + image_has_been_loaded = true + + + +func _on_CreateButton_pressed() -> void: + var err_window = "../InformationWindows/generic_error_window" + if ! image_has_been_loaded: + get_node(err_window).dialog_text = \ + "No image has been loaded." + get_node(err_window).popup() + return + if $Content/GridContainer/ItemName.text == "replace_me": + get_node(err_window).dialog_text = \ + "Please change the object name." + get_node(err_window).popup() + return + + var item = ESCItem.new() + item.name = $Content/GridContainer/ItemName.text + item.global_id = $Content/GridContainer/ItemGlobalID.text + item.is_interactive = $Content/GridContainer/StartsInnteractiveCheckBox.pressed + item.tooltip_name = $Content/GridContainer/ItemName.text + + var selected_index = $Content/GridContainer/DefaultActionOption.selected + item.default_action = $Content/GridContainer/DefaultActionOption.get_item_text(selected_index) + + # Add sprite to the background item + var item_sprite = Sprite2D.new() + item_sprite.texture = $Content/GridContainer/Preview/Preview.texture + item.add_child(item_sprite) + + # Add Dialog Position to the background item + var interact_position = ESCLocation.new() + interact_position.name = "interact_position" + interact_position.position.y = image_size.y * 2 + print("A") + item.add_child(interact_position) + + # Add Collision shape to the background item + var rectangle_shape = RectangleShape2D.new() + var collision_shape = CollisionShape2D.new() + + collision_shape.shape = rectangle_shape + collision_shape.shape.extents = image_size / 2 + print("B") + item.add_child(collision_shape) + print("c") + # Make it so all the nodes can be seen in the scene tree + interact_position.set_owner(item) + print("d") + collision_shape.set_owner(item) + print("e") + + item_sprite.set_owner(item) + + print("f") + +# get_tree().edited_scene_root.add_child(item) +# item.set_owner(get_tree().edited_scene_root) + + + var current_node = EditorPlugin.new().get_editor_interface().get_selection().get_selected_nodes()[0] +# print("Cur = "+str(current_node)) +# print("Cur0 = "+str(current_node[0])) + print("g") + + current_node.add_child(item) + print("h") + +# item.set_owner(current_node) + item.set_owner(get_tree().edited_scene_root) + print("Created.") + #item.queue_free() + +#func get_edited_root() -> Node: +# var plugin := EditorPlugin.new() +# var eds:EditorInterface +# eds = plugin.get_selection() +# var selected:EditorInterface +# selected = eds.get_selected_nodes()[0] +# if selected: +# return selected.get_tree().get_edited_scene_root() +# return null diff --git a/addons/escoria-wizard/BackgroundItem.gd.uid b/addons/escoria-wizard/BackgroundItem.gd.uid new file mode 100644 index 0000000..87b4c60 --- /dev/null +++ b/addons/escoria-wizard/BackgroundItem.gd.uid @@ -0,0 +1 @@ +uid://chtkau0u4dfi7 diff --git a/addons/escoria-wizard/CharacterCreator.gd b/addons/escoria-wizard/CharacterCreator.gd new file mode 100644 index 0000000..3404cd5 --- /dev/null +++ b/addons/escoria-wizard/CharacterCreator.gd @@ -0,0 +1,1739 @@ +@tool +# Outstanding proposed features +# v1.1 features +# * Have the editor kick in when an ESCPlayer is selected, i.e. "load/edit" +# * Add a settings page (if there's enough features to warrant it). This would have the path the scene gets written to, default angles for each direction so the developer can change them from the default 90s / 45s they are now, whether the ESCPlayer is click-through, errr can't think of anything else. +# * Redo the Escoria tutorial to use the plugin. + +extends Control + +const METADATA_ANIM_NAME = "anim_name" +const METADATA_SPRITESHEET_SOURCE_FILE = "spritesheet_source_file" +const METADATA_SPRITESHEET_FRAMES_HORIZ = "spritesheet_frames_horiz" +const METADATA_SPRITESHEET_FRAMES_VERT = "spritesheet_frames_vert" +const METADATA_SPRITESHEET_FIRST_FRAME = "spritesheet_first_frame" +const METADATA_SPRITESHEET_LAST_FRAME = "spritesheet_last_frame" +const METADATA_SPEED = "speed" +const METADATA_IS_MIRROR = "is_mirror" + +const DIR_UP = "up" +const DIR_UP_RIGHT = "upright" +const DIR_RIGHT = "right" +const DIR_DOWN_RIGHT = "downright" +const DIR_DOWN = "down" +const DIR_DOWN_LEFT = "downleft" +const DIR_LEFT = "left" +const DIR_UP_LEFT = "upleft" + +const DIR_LIST_8 = [DIR_UP, DIR_UP_RIGHT, DIR_RIGHT, DIR_DOWN_RIGHT, DIR_DOWN, DIR_DOWN_LEFT, \ + DIR_LEFT, DIR_UP_LEFT] +const DIR_LIST_4 = [DIR_UP, DIR_RIGHT, DIR_DOWN, DIR_LEFT] +const DIR_LIST_2 = [DIR_RIGHT, DIR_LEFT] +const DIR_LIST_1 = [DIR_DOWN] + +const TYPE_WALK = "walk" +const TYPE_TALK = "talk" +const TYPE_IDLE = "idle" + +const ANIM_IN_PROGRESS = "in_progress" + +const ANIMATION_SPEED_LABEL = "Animation speed" + +# Make the code more readable by shortening node references using constants +const NAME_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer" +const DIR_COUNT_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer" +const CHAR_TYPE_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer" +const ANIM_TYPE_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer" +const MIRROR_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer3/mirror_checkbox" +const ARROWS_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer" +const PREVIEW_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview/MarginContainer" +const PREVIEW_BGRND_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview/anim_preview_background" +const ANIM_CONTROLS_NODE = "VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer" +const STORE_ANIM_NODE = "VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim" +const SCROLL_VBOX_NODE = "VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/" +const SCROLL_CTRL_NODE = "VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control" +const NO_SPRITESHEET_NODE = "VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control/MarginContainer/no_spritesheet_found_sprite" +const CURRENT_SHEET_NODE = "VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current/MarginContainer2/current_spritesheet_label" +const ZOOM_LABEL_NODE = "VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll/zoom_label" +const ZOOM_SCROLL_NODE = "VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll/MarginContainer/zoom_scrollbar" +const CHARACTER_PATH_NODE = "VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer/character_path" +const GENERIC_ERROR_NODE = "InformationWindows/generic_error_window" +const UNSTORED_CHANGE_NODE = "InformationWindows/unstored_changes_window" +const UNSTORED_ANIMTYPE_CHANGE_NODE = "InformationWindows/unstored_changes_window_anim_change" +const EXPORT_PROGRESS_NODE = "InformationWindows/export_progress" +const PROGRESS_LABEL_NODE = "InformationWindows/export_progress/progress_label" +const EXPORT_COMPLETE_NODE = "InformationWindows/export_complete" +const FILE_DIALOG_NODE = "ImageFileDialog" +const CHARACTER_FILE_NODE = "CharacterPathFileDialog" +const CONFIG_FILE = "escoria-wizard.conf" + + +# Test flag - set to true to load test data. +var test_mode: bool = false + +# The currently loaded spritesheet image +var source_image: Image + +# The current size of each animation frame (the spritesheet is broken into squares of this size. +var frame_size: Vector2 + +# The current spritesheet zoom level. +var zoom_value: float = 1 + +# The speed of the animation being previewed in frames-per-second. +var current_animation_speed: int = 5 + +# The animation direction currently being edited +var direction_selected: String + +# This is the animation direction that has been clicked on by the user. +# Once it has been confirmed that there are no unstored changes to the current animation, +# the requested direction will become the "direction_selected". +var direction_requested: String + +# The animation type currently being edited +var animation_type_selected: String + +# This is the animation ty[e that has been clicked on by the user. +# Once it has been confirmed that there are no unstored changes to the current animation, +# the requested type will become the "animation_type_selected". +var animation_type_requested: String + +# This is the array that stores the data for each animation. +var anim_metadata = [] + +# Track the page showing in the help window +var help_window_page = 1 + +# Array to track frame settings so if you do an illegal action (like changing the last sprite frame +# prior to a spritesheet being loaded) the value can be reset +var spritesheet_settings = [1, 1, 0, 0] + +# To stop errors flagging when you change to a mirrored direction +var currently_changing_direction: bool = false + +# Whether all changes are automatically 'saved' or need manual confirmation before storing +var autostore: bool = false + +# Needed due to the yield method used for export to pass back the largest sprite used +# for the character. This will determine the collision shape size. +var export_largest_sprite: Vector2 + + +func _ready() -> void: + load_settings() + character_creator_reset() + $InformationWindows/help_window.current_page = 1 + + if test_mode: + setup_test_data() + + +func character_creator_reset() -> void: + # Disconnect all the signals to stop program logic firing during setup + disconnect_selector_signals() + + get_node(NAME_NODE).get_node("node_name").text = "replace_me" + get_node(NAME_NODE).get_node("global_id").text = "" + get_node(DIR_COUNT_NODE).get_node("four_directions").button_pressed = true + + # For unknown reasons the above doesn't cause the trigger to fire so manual steps required + get_node(DIR_COUNT_NODE).get_node("eight_directions").button_pressed = false + get_node(DIR_COUNT_NODE).get_node("two_directions").button_pressed = false + get_node(DIR_COUNT_NODE).get_node("one_direction").button_pressed = false + + get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed = true + animation_type_selected = "walk" + + # For unknown reasons the above doesn't cause the trigger to fire so manual steps required + if get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed: + get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed = false + + if get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed: + get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed = false + + get_node(NO_SPRITESHEET_NODE).visible = true + zoom_value = 1 + get_node(ZOOM_SCROLL_NODE).value = zoom_value + get_node(ZOOM_LABEL_NODE).text = "Zoom: %sx" % str(zoom_value) + get_node(ANIM_CONTROLS_NODE).get_node("original_size_label").text = "Source sprite size: (0, 0)" + get_node(ANIM_CONTROLS_NODE).get_node("frame_size_label").text = "Frame size: (0, 0)" + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").animation = ANIM_IN_PROGRESS + + preview_hide() + + get_node(STORE_ANIM_NODE).visible = false + + create_empty_animations() + + direction_selected = DIR_UP + activate_direction(direction_selected) + + reset_arrow_colours() + + # Reset GUI controls to initial values + get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value = spritesheet_settings[0] + get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value = spritesheet_settings[1] + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = spritesheet_settings[2] + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = spritesheet_settings[3] + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").value = current_animation_speed + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_label").text = "%s: 5 FPS" % ANIMATION_SPEED_LABEL + get_node(CURRENT_SHEET_NODE).text="No spritesheet loaded." + # Connect all the signals now the base settings are configured to stop program logic firing during setup + reset_frame_outlines() + + # Make sure help window doesn't swallow mouse input + $InformationWindows.visible = false + autostore = $VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/HBoxContainer/AutoStoreCheckBox.button_pressed + connect_selector_signals() + + +func connect_selector_signals() -> void: + get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").connect("value_changed", Callable(self, "controls_on_h_frames_spin_box_value_changed")) + get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").connect("value_changed", Callable(self, "controls_on_v_frames_spin_box_value_changed")) + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").connect("value_changed", Callable(self, "controls_on_start_frame_value_changed")) + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").connect("value_changed", Callable(self, "controls_on_end_frame_value_changed")) + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").connect("value_changed", Callable(self, "controls_on_anim_speed_scroll_bar_value_changed")) + + +func disconnect_selector_signals() -> void: + if get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").is_connected("value_changed", Callable(self, "controls_on_h_frames_spin_box_value_changed")): + get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").disconnect("value_changed", Callable(self, "controls_on_h_frames_spin_box_value_changed")) + + if get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").is_connected("value_changed", Callable(self, "controls_on_v_frames_spin_box_value_changed")): + get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").disconnect("value_changed", Callable(self, "controls_on_v_frames_spin_box_value_changed")) + + if get_node(ANIM_CONTROLS_NODE).get_node("start_frame").is_connected("value_changed", Callable(self, "controls_on_start_frame_value_changed")): + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").disconnect("value_changed", Callable(self, "controls_on_start_frame_value_changed")) + + if get_node(ANIM_CONTROLS_NODE).get_node("end_frame").is_connected("value_changed", Callable(self, "controls_on_end_frame_value_changed")): + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").disconnect("value_changed", Callable(self, "controls_on_end_frame_value_changed")) + + if get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").is_connected("value_changed", Callable(self, "controls_on_anim_speed_scroll_bar_value_changed")): + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").disconnect("value_changed", Callable(self, "controls_on_anim_speed_scroll_bar_value_changed")) + + +func reset_frame_outlines() -> void: + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").zoom_factor = .01 + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").total_num_columns = 1 + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").total_num_rows = 1 + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").start_cell = 0 + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").end_cell = 0 + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").cell_size = Vector2(1,1) + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").queue_redraw() + + +func calc_sprite_size() -> void: + var source_size = source_image.get_size() + var horiz_size = int(source_size.x / get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value) + var vert_size = int(source_size.y / get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value) + + frame_size = Vector2(horiz_size, vert_size) + + get_node(ANIM_CONTROLS_NODE).get_node("original_size_label").text = "Source sprite size: %s" % source_size + get_node(ANIM_CONTROLS_NODE).get_node("frame_size_label").text = "Frame size: %s" % frame_size + + +# Load test data - primarily for testing export, but also for testing general functionality. +# Different spritesheets, mirroring settings, frame counts, and speeds are used deliberately. +func setup_test_data() -> void: +# load_spritesheet("res://addons/escoria-wizard/graphics/mark-animtest.png") +# # Up, right, down, left, up/r, down/r, down/l, up/l +# var start_frames = [15, 10, 7, 10, 12, 14, 1, 14, 12, 3, 1, 1] +# var end_frames = [17, 14, 9, 14, 13, 18, 3, 18, 12, 3, 1, 22] +# var mirrored = [0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0,] +# var sourcefile = [0, 0, 0, 0, 2, 2, 1, 2, 2, 0, 0, 0] +# var fps = [3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3] +# +# for loop in range(12): # 12 for a 4 direction character, 24 for an 8 direction character +# if sourcefile[loop] == 0: +# anim_metadata[loop * 2][METADATA_SPRITESHEET_SOURCE_FILE] = "res://addons/escoria-wizard/graphics/mark-animtest.png" +# anim_metadata[loop * 2][METADATA_SPRITESHEET_FRAMES_HORIZ] = 8 +# anim_metadata[loop * 2][METADATA_SPRITESHEET_FRAMES_VERT] = 3 +# elif sourcefile[loop] == 1: +# anim_metadata[loop * 2][METADATA_SPRITESHEET_SOURCE_FILE] = "res://game/characters/mark/png/mark_talk_down.png" +# anim_metadata[loop * 2][METADATA_SPRITESHEET_FRAMES_HORIZ] = 3 +# anim_metadata[loop * 2][METADATA_SPRITESHEET_FRAMES_VERT] = 1 +# else: +# anim_metadata[loop * 2][METADATA_SPRITESHEET_SOURCE_FILE] = "res://game/characters/mark/png/markjester_talk.png" +# anim_metadata[loop * 2][METADATA_SPRITESHEET_FRAMES_HORIZ] = 21 +# anim_metadata[loop * 2][METADATA_SPRITESHEET_FRAMES_VERT] = 1 +# +# anim_metadata[loop * 2][METADATA_SPRITESHEET_FIRST_FRAME] = start_frames[loop] +# anim_metadata[loop * 2][METADATA_SPRITESHEET_LAST_FRAME] = end_frames[loop] +# anim_metadata[loop * 2][METADATA_SPEED] = fps[loop] +# +# anim_metadata[loop * 2][METADATA_IS_MIRROR] = mirrored[loop] != 0 +# +# get_node(NO_SPRITESHEET_NODE).visible = false +# +# reset_arrow_colours() + # Up, right, down, left, up/r, down/r, down/l, up/l + var spritebase:String="addons/escoria-wizard/graphics/robot" +# load_spritesheet("" + $spritepath + "/walk_up.png") + var start_frames = [1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 5,6,4,3,1,2,4,6] + var end_frames = [16,16,16,16,16,16,16,16, 53,53,53,53,53,53,53,53, 5,6,4,3,1,2,4,6] + var mirrored = [0,0,0,0,0,0,1,1, 0,0,0,0,0,0,1,1, 0,1,1,0,0,0,0,0] + var frames_h = [16,16,16,16,16,16,16,16, 14,14,14,14,14,14,14,14, 6,6,6,6,6,6,6,6] + var frames_v = [1,1,1,1,1,1,1,1, 4,4,4,4,4,4,4,4, 1,1,1,1,1,1,1,1] + + anim_metadata[0][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/walk_up.png" + anim_metadata[1][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/walk_upright.png" + anim_metadata[2][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/walk_right.png" + anim_metadata[3][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/walk_downright.png" + anim_metadata[4][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/walk_down.png" + anim_metadata[5][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/walk_downleft.png" + anim_metadata[6][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/walk_right.png" + anim_metadata[7][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/walk_upright.png" + + anim_metadata[8][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/talk_up.png" + anim_metadata[9][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/talk_upright.png" + anim_metadata[10][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/talk_right.png" + anim_metadata[11][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/talk_downright.png" + anim_metadata[12][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/talk_down.png" + anim_metadata[13][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/talk_downleft.png" + anim_metadata[14][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/talk_right.png" + anim_metadata[15][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/talk_upright.png" + + anim_metadata[16][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/idle_all.png" + anim_metadata[17][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/idle_all.png" + anim_metadata[18][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/idle_all.png" + anim_metadata[19][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/idle_all.png" + anim_metadata[20][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/idle_all.png" + anim_metadata[21][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/idle_all.png" + anim_metadata[22][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/idle_all.png" + anim_metadata[23][METADATA_SPRITESHEET_SOURCE_FILE] = spritebase + "/idle_all.png" + + for loop in range(24): + anim_metadata[loop][METADATA_SPRITESHEET_FIRST_FRAME] = start_frames[loop] + anim_metadata[loop][METADATA_SPRITESHEET_LAST_FRAME] = end_frames[loop] + anim_metadata[loop][METADATA_SPEED] = 24 + anim_metadata[loop][METADATA_IS_MIRROR] = mirrored[loop] != 0 + anim_metadata[loop][METADATA_SPRITESHEET_FRAMES_HORIZ] = frames_h[loop] + anim_metadata[loop][METADATA_SPRITESHEET_FRAMES_VERT] = frames_v[loop] + + get_node(NO_SPRITESHEET_NODE).visible = false + get_node(DIR_COUNT_NODE).get_node("eight_directions").button_pressed = true + for loop in ["four_directions", "two_directions", "one_direction"]: + get_node(DIR_COUNT_NODE).get_node(loop).button_pressed = false + reset_arrow_colours() + + +# Animations are stored as metadata in an array. This creates the initial empty array. +# The preview animation ("in_progress") is the only sprite animation created prior to the final export. +func create_empty_animations() -> void: + var sframes = SpriteFrames.new() + + var metadata_dict = { + METADATA_ANIM_NAME: "tbc", + METADATA_SPRITESHEET_SOURCE_FILE: "tbc", + METADATA_SPRITESHEET_FRAMES_HORIZ: -1, + METADATA_SPRITESHEET_FRAMES_VERT: -1, + METADATA_SPRITESHEET_FIRST_FRAME: 0, + METADATA_SPRITESHEET_LAST_FRAME: 0, + METADATA_SPEED: 30, + METADATA_IS_MIRROR: false + } + + var local_dict + + anim_metadata.clear() + + for typeloop in [TYPE_WALK, TYPE_TALK, TYPE_IDLE]: + for dirloop in DIR_LIST_8: + local_dict = metadata_dict.duplicate() + local_dict[METADATA_ANIM_NAME] = "%s_%s" % [typeloop, dirloop] + anim_metadata.append(local_dict) + + sframes.add_animation(ANIM_IN_PROGRESS) + + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").sprite_frames = sframes + + +# Loads a spritesheet and calculates the size of each sprite frame if loading a spritesheet +# to show a previously stored animation. +func load_spritesheet(file_to_load, read_settings_from_metadata: bool = false, metadata_frame: int = 0) -> void: + if source_image == null: + source_image = Image.new() + + var errorval = source_image.load(file_to_load) + + assert(not errorval, "Error loading file %s" % str(file_to_load)) + + var texture = ImageTexture.create_from_image(source_image) + #texture.set_flags(2) # Godot 4 no longer allows for direct setting of "repetition" or other flags in textures + + get_node(SCROLL_CTRL_NODE).get_node("spritesheet_sprite").texture = texture + + frame_size = source_image.get_size() + + if read_settings_from_metadata: + get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value = anim_metadata[metadata_frame][METADATA_SPRITESHEET_FRAMES_HORIZ] + get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value = anim_metadata[metadata_frame][METADATA_SPRITESHEET_FRAMES_VERT] + else: + get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value = 1 + get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value = 1 + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = 1 + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = 1 + + calc_sprite_size() + + get_node(CURRENT_SHEET_NODE).text = file_to_load + draw_frame_outlines() + set_zoom_scale_automatically(source_image.get_size()) + + # Make scroll bars appear if necessary + get_node(SCROLL_CTRL_NODE).get_node("spritesheet_sprite").custom_minimum_size = source_image.get_size() * zoom_value + + +# spritesheet is loaded. +func set_zoom_scale_automatically(spritesheet_size) -> void: + + var available_space = get_node(SCROLL_VBOX_NODE).get_node("spritesheet_scroll_container").size + + # Calculate the scale to make the preview as big as possible in the preview window depending on + # the height to width ratio of the frame + var spritesheet_scale = Vector2.ONE + spritesheet_scale.x = available_space.x / spritesheet_size.x + spritesheet_scale.y = available_space.y / spritesheet_size.y + var blah = Vector2.ONE + blah.x = spritesheet_size.x / available_space.x + blah.y = spritesheet_size.y / available_space.y + + var newscale = 0.0 + if spritesheet_scale.y > spritesheet_scale.x: + # Round to 1 decimal place + newscale = (int(spritesheet_scale.x * 10.0)) / 10.0 + else: + # Round to 1 decimal place + newscale = (int(spritesheet_scale.y * 10.0)) / 10.0 + if newscale < 0.1: + newscale = 0.1 + if newscale > 5: + newscale = 5 + get_node(ZOOM_SCROLL_NODE).value = newscale + + +# Draws an outline on the spritesheet to show which frames are included in the current animation +func draw_frame_outlines() -> void: + check_frame_limits() + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").zoom_factor = zoom_value + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").total_num_columns = get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").total_num_rows = get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").start_cell = get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").end_cell = get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").cell_size = frame_size + get_node(SCROLL_CTRL_NODE).get_node("frame_rectangles").queue_redraw() + + +# When given a frame number, this calculates the pixel coordinates that frame in the spritesheet +# based on the number of horizontal/vertical frames configured for this spritesheet +func calc_frame_coords(Frame: int) -> Vector2: + var column = (Frame - 1) % int(get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value) * frame_size.x + var row = int((Frame - 1) / get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value) * frame_size.y + return Vector2(column, row) + + +# Updates the animation metadata to store the changed / new settings for a particular animation +func store_animation(animation_to_store: String) -> void: + var texture + var rect_location + var frame_being_copied = Image.new() + var frame_counter: int = 0 + + var metadata_dict = { + METADATA_ANIM_NAME: animation_to_store, + METADATA_SPRITESHEET_SOURCE_FILE: get_node(CURRENT_SHEET_NODE).text, + METADATA_SPRITESHEET_FRAMES_HORIZ: get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value, + METADATA_SPRITESHEET_FRAMES_VERT: get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value, + METADATA_SPRITESHEET_FIRST_FRAME: get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value, + METADATA_SPRITESHEET_LAST_FRAME: get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value, + METADATA_SPEED: get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").value, + METADATA_IS_MIRROR: get_node(MIRROR_NODE).button_pressed + } + + var metadata_array_offset: int = get_metadata_array_offset() + + anim_metadata[metadata_array_offset] = metadata_dict + + if direction_selected == DIR_UP or direction_selected == DIR_DOWN: + return + + # If this direction has already been mirrored, replicate the changes + var opp_dir = find_opposite_direction(direction_selected) + var opp_metadata_array_offset: int = get_metadata_array_offset(opp_dir) + if anim_metadata[opp_metadata_array_offset][METADATA_IS_MIRROR]: + mirror_animation(direction_selected, opp_dir) + + +# Updates the metadata to mirror animation "source" to animation "dest" +# The "source" animation is the animation that really exists as sprite frames +# in the animated sprite +func mirror_animation(source: String, dest: String) -> void: + var texture + var rect_location + var frame_being_copied = Image.new() + var frame_counter: int = 0 +# + var metadata_source_offset = get_metadata_array_offset(source) + var current_anim_type = return_current_animation_type() + var dest_anim_name = "%s_%s" % [current_anim_type, dest] + + var metadata_dict = { + METADATA_ANIM_NAME: dest_anim_name, + METADATA_SPRITESHEET_SOURCE_FILE: anim_metadata[metadata_source_offset][METADATA_SPRITESHEET_SOURCE_FILE], + METADATA_SPRITESHEET_FRAMES_HORIZ: anim_metadata[metadata_source_offset][METADATA_SPRITESHEET_FRAMES_HORIZ], + METADATA_SPRITESHEET_FRAMES_VERT: anim_metadata[metadata_source_offset][METADATA_SPRITESHEET_FRAMES_VERT], + METADATA_SPRITESHEET_FIRST_FRAME: anim_metadata[metadata_source_offset][METADATA_SPRITESHEET_FIRST_FRAME], + METADATA_SPRITESHEET_LAST_FRAME: anim_metadata[metadata_source_offset][METADATA_SPRITESHEET_LAST_FRAME], + METADATA_SPEED: anim_metadata[metadata_source_offset][METADATA_SPEED], + METADATA_IS_MIRROR: true + } + + var metadata_dest_offset = get_metadata_array_offset(dest) + anim_metadata[metadata_dest_offset] = metadata_dict + disconnect_selector_signals() + reset_arrow_colours() + connect_selector_signals() + +func unmirror_animation(anim_to_unmirror: String) -> void: + var metadata_dict = { + METADATA_ANIM_NAME: "tbc", + METADATA_SPRITESHEET_SOURCE_FILE: "tbc", + METADATA_SPRITESHEET_FRAMES_HORIZ: -1, + METADATA_SPRITESHEET_FRAMES_VERT: -1, + METADATA_SPRITESHEET_FIRST_FRAME: 0, + METADATA_SPRITESHEET_LAST_FRAME: 0, + METADATA_SPEED: 30, + METADATA_IS_MIRROR: false + } + spritesheet_settings[2] = 0 + spritesheet_settings[3] = 0 + var metadata_dest_offset = get_metadata_array_offset(anim_to_unmirror) + anim_metadata[metadata_dest_offset] = metadata_dict + reset_arrow_colours() + + +# Shows the preview animation. Required as the no_anim_found sprite doesn't always cover the +# whole preview due to UI peculiarities. +func preview_show(): + get_node(PREVIEW_NODE).get_node("no_anim_found_sprite").visible = false + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").visible = true + + +# Hides the preview animation. Required when the no_anim_found sprite doesn't cover the +# whole preview due to UI peculiarities. +func preview_hide(): + get_node(PREVIEW_NODE).get_node("no_anim_found_sprite").visible = true + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").visible = false + + +# Creates the "in_progress" animation which is shown in the UI as the animation preview based +# on the currently selected settings. +# +# A mirrored animation (frames in reverse order and sprites horizontally flipped) is generated here +# for the purpose of the preview but isn't generated in the final export as it relies on ESCPlayer's +# is_mirrored setting. +func preview_update() -> void: + if get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value > 0: + check_frame_limits() + + var current_anim_type = return_current_animation_type() + var anim_name = "%s_%s" % [current_anim_type, direction_selected] + var offset = get_metadata_array_offset() + var generate_mirror = get_node(MIRROR_NODE).button_pressed + + var texture + var rect_location + var frame_counter: int = 0 + var frame_duration: float = 1.0 + var frame_being_copied: Image = Image.create_empty(frame_size.x, frame_size.y, false, source_image.get_format()) + + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").sprite_frames.clear(ANIM_IN_PROGRESS) + + for loop in range(get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value - get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value + 1): + rect_location = calc_frame_coords(get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value + loop) + frame_being_copied.blit_rect(source_image, Rect2(rect_location, Vector2(frame_size.x, frame_size.y)), Vector2(0, 0)) + + if generate_mirror: + frame_being_copied.flip_x() + + texture = ImageTexture.create_from_image(frame_being_copied) + # Remove the image filter to make pixel correct graphics + #texture.set_flags(2) # Godot 4 no longer allows for the setting of "repetition" (and other) flags + + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").sprite_frames.add_frame(ANIM_IN_PROGRESS, texture, frame_duration, frame_counter) + frame_counter += 1 + + preview_show() + + # Calculate the scale to make the preview as big as possible in the preview window depending on + # the height to width ratio of the frame + var preview_scale = Vector2.ONE + preview_scale.x = get_node(PREVIEW_BGRND_NODE).size.x / frame_size.x + preview_scale.y = get_node(PREVIEW_BGRND_NODE).size.y / frame_size.y + + if preview_scale.y > preview_scale.x: + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").scale = Vector2(preview_scale.x, preview_scale.x) + else: + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").scale = Vector2(preview_scale.y, preview_scale.y) + else: + preview_hide() + + +# Ensure that the spritesheet settings are valid +func check_frame_limits(): + var max_frame = get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value * get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value + + if get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value > max_frame: + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = max_frame + + if get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value > max_frame: + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = max_frame + + if get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value > get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value: + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value + +# If any spritesheet settings have changed, display the "store animation" button to save the changes. +# If the values are manually reset after a change to the previously stored settings, the button will disappear. +func check_if_controls_have_changed(): + var metadata_array_offset: int = get_metadata_array_offset() + var metadata_entry = anim_metadata[metadata_array_offset] + + if autostore == true: + return + + # Need to check this or it registers if you load a sprite and set the number of horizontal + # or vertical frames and haven't set a start/end frame yet + if get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value > 0: + get_node(STORE_ANIM_NODE).visible = \ + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").value != metadata_entry[METADATA_SPEED] \ + or get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value != metadata_entry[METADATA_SPRITESHEET_FIRST_FRAME] \ + or get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value != metadata_entry[METADATA_SPRITESHEET_LAST_FRAME] \ + or get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value != metadata_entry[METADATA_SPRITESHEET_FRAMES_HORIZ] \ + or get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value != metadata_entry[METADATA_SPRITESHEET_FRAMES_VERT] + + +# If the user tries to change settings before they've loaded a spritesheet, this will display +# a warning window instead of letting them change settings. +func has_spritesheet_been_loaded() -> bool: + if source_image == null: + get_node(GENERIC_ERROR_NODE).dialog_text = "Please load a spritesheet to begin." + get_node(GENERIC_ERROR_NODE).popup_centered() + return false + return true + + +func animation_on_dir_up_pressed() -> void: + check_activate_direction(DIR_UP) + + +# Runs when the direction arrow is clicked +func animation_on_dir_right_pressed() -> void: + check_activate_direction(DIR_RIGHT) + + +# Runs when the direction arrow is clicked +func animation_on_dir_left_pressed() -> void: + check_activate_direction(DIR_LEFT) + + +# Runs when the direction arrow is clicked +func animation_on_dir_down_pressed() -> void: + check_activate_direction(DIR_DOWN) + + +# Runs when the direction arrow is clicked +func animation_on_dir_downright_pressed() -> void: + check_activate_direction(DIR_DOWN_RIGHT) + + +# Runs when the direction arrow is clicked +func animation_on_dir_downleft_pressed() -> void: + check_activate_direction(DIR_DOWN_LEFT) + + +# Runs when the direction arrow is clicked +func animation_on_dir_upright_pressed() -> void: + check_activate_direction(DIR_UP_RIGHT) + + +# Runs when the direction arrow is clicked +func animation_on_dir_upleft_pressed() -> void: + check_activate_direction(DIR_UP_LEFT) + + +# If the user tries to mirror an animation, ensure they're not trying to mirror an already +# mirrored direction, and that the direction they're trying to mirror has been created. +func animation_on_mirror_checkbox_toggled(button_pressed: bool) -> void: + if not has_spritesheet_been_loaded(): + get_node(GENERIC_ERROR_NODE).dialog_text = "No animation has been configured." + get_node(GENERIC_ERROR_NODE).popup_centered() + get_node(MIRROR_NODE).button_pressed = false + return + + var opp_dir = find_opposite_direction(direction_selected) + var opp_anim_name="%s_%s" % [return_current_animation_type(), opp_dir] + var metadata_array_offset: int = get_metadata_array_offset(opp_dir) + + if button_pressed: + if anim_metadata[metadata_array_offset][METADATA_IS_MIRROR]: + get_node(GENERIC_ERROR_NODE).dialog_text = \ + "You can't mirror a direction that is already mirrored." + get_node(GENERIC_ERROR_NODE).popup_centered() + get_node(MIRROR_NODE).set_pressed_no_signal(false) + return + + if anim_metadata[metadata_array_offset][METADATA_SPRITESHEET_FIRST_FRAME] == 0: + get_node(GENERIC_ERROR_NODE).dialog_text = \ + "You can't mirror an animation that hasn't been set up." + get_node(GENERIC_ERROR_NODE).popup_centered() + get_node(MIRROR_NODE).set_pressed_no_signal(false) + return + + mirror_animation(opp_dir, direction_selected) + preview_update() + else: + unmirror_animation(direction_selected) + + +# When the animation speed has been changed, update the speed and label +func controls_on_anim_speed_scroll_bar_value_changed(value: float) -> void: + if not has_spritesheet_been_loaded(): + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").value = current_animation_speed + return + + if anim_metadata[get_metadata_array_offset()][METADATA_IS_MIRROR] and not currently_changing_direction: + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").value = current_animation_speed + get_node(GENERIC_ERROR_NODE).dialog_text = "You cannot change a mirrored animation." + get_node(GENERIC_ERROR_NODE).popup_centered() + return + + current_animation_speed = int(value) + + check_if_controls_have_changed() + + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_label").text = "%s: %s FPS" % [ANIMATION_SPEED_LABEL, value] + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").sprite_frames.set_animation_speed(ANIM_IN_PROGRESS, value) + + preview_update() + + if autostore == true: + store_on_anim_store_button_pressed() + + +# When the first animation frame setting is changed, update the animation preview appropriately +func controls_on_start_frame_value_changed(value: float) -> void: + if not has_spritesheet_been_loaded(): + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = spritesheet_settings[2] + return + + if anim_metadata[get_metadata_array_offset()][METADATA_IS_MIRROR] and not currently_changing_direction: + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = spritesheet_settings[2] + get_node(GENERIC_ERROR_NODE).dialog_text = "You cannot change a mirrored animation." + get_node(GENERIC_ERROR_NODE).popup_centered() + return + spritesheet_settings[2] = get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value + if get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value > 0: + preview_show() + check_if_controls_have_changed() + draw_frame_outlines() + preview_update() + + if autostore == true: + store_on_anim_store_button_pressed() + + +# When the last animation frame setting is changed, update the animation preview appropriately +func controls_on_end_frame_value_changed(value: float) -> void: + if not has_spritesheet_been_loaded(): + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = spritesheet_settings[3] + return + + if anim_metadata[get_metadata_array_offset()][METADATA_IS_MIRROR] and not currently_changing_direction: + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = spritesheet_settings[3] + get_node(GENERIC_ERROR_NODE).dialog_text = "You cannot change a mirrored animation." + get_node(GENERIC_ERROR_NODE).popup_centered() + return + + spritesheet_settings[3] = get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value + if get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value > 0: + if get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value == 0: + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = 1 + spritesheet_settings[2] = 1 + preview_show() + check_if_controls_have_changed() + + draw_frame_outlines() + preview_update() + + if autostore == true: + store_on_anim_store_button_pressed() + + +# When the number of horizontal frames in the spritesheet setting is changed, +# update the animation preview appropriately +func controls_on_h_frames_spin_box_value_changed(value: float) -> void: + if not has_spritesheet_been_loaded(): + get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value = spritesheet_settings[0] + return + + if anim_metadata[get_metadata_array_offset()][METADATA_IS_MIRROR] and not currently_changing_direction: + get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value = spritesheet_settings[0] + get_node(GENERIC_ERROR_NODE).dialog_text = "You cannot change a mirrored animation." + get_node(GENERIC_ERROR_NODE).popup_centered() + return + + spritesheet_settings[0] = get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value + preview_show() + + check_if_controls_have_changed() + calc_sprite_size() + draw_frame_outlines() + preview_update() + + if autostore == true: + store_on_anim_store_button_pressed() + + +# When the number of vertical frames in the spritesheet setting is changed, +# update the animation preview appropriately +func controls_on_v_frames_spin_box_value_changed(value: float) -> void: + if not has_spritesheet_been_loaded(): + get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value = spritesheet_settings[1] + return + + if anim_metadata[get_metadata_array_offset()][METADATA_IS_MIRROR] and not currently_changing_direction: + get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value = spritesheet_settings[1] + get_node(GENERIC_ERROR_NODE).dialog_text = "You cannot change a mirrored animation." + get_node(GENERIC_ERROR_NODE).popup_centered() + return + + spritesheet_settings[1] = get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value + + preview_show() + + check_if_controls_have_changed() + calc_sprite_size() + draw_frame_outlines() + preview_update() + + if autostore == true: + store_on_anim_store_button_pressed() + + +# Load a spritesheet when selected in the file browser +func controls_on_FileDialog_file_selected(path: String) -> void: + get_node(NO_SPRITESHEET_NODE).visible = false + load_spritesheet(path) + + +# Check all animations have been created when the user wants to export the ESCPlayer +func spritesheet_on_export_button_pressed() -> void: + var missing_walk_animations: int = 0 + var missing_talk_animations: int = 0 + var missing_idle_animations: int = 0 + var anim_name: String = "" + var dirnames = [] + + var scene_name = "%s/%s.scn" % [get_node(CHARACTER_PATH_NODE).text, get_node(NAME_NODE).get_node("node_name").text] + + if FileAccess.file_exists(scene_name): + get_node(GENERIC_ERROR_NODE).dialog_text = \ + "Scene file '%s' already exists.\nPlease change Global_ID or path,\nor delete scene before continuing.\n" \ + % scene_name + get_node(GENERIC_ERROR_NODE).popup_centered() + return + + if get_node(DIR_COUNT_NODE).get_node("four_directions").button_pressed: + dirnames = DIR_LIST_4 + elif get_node(DIR_COUNT_NODE).get_node("eight_directions").button_pressed: + dirnames = DIR_LIST_8 + elif get_node(DIR_COUNT_NODE).get_node("two_directions").button_pressed: + dirnames = DIR_LIST_2 + else: + dirnames = DIR_LIST_1 + + for dirloop in dirnames: + anim_name = "%s_%s" % [TYPE_WALK, dirloop] + + if anim_metadata[get_metadata_array_offset(dirloop, TYPE_WALK)][METADATA_SPRITESHEET_FIRST_FRAME] == 0: + missing_walk_animations += 1 + + anim_name = "%s_%s" % [TYPE_TALK, dirloop] + + if anim_metadata[get_metadata_array_offset(dirloop, TYPE_TALK)][METADATA_SPRITESHEET_FIRST_FRAME] == 0: + missing_talk_animations += 1 + + anim_name = "%s_%s" % [TYPE_IDLE, dirloop] + + if anim_metadata[get_metadata_array_offset(dirloop, TYPE_IDLE)][METADATA_SPRITESHEET_FIRST_FRAME] == 0: + missing_idle_animations += 1 + + if missing_idle_animations + missing_talk_animations + missing_walk_animations > 0: + get_node(GENERIC_ERROR_NODE).dialog_text = \ + "One or more animations are not configured.\nPlease ensure all arrows are green for\nwalk, talk, and idle animations.\n\n" + + if missing_walk_animations: + get_node(GENERIC_ERROR_NODE).dialog_text += \ + "%s walk animations not configured.\n" % missing_walk_animations + + if missing_talk_animations: + get_node(GENERIC_ERROR_NODE).dialog_text += \ + "%s talk animations not configured.\n" % missing_talk_animations + + if missing_idle_animations: + get_node(GENERIC_ERROR_NODE).dialog_text += \ + "%s idle animations not configured." % missing_idle_animations + + get_node(GENERIC_ERROR_NODE).popup_centered() + + return + +# export_thread = Thread.new() +# export_thread.start(self, "export_player") + export_player(scene_name) + +# Update the spritesheet zoom and scrollbars +func spritesheet_on_zoom_scrollbar_value_changed(value: float) -> void: + if not has_spritesheet_been_loaded(): + return + + zoom_value = snapped(value, 0.1) + get_node(ZOOM_LABEL_NODE).text = "Zoom: %sx" % str(zoom_value) + if zoom_value > 1.0: + get_node(SCROLL_CTRL_NODE).get_node("spritesheet_sprite").custom_minimum_size = source_image.get_size() * zoom_value + get_node(SCROLL_CTRL_NODE).get_node("spritesheet_sprite").scale = Vector2.ONE + else: + get_node(SCROLL_CTRL_NODE).get_node("spritesheet_sprite").custom_minimum_size = source_image.get_size() + get_node(SCROLL_CTRL_NODE).get_node("spritesheet_sprite").scale.x = zoom_value + get_node(SCROLL_CTRL_NODE).get_node("spritesheet_sprite").scale.y = zoom_value + draw_frame_outlines() + + +# Show the file manager when the load spritesheet button is pressed +func spritesheet_on_load_spritesheet_button_pressed() -> void: + get_node(FILE_DIALOG_NODE).popup_centered() + + +# Reset zoom settings when the reset button is pushed. Also called when a new +# spritesheet is loaded. +func spritesheet_on_zoom_reset_button_pressed() -> void: + if not has_spritesheet_been_loaded(): + return + + get_node(ZOOM_SCROLL_NODE).value = 1 + + get_node(SCROLL_VBOX_NODE).get_node("spritesheet_scroll_container").scroll_horizontal = 0 + get_node(SCROLL_VBOX_NODE).get_node("spritesheet_scroll_container").scroll_vertical = 0 + + +# If the node name is changed, update the global_id to match. +# NOTE : Updating the global_id doesn't update the nodename, allowing them to be different. +func nodename_on_node_name_text_changed(new_text: String) -> void: + get_node(NAME_NODE).get_node("global_id").text = new_text + + +# If 8 directions was already selected, don't let it be unselected. +# If 4 directions was selected, unselect it. +func directions_on_eight_directions_pressed() -> void: + if not get_node(DIR_COUNT_NODE).get_node("eight_directions").button_pressed: + # Don't let them untick all boxes + get_node(DIR_COUNT_NODE).get_node("eight_directions").button_pressed = true + + for loop in ["four_directions", "two_directions", "one_direction"]: + get_node(DIR_COUNT_NODE).get_node(loop).button_pressed = false + reset_arrow_colours() + + +# If 4 directions was already selected, don't let it be unselected. +# If previously selected direction is now invalid, change it to a valid one. +func directions_on_four_directions_pressed() -> void: + if not get_node(DIR_COUNT_NODE).get_node("four_directions").button_pressed: + # Don't let them untick all boxes + get_node(DIR_COUNT_NODE).get_node("four_directions").button_pressed = true + else: + # Current direction is illegal + for loop in ["eight_directions", "two_directions", "one_direction"]: + get_node(DIR_COUNT_NODE).get_node(loop).button_pressed = false + if not direction_selected in DIR_LIST_4: + direction_selected = DIR_UP + activate_direction(DIR_UP) + reset_arrow_colours() + + +# If 2 directions was already selected, don't let it be unselected. +# If previously selected direction is now invalid, change it to a valid one. +func directions_on_two_directions_pressed() -> void: + if not get_node(DIR_COUNT_NODE).get_node("two_directions").button_pressed: + # Don't let them untick all boxes + get_node(DIR_COUNT_NODE).get_node("two_directions").button_pressed = true + else: + for loop in ["eight_directions", "four_directions", "one_direction"]: + get_node(DIR_COUNT_NODE).get_node(loop).button_pressed = false + # Current direction is illegal + if not direction_selected in DIR_LIST_2: + direction_selected = DIR_RIGHT + activate_direction(DIR_RIGHT) + reset_arrow_colours() + + +# If 1 direction was already selected, don't let it be unselected. +# If previously selected direction is now invalid, change it to a valid one. +func directions_on_one_direction_pressed() -> void: + if not get_node(DIR_COUNT_NODE).get_node("one_direction").button_pressed: + # Don't let them untick all boxes + get_node(DIR_COUNT_NODE).get_node("one_direction").button_pressed = true + else: + for loop in ["eight_directions", "four_directions", "two_directions"]: + get_node(DIR_COUNT_NODE).get_node(loop).button_pressed = false + # Current direction is illegal + if not direction_selected in DIR_LIST_1: + direction_selected = DIR_DOWN + activate_direction(DIR_DOWN) + reset_arrow_colours() + +# Returns the currently selected animation type +func return_current_animation_type() -> String: + var animation_type: String = "" + + if get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed: + animation_type = TYPE_WALK + elif get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed: + animation_type = TYPE_TALK + elif get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed: + animation_type = TYPE_IDLE + + assert(not animation_type.is_empty(), "No animation type selected.") + + return animation_type + + +# Runs whenever a direction arrow is clicked. If the store button is visible (i.e. the settings +# for the sprite frames have changed since they were last stored) the selected direction isn't +# changed and a confirmation window is shown instead. +func check_activate_direction(direction) -> void: + direction_requested = direction + + if get_node(STORE_ANIM_NODE).visible: + get_node(ARROWS_NODE).get_node("Container_%s" % direction).get_node("set_dir_%s" % direction).button_pressed = false + get_node(ARROWS_NODE).get_node("Container_%s" % direction).get_node("unset_dir_%s" % direction).button_pressed = false + $InformationWindows/unstored_changes_window.popup_centered() + else: + activate_direction(direction) + + +# Change the selected direction. This clears the selected direction arrow and mirror settings. +# If the selected direction has an animation, it will be displayed, if not, the "no animation" +# graphic will be displayed in the preview window. +# Spritesheet control values are set based on the direction chosen (if it had a previous animation) +# If it used a different spritesheet, that will be loaded. +func activate_direction(direction) -> void: + var anim_type = return_current_animation_type() + var arrows = get_tree().get_nodes_in_group("direction_buttons") + var anim_name = "%s_%s" % [anim_type, direction] + + currently_changing_direction = true + direction_selected = direction + + for arrow in arrows: + arrow.button_pressed = false + + if direction == DIR_UP or direction == DIR_DOWN: + get_node(MIRROR_NODE).visible = false + else: + get_node(MIRROR_NODE).visible = true + + get_node(MIRROR_NODE).set_pressed_no_signal(false) + + # If no animation has been created yet for this direction + if anim_metadata[get_metadata_array_offset()][METADATA_SPRITESHEET_FIRST_FRAME] == 0: + get_node(ARROWS_NODE).get_node("Container_%s" % direction).get_node("unset_dir_%s" % direction).button_pressed = true + spritesheet_settings[2] = 0 + spritesheet_settings[3] = 0 + + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = 0 + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = 0 + preview_hide() + else: + get_node(ARROWS_NODE).get_node("Container_%s" % direction).get_node("set_dir_%s" % direction).button_pressed = true + + var metadata = anim_metadata[get_metadata_array_offset()] + + assert(metadata[METADATA_ANIM_NAME] == anim_name, \ + "Anim %s expected in metadata array. Found %s" % [anim_name, metadata[METADATA_ANIM_NAME]]) + + if metadata[METADATA_SPRITESHEET_SOURCE_FILE] != get_node(CURRENT_SHEET_NODE).text: + load_spritesheet(metadata[METADATA_SPRITESHEET_SOURCE_FILE]) + + # Disconnect the signals so if we're changing to a mirrored direction it doesn't complain + # when all the settings update +# disconnect_selector_signals() + get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value = metadata[METADATA_SPRITESHEET_FRAMES_HORIZ] + get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value = metadata[METADATA_SPRITESHEET_FRAMES_VERT] + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = metadata[METADATA_SPRITESHEET_FIRST_FRAME] + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = metadata[METADATA_SPRITESHEET_LAST_FRAME] + get_node(ANIM_CONTROLS_NODE).get_node("anim_speed_scroll_bar").value = metadata[METADATA_SPEED] + get_node(MIRROR_NODE).set_pressed_no_signal(metadata[METADATA_IS_MIRROR]) +# connect_selector_signals() + preview_update() + + # Restart animation otherwise it will first complete all the frames before changing to the new animation + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").stop() + get_node(PREVIEW_NODE).get_node("anim_preview_sprite").play() + currently_changing_direction = false + +# Store the metadata for the animation changes for the current direction +func store_on_anim_store_button_pressed() -> void: + get_node(STORE_ANIM_NODE).visible = false + + var anim_type = return_current_animation_type() + store_animation("%s_%s" % [anim_type, direction_selected]) + + reset_arrow_colours() + + +# Based on the type of animation and direction, find its array position in the metadata array. +func get_metadata_array_offset(dir_to_retrieve = "default", anim_type = "default") -> int: + var offset: int = 0 + + if anim_type == "default": + anim_type = return_current_animation_type() + + if anim_type == TYPE_TALK: + offset = 8 + elif anim_type == TYPE_IDLE: + offset = 16 + + if dir_to_retrieve == "default": + dir_to_retrieve = direction_selected + + var dir_offset: int = DIR_LIST_8.find(dir_to_retrieve) + + assert(dir_offset > -1, "Could not find direction in list. This should never happen.") + + return offset + dir_offset + + +# Using both set and unset buttons (instead of changing the texture to a different colour) as +# updating the direction arrow sprite was causing issues due to Godot storing the +# sprite by reference rather than by value. It was easier to duplicate the sprites/buttons. +func reset_arrow_colours() -> void: + var arrows = get_tree().get_nodes_in_group("direction_buttons") + for arrow in arrows: + arrow.visible = false + + get_node(ARROWS_NODE).get_node("Container_up").get_node("ColorRectSpacer").visible = false + get_node(ARROWS_NODE).get_node("Container_left").get_node("ColorRectSpacer").visible = false + get_node(ARROWS_NODE).get_node("Container_down").get_node("ColorRectSpacer").visible = false + var dir_list=DIR_LIST_8 + if get_node(DIR_COUNT_NODE).get_node("four_directions").button_pressed: + dir_list=DIR_LIST_4 + if not direction_selected in DIR_LIST_4: + direction_selected = DIR_UP +# get_node(ARROWS_NODE).get_node("Container_up").get_node("ColorRectSpacer").visible = true + if get_node(DIR_COUNT_NODE).get_node("two_directions").button_pressed: + dir_list=DIR_LIST_2 + if not direction_selected in DIR_LIST_2: + direction_selected = DIR_RIGHT + get_node(ARROWS_NODE).get_node("Container_up").get_node("ColorRectSpacer").visible = true + get_node(ARROWS_NODE).get_node("Container_down").get_node("ColorRectSpacer").visible = true + if get_node(DIR_COUNT_NODE).get_node("one_direction").button_pressed: + dir_list=DIR_LIST_1 + if not direction_selected in DIR_LIST_1: + direction_selected = DIR_DOWN + get_node(ARROWS_NODE).get_node("Container_up").get_node("ColorRectSpacer").visible = true + get_node(ARROWS_NODE).get_node("Container_left").get_node("ColorRectSpacer").visible = true + + + for dir in dir_list: + if anim_metadata[get_metadata_array_offset(dir)][METADATA_SPRITESHEET_FIRST_FRAME] > 0: + get_node(ARROWS_NODE).get_node("Container_%s" % dir).get_node("set_dir_%s" % dir).visible = true + get_node(ARROWS_NODE).get_node("Container_%s" % dir).get_node("unset_dir_%s" % dir).visible = false + else: + get_node(ARROWS_NODE).get_node("Container_%s" % dir).get_node("set_dir_%s" % dir).visible = false + get_node(ARROWS_NODE).get_node("Container_%s" % dir).get_node("unset_dir_%s" % dir).visible = true + + # This works when you change between animation types + # eg. walk and talk - to ensure that the arrow will get changed back from selected with stored + # animation to selected with unstored animation if needs be. It also resets the preview. + activate_direction(direction_selected) + + +# If the user tries to change direction without commiting first, a window will appear to +# confirm what they want to do. +# The button associated with this function chooses to save the changes, then will update +# the interface to select the new direction. +func unstored_warning_on_commit_button_pressed() -> void: + get_node(UNSTORED_CHANGE_NODE).visible = false + get_node(STORE_ANIM_NODE).visible = false + + var anim_type = return_current_animation_type() + store_animation("%s_%s" % [anim_type, direction_selected]) + + reset_arrow_colours() + + activate_direction(direction_requested) + + +# If the user tries to change direction without commiting first, a window will appear to +# confirm what they want to do. +# The button associated with this function chooses to lose the changes, then will update +# the interface to select the new direction. +func unstored_warning_on_lose_button_pressed() -> void: + get_node(UNSTORED_CHANGE_NODE).visible = false + get_node(STORE_ANIM_NODE).visible = false + + activate_direction(direction_requested) + + +# If the user tries to change direction without commiting first, a window will appear to +# confirm what they want to do. +# The button associated with this function chooses to cancel the request to change direction +# and let the user continue to edit the current animation. +func unstored_warning_on_cancel_button_pressed() -> void: + get_node(UNSTORED_CHANGE_NODE).visible = false + + +# Returns the opposite direction for mirroring animations +func find_opposite_direction(direction:String) -> String: + var opposite_dir: String = "" + + match direction: + DIR_UP_RIGHT: + opposite_dir = DIR_UP_LEFT + DIR_RIGHT: + opposite_dir = DIR_LEFT + DIR_DOWN_RIGHT: + opposite_dir = DIR_DOWN_LEFT + DIR_DOWN_LEFT: + opposite_dir = DIR_DOWN_RIGHT + DIR_LEFT: + opposite_dir = DIR_RIGHT + DIR_UP_LEFT: + opposite_dir = DIR_UP_RIGHT + + assert(not opposite_dir.is_empty(), "This should never happen : direction = %s" % direction) + return opposite_dir + + +# Creates an ESCPlayer node based on the settings configured in the wizard. +# It will save it as a scene (named based on the name provided in the GUI text box) +# and open it in the Godot editor - which is why this utility has to run as a plugin. +# This will also create an ESCDialogue position, and a collision box. The collision box will +# be sized based on the widest and tallest frames encountered during export (note that the +# widest/tallest frame settings do not necessarily come from the same animation frame but are +# from all the animation frames. +func export_player(scene_name) -> void: + var num_directions + var start_angle_array + var angle_size + var dirnames + + var plugin_reference = get_node("..").plugin_reference + + disconnect_selector_signals() + get_node(EXPORT_PROGRESS_NODE).popup_centered() + get_node(EXPORT_PROGRESS_NODE).get_node("progress_bar").value = 0 + get_node(EXPORT_PROGRESS_NODE).get_node("progress_bar").visible = true + get_node(EXPORT_PROGRESS_NODE).get_node("progress_label").visible = true + + if get_node(DIR_COUNT_NODE).get_node("eight_directions").button_pressed: + num_directions = 8 + if get_node(DIR_COUNT_NODE).get_node("four_directions").button_pressed: + num_directions = 4 + if get_node(DIR_COUNT_NODE).get_node("two_directions").button_pressed: + num_directions = 2 + else: + num_directions = 1 + + var new_character + # NPCs can't be ESCPlayers or the player won't walk up to them when + # you interact with them + if get_node(CHAR_TYPE_NODE).get_node("npc").button_pressed: + new_character = ESCItem.new() + else: + new_character = ESCPlayer.new() + new_character.selectable = true + new_character.name = get_node(NAME_NODE).get_node("node_name").text + + if get_node(NAME_NODE).get_node("global_id").text.is_empty(): + new_character.global_id = new_character.name + else: + new_character.global_id = get_node(NAME_NODE).get_node("global_id").text + + new_character.tooltip_name = new_character.name + + new_character.default_action = "look" + + var animations_resource = ESCAnimationResource.new() + + # This is necessary to avoid a Godot bug when appending to one array + # appends to all arrays in the same class (possibly for resources only). + animations_resource.dir_angles = [] + animations_resource.directions = [] + animations_resource.idles = [] + animations_resource.speaks = [] + + if get_node(DIR_COUNT_NODE).get_node("four_directions").button_pressed: + num_directions = 4 + start_angle_array = [315, 45, 135, 225] + angle_size = 90 + dirnames = DIR_LIST_4 + elif get_node(DIR_COUNT_NODE).get_node("eight_directions").button_pressed: + num_directions = 8 + start_angle_array = [337, 22, 67, 112, 157, 202, 247, 292] + angle_size = 45 + dirnames = DIR_LIST_8 + elif get_node(DIR_COUNT_NODE).get_node("two_directions").button_pressed: + num_directions = 2 + start_angle_array = [0, 180] + angle_size = 180 + dirnames = DIR_LIST_2 + else: + num_directions = 1 + start_angle_array = [0] + angle_size = 360 + dirnames = DIR_LIST_1 + + for loop in range(num_directions): + # Need to create new objects here each time in order to avoid having multiple references + # to the same objects. + var dir_angle = ESCDirectionAngle.new() + var anim_details: ESCAnimationName + + dir_angle.angle_start = start_angle_array[loop] + dir_angle.angle_size = angle_size + animations_resource.dir_angles.append(dir_angle) + + anim_details = _create_esc_animation(TYPE_WALK, dirnames[loop]) + animations_resource.directions.append(anim_details) + + anim_details = _create_esc_animation(TYPE_TALK, dirnames[loop]) + animations_resource.speaks.append(anim_details) + + anim_details = _create_esc_animation(TYPE_IDLE, dirnames[loop]) + animations_resource.idles.append(anim_details) + +# var largest_sprite = export_generate_animations(new_character, num_directions) + export_largest_sprite = Vector2.ONE + + # TODO: This call updates export_largest_sprite, and there is likely a better + # way to accomplish this. + await export_generate_animations(new_character, num_directions) + + # Add Collision shape to the ESCPlayer + var rectangle_shape = RectangleShape2D.new() + var collision_shape = CollisionShape2D.new() + progress_bar_update("Creating collision shape") + await get_tree().process_frame + + collision_shape.shape = rectangle_shape + collision_shape.shape.size = export_largest_sprite + collision_shape.position.y = -(export_largest_sprite.y / 2) + + new_character.add_child(collision_shape) + progress_bar_update("Setting up dialog position") + await get_tree().process_frame + + # Add Dialog Position to the ESCPlayer + var dialog_position = ESCDialogLocation.new() + dialog_position.name = "ESCDialogLocation" + dialog_position.position.y = -(export_largest_sprite.y * 1.2) + new_character.add_child(dialog_position) + + if get_node(CHAR_TYPE_NODE).get_node("npc").button_pressed: + # Add Interaction Position to an NPC + var interaction_position = ESCLocation.new() + interaction_position.name = "interact_position" + interaction_position.position.y = +(export_largest_sprite.y * 1.2) + new_character.add_child(interaction_position) + interaction_position.set_owner(new_character) + + progress_bar_update("Configuring animations") + await get_tree().process_frame + # Make it so all the nodes can be seen in the scene tree + new_character.animations = animations_resource + progress_bar_update("Adding child to scene tree") + await get_tree().process_frame + get_tree().edited_scene_root.add_child(new_character) + new_character.set_owner(get_tree().edited_scene_root) + + # Making the owner "new_character" rather than "get_tree().edited_scene_root" means that + # when saving as a packed scene, the child nodes get saved under the parent (as the parent + # must own the child nodes). If the owner is not the scene root though, the nodes will NOT + # show up in the scene tree. + collision_shape.set_owner(new_character) + dialog_position.set_owner(new_character) + + # Export scene + var packed_scene = PackedScene.new() + + progress_bar_update("Packing scene - this might take up to 30 seconds") + await get_tree().process_frame + packed_scene.pack(get_tree().edited_scene_root.get_node(NodePath(new_character.name))) + + progress_bar_update("Resource saving - this might take up to 30 seconds") + await get_tree().process_frame + # Flag suggestions from https://godotengine.org/qa/50437/how-to-turn-a-node-into-a-packedscene-via-gdscript + + # Make sure the path exists + if not DirAccess.dir_exists_absolute(scene_name.get_base_dir()): + DirAccess.make_dir_recursive_absolute(scene_name.get_base_dir()) + + ResourceSaver.save(packed_scene, scene_name, ResourceSaver.FLAG_CHANGE_PATH|ResourceSaver.FLAG_REPLACE_SUBRESOURCE_PATHS|ResourceSaver.FLAG_COMPRESS) + + progress_bar_update("Releasing resources - this might take up to 30 seconds") + await get_tree().process_frame + new_character.queue_free() + + get_tree().edited_scene_root.get_node(NodePath(new_character.name)).queue_free() + plugin_reference.open_scene(scene_name) + plugin_reference._make_visible(false) + get_node(EXPORT_PROGRESS_NODE).hide() + get_node(EXPORT_COMPLETE_NODE).popup_centered() + + connect_selector_signals() + + +# Updates the text in the export window so the user knows what's happening +func progress_bar_update(message, bar_increase_amount = 1) -> void: + get_node(PROGRESS_LABEL_NODE).text = message + get_node(EXPORT_PROGRESS_NODE).get_node("progress_bar").value += bar_increase_amount + + +# When exporting the ESCPlayer, this function loads the relevant spritesheets based on the +# animation metadata, and copies the frames to the relevant animations within the animatedsprite +# attached to the ESCPlayer. +#func export_generate_animations(character_node, num_directions) -> Vector2: +func export_generate_animations(character_node, num_directions) -> void: + # This variable is used instead of running this function in a thread as I hit this issue + # when I tried to thread this - https://github.com/godotengine/godot/issues/38058 + var display_refresh_timer:int = Time.get_ticks_msec() + var direction_names + var loaded_spritesheet: String + var largest_frame_dimensions: Vector2 = Vector2.ZERO + var sprite_frames = SpriteFrames.new() + var default_anim_length = 0 + var default_anim_speed = 1 + var frame_counter: int = 0 + + match num_directions: + 1: direction_names = DIR_LIST_1 + 2: direction_names = DIR_LIST_2 + 4: direction_names = DIR_LIST_4 + 8: direction_names = DIR_LIST_8 + + for animtype in [TYPE_WALK, TYPE_TALK, TYPE_IDLE]: + for anim_dir in direction_names: + # Using this in place of threads due to the above mentioned issue so that the + # UI continues to update while the export is running + var current_ticks = Time.get_ticks_msec() + if current_ticks - display_refresh_timer > 30: + await get_tree().process_frame + + display_refresh_timer = current_ticks + + if num_directions == 4: + progress_bar_update("Processing "+str(animtype)+" "+str(anim_dir),2) + else: + progress_bar_update("Processing "+str(animtype)+" "+str(anim_dir),1) + + var anim_name = "%s_%s" % [animtype, anim_dir] + var metadata = anim_metadata[get_metadata_array_offset(anim_dir, animtype)] + + if metadata[METADATA_IS_MIRROR]: + continue + + var rect_location + sprite_frames.add_animation(anim_name) + + if metadata[METADATA_SPRITESHEET_SOURCE_FILE] != loaded_spritesheet: + load_spritesheet(metadata[METADATA_SPRITESHEET_SOURCE_FILE], true, get_metadata_array_offset(anim_dir, animtype)) + + loaded_spritesheet = metadata[METADATA_SPRITESHEET_SOURCE_FILE] + calc_sprite_size() + if (frame_size.x / 2) > largest_frame_dimensions.x: + largest_frame_dimensions.x = frame_size.x + + if (frame_size.y / 2) > largest_frame_dimensions.y: + largest_frame_dimensions.y = frame_size.y + + if animtype == TYPE_IDLE and anim_dir == "down": + default_anim_length = metadata[METADATA_SPRITESHEET_LAST_FRAME] - metadata[METADATA_SPRITESHEET_FIRST_FRAME] + 1 + default_anim_speed = metadata[METADATA_SPEED] + + var frame_duration: float = 1.0 + var frame_being_copied: Image = Image.create_empty(frame_size.x, frame_size.y, false, source_image.get_format()) + + for loop in range(metadata[METADATA_SPRITESHEET_LAST_FRAME] - metadata[METADATA_SPRITESHEET_FIRST_FRAME] + 1): + rect_location = calc_frame_coords(metadata[METADATA_SPRITESHEET_FIRST_FRAME] + loop) + frame_being_copied.blit_rect(source_image, Rect2(rect_location, Vector2(frame_size.x, frame_size.y)), Vector2(0, 0)) + var texture: ImageTexture = ImageTexture.create_from_image(frame_being_copied) + + # Remove "filter" flag so it's pixel perfect + #texture.set_flags(2) + sprite_frames.add_frame(anim_name, texture, frame_duration, frame_counter) + sprite_frames.set_animation_speed(anim_name, metadata[METADATA_SPEED]) + frame_counter += 1 + + # Generate default animation. This is used by the object manager to set the + # state when the object is registered. If there's no current state, the + # default animation will be used. + var frame_duration: float = 1.0 + + for loop in range(default_anim_length): + var texture: ImageTexture = sprite_frames.get_frame_texture("idle_down", loop) + + # Remove "filter" flag so it's pixel perfect + #texture.set_flags(2) + sprite_frames.add_frame("default", texture, frame_duration, loop) + sprite_frames.set_animation_speed("default", default_anim_speed) + + var animated_sprite = AnimatedSprite2D.new() + + progress_bar_update("Adding sprite frames to node") + animated_sprite.sprite_frames = sprite_frames + if num_directions == 2: + animated_sprite.animation = "%s_%s" % [TYPE_IDLE, DIR_RIGHT] + else: + animated_sprite.animation = "%s_%s" % [TYPE_IDLE, DIR_DOWN] + animated_sprite.position.y = -(largest_frame_dimensions.y / 2) # Place feet at (0,0) + animated_sprite.animation = "default" + animated_sprite.play() + + character_node.add_child(animated_sprite) + # Making the owner "character_node" rather than "get_tree().edited_scene_root" means that + # when saving as a packed scene, the child nodes get saved under the parent (as the parent + # must own the child nodes). If the owner is not the scene root though, the nodes will NOT + # show up in the scene tree. + animated_sprite.set_owner(character_node) + #return largest_frame_dimensions + export_largest_sprite = largest_frame_dimensions + + +# Open the help window +func spritesheet_on_help_button_pressed() -> void: + $InformationWindows/help_window.popup_centered() + $InformationWindows/help_window.show_page() + + +func spritesheet_on_reset_button_pressed() -> void: + $InformationWindows/ConfirmationDialog.dialog_text = "WARNING!\n\n" + \ + "If you continue you will lose the current character." + $InformationWindows/ConfirmationDialog.popup_centered() + + +func spritesheet_on_reset_confirmed() -> void: + spritesheet_settings = [1, 1, 0, 0] + source_image = null + get_node(SCROLL_CTRL_NODE).get_node("spritesheet_sprite").texture = null + create_empty_animations() + character_creator_reset() + + +func spritesheet_on_MainMenuConfirmation_confirmed() -> void: + get_node("../Menu").visible = true + get_node(".").visible = false + + +func spritesheet_on_main_menu_button_up() -> void: + $InformationWindows/MainMenuConfirmation.popup_centered() + + +func load_settings() -> void: + var file_path = "res://" + + if FileAccess.file_exists(CONFIG_FILE): + var file: FileAccess = FileAccess.open(CONFIG_FILE, FileAccess.READ) + file_path = file.get_pascal_string() + file.close() + + get_node(CHARACTER_PATH_NODE).text = file_path + + +# Creates and returns an ESCAnimationName for use by ESCAnimationResource +# +# #### Parameters +# +# - type: One of TYPE_WALK, TYPE_TALK, TYPE_IDLE (these are consts defined at the top of this script) +# - dir_name: One of DIR_LIST_8's or DIR_LIST_4's entries (these are consts defined at the top of this script) +# +# **Returns** a valid ESCAnimationName object. +func _create_esc_animation(type: String, dir_name: String) -> ESCAnimationName: + var anim_details = ESCAnimationName.new() + + anim_details.animation = "%s_%s" % [type, dir_name] + + if anim_metadata[get_metadata_array_offset(dir_name, type)][METADATA_IS_MIRROR]: + anim_details.mirrored = true + anim_details.animation = "%s_%s" % [type, find_opposite_direction(dir_name)] + else: + anim_details.mirrored = false + return anim_details + + +func _on_character_path_change_button_pressed() -> void: + get_node(CHARACTER_FILE_NODE).popup_centered() + + +func _on_CharacterPathFileDialog_dir_selected(dir: String) -> void: + get_node(CHARACTER_PATH_NODE).text = dir + var file: FileAccess = FileAccess.open(CONFIG_FILE, FileAccess.WRITE) + file.store_pascal_string(dir) + file.close() + + +# Mouse clicks inside the spritesheet +func _on_control_gui_input(event: InputEvent) -> void: + var clicked_tile: Vector2 + + if event.is_pressed(): + var num_horiz_frames = get_node(ANIM_CONTROLS_NODE).get_node("h_frames_spin_box").value + var num_vert_frames = get_node(ANIM_CONTROLS_NODE).get_node("v_frames_spin_box").value + + clicked_tile.x = int(event.position.x / (frame_size.x * zoom_value)) + if clicked_tile.x >= num_horiz_frames: + return + clicked_tile.y = int(event.position.y / (frame_size.y * zoom_value)) + if clicked_tile.y >= num_vert_frames: + return + + var absolute_frame = ((clicked_tile.y * num_horiz_frames) + clicked_tile.x) + 1 + + if event.button_index == MOUSE_BUTTON_LEFT: + get_node(ANIM_CONTROLS_NODE).get_node("start_frame").value = absolute_frame + if event.button_index == MOUSE_BUTTON_RIGHT: + get_node(ANIM_CONTROLS_NODE).get_node("end_frame").value = absolute_frame + + +# If the user tries to change animation type without commiting first, a window will appear to +# confirm what they want to do. +# The button associated with this function chooses to save the changes, then will update +# the interface to select the new direction. +func unstored_animchange_warning_on_commit_button_pressed() -> void: + get_node(UNSTORED_ANIMTYPE_CHANGE_NODE).visible = false + get_node(STORE_ANIM_NODE).visible = false + var anim_type = return_current_animation_type() + store_animation("%s_%s" % [anim_type, direction_selected]) + change_animation_type(animation_type_requested) + reset_arrow_colours() + +# If the user tries to change animation type without commiting first, a window will appear to +# confirm what they want to do. +# The button associated with this function chooses to lose the changes, then will update +# the interface to select the new direction. +func unstored_animchange_warning_on_lose_button_pressed() -> void: + get_node(UNSTORED_ANIMTYPE_CHANGE_NODE).visible = false + get_node(STORE_ANIM_NODE).visible = false + change_animation_type(animation_type_requested) + reset_arrow_colours() + + +# If the user tries to change animation type without commiting first, a window will appear to +# confirm what they want to do. +# The button associated with this function chooses to cancel the request to change direction +# and let the user continue to edit the current animation. +func unstored_animchange_warning_on_cancel_button_pressed() -> void: + get_node(UNSTORED_ANIMTYPE_CHANGE_NODE).visible = false + + +func change_animation_type(anim_type) -> void: + if anim_type == "walk": + get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed = true + get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed = false + get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed = false + animation_type_selected = "walk" + elif anim_type == "talk": + get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed = true + get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed = false + get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed = false + animation_type_selected = "talk" + else: # idle + get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed = true + get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed = false + get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed = false + animation_type_selected = "idle" + reset_arrow_colours() + + +# If the walk button is selected, unselect the other buttons. +# If this option was already the selected option, reselect it rather than letting the +# user disable it (which would mean that none of walk/talk/idle were selected. +func animation_on_walk_checkbox_pressed() -> void: + # Don't let the checkbox be unselected if it's currently selected + if animation_type_selected == "walk": + get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed = true + return + if get_node(STORE_ANIM_NODE).visible: + # Reset the buttons back to how they were + get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed = false + if animation_type_selected == "talk": + get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed = true + if animation_type_selected == "idle": + get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed = true + animation_type_requested="walk" + get_node(UNSTORED_ANIMTYPE_CHANGE_NODE).popup_centered() + else: + change_animation_type("walk") + + +# If the talk button is selected, unselect the other buttons. +# If this option was already the selected option, reselect it rather than letting the +# user disable it (which would mean that none of walk/talk/idle were selected. +func animation_on_talk_checkbox_pressed() -> void: + # Don't let the checkbox be unselected if it's currently selected + if animation_type_selected == "talk": + get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed = true + return + if get_node(STORE_ANIM_NODE).visible: + # Reset the buttons back to how they were + get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed = false + if animation_type_selected == "idle": + get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed = true + if animation_type_selected == "walk": + get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed = true + animation_type_requested="talk" + get_node(UNSTORED_ANIMTYPE_CHANGE_NODE).popup_centered() + else: + change_animation_type("talk") + + +# If the idle button is selected, unselect the other buttons. +# If this option was already the selected option, reselect it rather than letting the +# user disable it (which would mean that none of walk/talk/idle were selected. +func animation_on_idle_checkbox_pressed() -> void: + # Don't let the checkbox be unselected if it's currently selected + if animation_type_selected == "idle": + get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed = true + return + if get_node(STORE_ANIM_NODE).visible: + # Reset the buttons back to how they were + get_node(ANIM_TYPE_NODE).get_node("idle_checkbox").button_pressed = false + if animation_type_selected == "talk": + get_node(ANIM_TYPE_NODE).get_node("talk_checkbox").button_pressed = true + if animation_type_selected == "walk": + get_node(ANIM_TYPE_NODE).get_node("walk_checkbox").button_pressed = true + animation_type_requested="idle" + get_node(UNSTORED_ANIMTYPE_CHANGE_NODE).popup_centered() + else: + change_animation_type("idle") + + +# When Auto storage checkbox is checked +func _on_AutoStoreCheckBox_toggled(button_pressed: bool) -> void: + autostore = button_pressed + + +# When player checkbox selected +func _on_player_pressed(): +# If player button was already selected, don't let it be unselected. + get_node(CHAR_TYPE_NODE).get_node("player").button_pressed = true + get_node(CHAR_TYPE_NODE).get_node("npc").button_pressed = false + + +# When NPC checkbox selected +func _on_npc_pressed(): +# If npc button was already selected, don't let it be unselected. + get_node(CHAR_TYPE_NODE).get_node("npc").button_pressed = true + get_node(CHAR_TYPE_NODE).get_node("player").button_pressed = false diff --git a/addons/escoria-wizard/CharacterCreator.gd.uid b/addons/escoria-wizard/CharacterCreator.gd.uid new file mode 100644 index 0000000..38158ab --- /dev/null +++ b/addons/escoria-wizard/CharacterCreator.gd.uid @@ -0,0 +1 @@ +uid://bwbxhkbjuggxq diff --git a/addons/escoria-wizard/CharacterCreator.tscn b/addons/escoria-wizard/CharacterCreator.tscn new file mode 100644 index 0000000..23d41c8 --- /dev/null +++ b/addons/escoria-wizard/CharacterCreator.tscn @@ -0,0 +1,1179 @@ +[gd_scene load_steps=40 format=3 uid="uid://cmd72pi6txw2v"] + +[ext_resource type="Texture2D" uid="uid://4e5o0jygjpmv" path="res://addons/escoria-wizard/graphics/darkgreenarrow_down.png" id="1"] +[ext_resource type="Texture2D" uid="uid://bhtllcrcfgi2l" path="res://addons/escoria-wizard/graphics/greyarrow_r.png" id="2"] +[ext_resource type="Texture2D" uid="uid://bf8hvhitupl4x" path="res://addons/escoria-wizard/graphics/darkgreenarrow_right.png" id="3"] +[ext_resource type="Texture2D" uid="uid://c108mcii2pnd2" path="res://addons/escoria-wizard/graphics/greenarrow_right.png" id="4"] +[ext_resource type="Texture2D" uid="uid://c56jk51hra2ad" path="res://addons/escoria-wizard/graphics/greenarrow_down.png" id="5"] +[ext_resource type="Texture2D" uid="uid://dd1kynxpr5kp7" path="res://addons/escoria-wizard/graphics/greyarrow_ul.png" id="6"] +[ext_resource type="Texture2D" uid="uid://db0x28tf68ra4" path="res://addons/escoria-wizard/graphics/greyarrow_dl.png" id="7"] +[ext_resource type="Texture2D" uid="uid://cta81hch2hfbb" path="res://addons/escoria-wizard/graphics/darkgreyarrow_d.png" id="8"] +[ext_resource type="Texture2D" uid="uid://bm8l75biydrpy" path="res://addons/escoria-wizard/graphics/no_animation.png" id="9"] +[ext_resource type="Texture2D" uid="uid://cvbadwfv44b31" path="res://addons/escoria-wizard/graphics/greenarrow_downleft.png" id="10"] +[ext_resource type="Texture2D" uid="uid://c66yr3xxyocgf" path="res://addons/escoria-wizard/graphics/darkgreenarrow_downleft.png" id="11"] +[ext_resource type="Texture2D" uid="uid://etvk6ieq08d8" path="res://addons/escoria-wizard/graphics/darkgreenarrow_left.png" id="12"] +[ext_resource type="Texture2D" uid="uid://r35l3buu5y1c" path="res://addons/escoria-wizard/graphics/greyarrow_l.png" id="13"] +[ext_resource type="Texture2D" uid="uid://bkhd38cvci0o6" path="res://addons/escoria-wizard/graphics/darkgreyarrow_ur.png" id="14"] +[ext_resource type="Texture2D" uid="uid://v431ykg6an0r" path="res://addons/escoria-wizard/graphics/darkgreyarrow_ul.png" id="15"] +[ext_resource type="Texture2D" uid="uid://cbam52s6amdah" path="res://addons/escoria-wizard/graphics/darkgreenarrow_upright.png" id="16"] +[ext_resource type="Texture2D" uid="uid://cadthv31wt2lr" path="res://addons/escoria-wizard/graphics/greyarrow_ur.png" id="17"] +[ext_resource type="Texture2D" uid="uid://bu2yu64fio8re" path="res://addons/escoria-wizard/graphics/greyarrow_u.png" id="18"] +[ext_resource type="Texture2D" uid="uid://cfewua8ben82l" path="res://addons/escoria-wizard/graphics/darkgreyarrow_r.png" id="19"] +[ext_resource type="Texture2D" uid="uid://bng28oph8rshy" path="res://addons/escoria-wizard/graphics/darkgreyarrow_u.png" id="20"] +[ext_resource type="Texture2D" uid="uid://dknfjhmw4ulgx" path="res://addons/escoria-wizard/graphics/darkgreyarrow_l.png" id="21"] +[ext_resource type="Texture2D" uid="uid://cooshd11j6mme" path="res://addons/escoria-wizard/graphics/greyarrow_dr.png" id="22"] +[ext_resource type="Texture2D" uid="uid://yhgaqrufthbd" path="res://addons/escoria-wizard/graphics/greenarrow_up.png" id="23"] +[ext_resource type="Texture2D" uid="uid://blgnjkqah3tux" path="res://addons/escoria-wizard/graphics/greenarrow_upright.png" id="24"] +[ext_resource type="Texture2D" uid="uid://bag4myopmjamd" path="res://addons/escoria-wizard/graphics/darkgreenarrow_downright.png" id="25"] +[ext_resource type="Texture2D" uid="uid://dkv2avtnxqov1" path="res://addons/escoria-wizard/graphics/greenarrow_left.png" id="26"] +[ext_resource type="Texture2D" uid="uid://b4chlwkmru1aj" path="res://addons/escoria-wizard/graphics/darkgreenarrow_upleft.png" id="27"] +[ext_resource type="Texture2D" uid="uid://dfk2rb1g2b4gl" path="res://addons/escoria-wizard/graphics/darkgreenarrow_up.png" id="28"] +[ext_resource type="Texture2D" uid="uid://bnxd0blo12sdc" path="res://addons/escoria-wizard/graphics/no_spritesheet.png" id="29"] +[ext_resource type="Texture2D" uid="uid://c7hp4rcgl7v5k" path="res://addons/escoria-wizard/graphics/greenarrow_upleft.png" id="30"] +[ext_resource type="Texture2D" uid="uid://bxfeqwi1hmjxl" path="res://addons/escoria-wizard/graphics/greenarrow_downright.png" id="31"] +[ext_resource type="Texture2D" uid="uid://c3ohia3wlf1y7" path="res://addons/escoria-wizard/graphics/icon.png" id="32"] +[ext_resource type="Texture2D" uid="uid://co12gknvv3u8u" path="res://addons/escoria-wizard/graphics/greyarrow_d.png" id="33"] +[ext_resource type="Texture2D" uid="uid://cbydni2j81n0l" path="res://addons/escoria-wizard/graphics/darkgreyarrow_dr.png" id="34"] +[ext_resource type="Texture2D" uid="uid://s7i1hfxl2dnm" path="res://addons/escoria-wizard/graphics/darkgreyarrow_dl.png" id="35"] +[ext_resource type="Script" uid="uid://ihbrgvupidxn" path="res://addons/escoria-wizard/draw_frame_rects.gd" id="36"] +[ext_resource type="Script" uid="uid://bwbxhkbjuggxq" path="res://addons/escoria-wizard/CharacterCreator.gd" id="37"] +[ext_resource type="PackedScene" uid="uid://b1mr4ghmqeyo" path="res://addons/escoria-wizard/help_window.tscn" id="38"] + +[sub_resource type="SpriteFrames" id="SpriteFrames_m6nm6"] +animations = [{ +"frames": [], +"loop": true, +"name": &"default", +"speed": 5.0 +}, { +"frames": [], +"loop": true, +"name": &"in_progress", +"speed": 5.0 +}] + +[node name="CharacterCreator" type="MarginContainer"] +offset_right = 1290.0 +offset_bottom = 900.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 50 +theme_override_constants/margin_top = 50 +theme_override_constants/margin_right = 50 +theme_override_constants/margin_bottom = 50 +script = ExtResource("37") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="node_name_colorrect2" type="ColorRect" parent="VBoxContainer/MarginContainer"] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +color = Color(0.235294, 0.341176, 0.290196, 1) + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/MarginContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/MarginContainer/CenterContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.921569, 1, 0, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +text = "Character Creator" +uppercase = true + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(1190, 550) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 20 + +[node name="configuration" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(400, 550) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="node_name_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration"] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +color = Color(0.235294, 0.341176, 0.290196, 1) + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="node_name" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer"] +custom_minimum_size = Vector2(400, 130) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name"] +layout_mode = 2 + +[node name="name_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 3 +color = Color(0.215686, 0.478431, 0.235294, 1) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 6 +text = "Node Details" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name"] +layout_mode = 2 +theme_override_constants/margin_left = 5 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2"] +layout_mode = 2 +columns = 3 + +[node name="node_name_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +layout_mode = 2 +text = "Name:" + +[node name="node_name" type="LineEdit" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "The will be the name of the node in your scene tree." +text = "replace_me" +caret_blink = true +caret_blink_interval = 0.5 + +[node name="Spacer" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="global_id_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +layout_mode = 2 +text = "Global ID:" + +[node name="global_id" type="LineEdit" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 +tooltip_text = "The global id for the character to be used in ESC scripts." +caret_blink = true +caret_blink_interval = 0.5 + +[node name="Spacer2" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="character_path_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +layout_mode = 2 +text = "Save to folder:" + +[node name="character_path" type="LineEdit" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 +tooltip_text = "The directory on disk where the character scene will be saved." +text = "res://game/characters/sophie_2" +editable = false +caret_blink = true +caret_blink_interval = 0.5 + +[node name="character_path_change_button" type="Button" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +layout_mode = 2 +text = "Change" + +[node name="charactertype" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer"] +custom_minimum_size = Vector2(400, 50) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype"] +layout_mode = 2 + +[node name="directions_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/MarginContainer"] +layout_mode = 2 +color = Color(0.215686, 0.478431, 0.235294, 1) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="direction_number_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/MarginContainer/MarginContainer"] +layout_mode = 2 +text = "Player or NPC?" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype"] +layout_mode = 2 +theme_override_constants/separation = 50 +alignment = 1 + +[node name="player" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Create a user-controlled character" +text = "player" + +[node name="npc" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Create a non-player character" +text = "npc" + +[node name="directions" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer"] +custom_minimum_size = Vector2(400, 50) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions"] +layout_mode = 2 + +[node name="directions_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/MarginContainer"] +layout_mode = 2 +color = Color(0.215686, 0.478431, 0.235294, 1) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="direction_number_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/MarginContainer/MarginContainer"] +layout_mode = 2 +text = "Number of Directions" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions"] +layout_mode = 2 +theme_override_constants/separation = 50 +alignment = 1 + +[node name="one_direction" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Create 4 directions of animation" +text = "1" + +[node name="two_directions" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Create 4 directions of animation" +text = "2" + +[node name="four_directions" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Create 4 directions of animation" +button_pressed = true +text = "4" + +[node name="eight_directions" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Create 8 directions of animation" +text = "8" + +[node name="animation" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer"] +custom_minimum_size = Vector2(400, 200) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +layout_mode = 2 + +[node name="animation_type_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer"] +layout_mode = 2 +color = Color(0.215686, 0.478431, 0.235294, 1) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="animation_type_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer/MarginContainer"] +layout_mode = 2 +text = "Animation" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +layout_mode = 2 +theme_override_constants/separation = 20 +alignment = 1 + +[node name="walk_checkbox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Configure walk animations" +button_pressed = true +text = "walk" + +[node name="talk_checkbox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Configure talk animations" +text = "talk" + +[node name="idle_checkbox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Configure idle animations" +text = "idle" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer2"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 + +[node name="HBoxContainer2" type="GridContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +layout_mode = 2 +columns = 5 + +[node name="Control" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 + +[node name="HBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +layout_mode = 2 + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2"] +layout_mode = 2 +columns = 3 + +[node name="Container_upleft" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="set_dir_upleft" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upleft" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Up left animation (configured)" +toggle_mode = true +texture_normal = ExtResource("27") +texture_pressed = ExtResource("30") +texture_hover = ExtResource("30") + +[node name="unset_dir_upleft" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upleft" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Up left animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource("15") +texture_pressed = ExtResource("6") +texture_hover = ExtResource("6") + +[node name="Container_up" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="set_dir_up" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up" groups=["direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Up animation (configured)" +toggle_mode = true +texture_normal = ExtResource("28") +texture_pressed = ExtResource("23") +texture_hover = ExtResource("23") + +[node name="unset_dir_up" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up" groups=["direction_buttons"]] +layout_mode = 2 +tooltip_text = "Up animation (unconfigured)" +toggle_mode = true +button_pressed = true +texture_normal = ExtResource("20") +texture_pressed = ExtResource("18") +texture_hover = ExtResource("18") + +[node name="ColorRectSpacer" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up"] +visible = false +modulate = Color(0.235294, 0.341176, 0.290196, 1) +custom_minimum_size = Vector2(28, 28) +layout_mode = 2 + +[node name="Container_upright" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="unset_dir_upright" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upright" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Up right animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource("14") +texture_pressed = ExtResource("17") +texture_hover = ExtResource("17") + +[node name="set_dir_upright" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upright" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Up right animation (configured)" +toggle_mode = true +texture_normal = ExtResource("16") +texture_pressed = ExtResource("24") +texture_hover = ExtResource("24") + +[node name="Container_left" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="set_dir_left" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left" groups=["direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Left animation (configured)" +toggle_mode = true +texture_normal = ExtResource("12") +texture_pressed = ExtResource("26") +texture_hover = ExtResource("26") + +[node name="unset_dir_left" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left" groups=["direction_buttons"]] +layout_mode = 2 +tooltip_text = "Left animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource("21") +texture_pressed = ExtResource("13") +texture_hover = ExtResource("13") + +[node name="ColorRectSpacer" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left"] +visible = false +modulate = Color(0.235294, 0.341176, 0.290196, 1) +custom_minimum_size = Vector2(28, 28) +layout_mode = 2 + +[node name="Container_centre" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="Container_right" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="set_dir_right" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_right" groups=["direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Right animation (configured)" +toggle_mode = true +texture_normal = ExtResource("3") +texture_pressed = ExtResource("4") +texture_hover = ExtResource("4") + +[node name="unset_dir_right" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_right" groups=["direction_buttons"]] +layout_mode = 2 +tooltip_text = "Right animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource("19") +texture_pressed = ExtResource("2") +texture_hover = ExtResource("2") + +[node name="Container_downleft" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="unset_dir_downleft" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downleft" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Down left animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource("35") +texture_pressed = ExtResource("7") +texture_hover = ExtResource("7") + +[node name="set_dir_downleft" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downleft" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Down left animation (configured)" +toggle_mode = true +texture_normal = ExtResource("11") +texture_pressed = ExtResource("10") +texture_hover = ExtResource("10") + +[node name="Container_down" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="set_dir_down" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down" groups=["direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Down animation (configured)" +toggle_mode = true +texture_normal = ExtResource("1") +texture_pressed = ExtResource("5") +texture_hover = ExtResource("5") + +[node name="unset_dir_down" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down" groups=["direction_buttons"]] +layout_mode = 2 +tooltip_text = "Down animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource("8") +texture_pressed = ExtResource("33") +texture_hover = ExtResource("33") + +[node name="ColorRectSpacer" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down"] +visible = false +modulate = Color(0.235294, 0.341176, 0.290196, 1) +custom_minimum_size = Vector2(28, 28) +layout_mode = 2 + +[node name="Container_downright" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +layout_mode = 2 + +[node name="set_dir_downright" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downright" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Down right animation (configured)" +toggle_mode = true +texture_normal = ExtResource("25") +texture_pressed = ExtResource("31") +texture_hover = ExtResource("31") + +[node name="unset_dir_downright" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downright" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +layout_mode = 2 +tooltip_text = "Down right animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource("34") +texture_pressed = ExtResource("22") +texture_hover = ExtResource("22") + +[node name="MarginContainer3" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 15 + +[node name="mirror_checkbox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer3"] +visible = false +layout_mode = 2 +tooltip_text = "Mirror opposite direction's animation" +text = "Mirror" + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer"] +custom_minimum_size = Vector2(0, 35) +layout_mode = 2 + +[node name="Control2" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 + +[node name="preview" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +custom_minimum_size = Vector2(100, 200) +layout_mode = 2 + +[node name="anim_preview_background" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview"] +layout_mode = 2 +tooltip_text = "Animation preview based on current animation settings" +color = Color(0.215686, 0.207843, 0.207843, 1) + +[node name="MarginContainer" type="CenterContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="anim_preview_sprite" type="AnimatedSprite2D" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview/MarginContainer"] +visible = false +scale = Vector2(0.05, 0.05) +sprite_frames = SubResource("SpriteFrames_m6nm6") +animation = &"in_progress" +centered = false + +[node name="no_anim_found_sprite" type="TextureRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview/MarginContainer"] +layout_mode = 2 +texture = ExtResource("9") + +[node name="Control3" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 + +[node name="Control4" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 + +[node name="current_direction_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +layout_mode = 2 +text = "Current Direction" + +[node name="Control5" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 + +[node name="anim_preview_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +layout_mode = 2 +text = "Animation Preview" + +[node name="Control6" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 + +[node name="MarginContainer3" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer3"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 + +[node name="autosave" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +custom_minimum_size = Vector2(400, 50) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave"] +layout_mode = 2 + +[node name="autosave_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer"] +layout_mode = 2 +color = Color(0.215686, 0.478431, 0.235294, 1) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="Autosave_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer/MarginContainer"] +layout_mode = 2 +text = "Autosave changes" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="Autosave_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer/MarginContainer2"] +layout_mode = 2 +text = "Autosave changes" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave"] +layout_mode = 2 +theme_override_constants/separation = 50 +alignment = 1 + +[node name="AutoStoreCheckBox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/HBoxContainer"] +layout_mode = 2 +tooltip_text = "All changes in this tool are automatically saved for a quicker workflow." +text = "Auto-store all changes to this character" + +[node name="spritesheet" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(440, 550) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="spritesheet_background" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet"] +layout_mode = 2 +mouse_filter = 1 +color = Color(0.235294, 0.341176, 0.290196, 1) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet"] +custom_minimum_size = Vector2(450, 550) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer"] +layout_mode = 2 + +[node name="spritesheet_scroll_container" type="ScrollContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +tooltip_text = "Loaded spritesheet" + +[node name="control" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container"] +custom_minimum_size = Vector2(192, 210) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="spritesheet_area" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control"] +layout_mode = 2 +mouse_filter = 1 +color = Color(0.203922, 0.184314, 0.184314, 1) + +[node name="spritesheet_sprite" type="TextureRect" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control"] +custom_minimum_size = Vector2(240, 25) +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 0 +expand_mode = 1 + +[node name="frame_rectangles" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control"] +layout_mode = 2 +mouse_filter = 1 +script = ExtResource("36") +total_num_rows = 1 +total_num_columns = 1 +cell_size = Vector2(1, 1) +zoom_factor = 0.01 + +[node name="MarginContainer" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control"] +layout_mode = 2 + +[node name="no_spritesheet_found_sprite" type="TextureRect" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control/MarginContainer"] +layout_mode = 2 +texture = ExtResource("29") + +[node name="zoom_scroll" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(440, 0) +layout_mode = 2 + +[node name="zoom_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll"] +custom_minimum_size = Vector2(80, 0) +layout_mode = 2 +text = "Zoom: 1.0x" + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="zoom_scrollbar" type="HScrollBar" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll/MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +tooltip_text = "Current spritesheet zoom level" +min_value = 0.1 +max_value = 5.0 +value = 1.0 + +[node name="zoom_reset_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll"] +layout_mode = 2 +tooltip_text = "Reset spritesheet zoom" +text = "Reset" + +[node name="zoom_current" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(440, 30) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current"] +layout_mode = 2 + +[node name="current_spritesheet" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current/MarginContainer"] +layout_mode = 2 +text = "Current spritesheet:" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="current_spritesheet_label" type="TextEdit" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current/MarginContainer2"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "No spritesheet loaded." + +[node name="spritesheet_controls" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(300, 450) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="background_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet_controls"] +layout_mode = 2 +color = Color(0.235294, 0.341176, 0.290196, 1) + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls"] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_bottom = 6 + +[node name="name_colorrect3" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer"] +layout_mode = 2 +color = Color(0.215686, 0.478431, 0.235294, 1) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer/MarginContainer"] +layout_mode = 2 +text = "Spritesheet Details" + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +layout_mode = 2 + +[node name="load_spritesheet_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/CenterContainer"] +layout_mode = 2 +tooltip_text = "Load a new spritesheet to create animations" +text = "Load spritesheet" + +[node name="spritesheet_details_container" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +layout_mode = 2 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container"] +custom_minimum_size = Vector2(260, 0) +layout_mode = 2 +theme_override_constants/h_separation = 8 +theme_override_constants/v_separation = 10 +columns = 2 + +[node name="h_frames_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +text = "Horizontal frames:" +horizontal_alignment = 2 + +[node name="h_frames_spin_box" type="SpinBox" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +tooltip_text = "Divide spritesheet into this many horizontal frames" +min_value = 1.0 +value = 1.0 +rounded = true + +[node name="v_frames_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +text = "Vertical frames:" +horizontal_alignment = 2 + +[node name="v_frames_spin_box" type="SpinBox" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +tooltip_text = "Divide spritesheet into this many vertical frames" +min_value = 1.0 +value = 1.0 +rounded = true + +[node name="start_frame_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +text = "Start frame:" +horizontal_alignment = 2 + +[node name="start_frame" type="SpinBox" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +tooltip_text = "Start frame to use for this animation" +max_value = 1000.0 +rounded = true + +[node name="end_frame_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +text = "End frame:" +horizontal_alignment = 2 + +[node name="end_frame" type="SpinBox" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +tooltip_text = "End frame to use for this animation" +max_value = 1000.0 +rounded = true + +[node name="anim_speed_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +text = "Animation speed: 5 FPS" +horizontal_alignment = 2 + +[node name="anim_speed_scroll_bar" type="HScrollBar" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +tooltip_text = "Speed of the current animation" +min_value = 1.0 +max_value = 60.0 +step = 1.0 +value = 5.0 +rounded = true + +[node name="original_size_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +text = "Source sprite size: (0, 0)" +horizontal_alignment = 2 + +[node name="empty_node2" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 + +[node name="frame_size_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 +text = "Frame size: (0, 0)" +horizontal_alignment = 2 + +[node name="empty_node3" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +layout_mode = 2 + +[node name="empty_node" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 + +[node name="store_anim" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +visible = false +custom_minimum_size = Vector2(300, 100) +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim"] +layout_mode = 2 + +[node name="name_colorrect2" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/MarginContainer"] +layout_mode = 2 +color = Color(0.215686, 0.478431, 0.235294, 1) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/MarginContainer/MarginContainer"] +layout_mode = 2 +size_flags_vertical = 6 +text = "Store Animation +" +vertical_alignment = 1 + +[node name="CenterContainer2" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim"] +layout_mode = 2 + +[node name="anim_store_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/CenterContainer2"] +layout_mode = 2 +text = "Store Animation" + +[node name="MarginContainer2" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer2"] +layout_mode = 2 + +[node name="export_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer2/VBoxContainer"] +layout_mode = 2 +tooltip_text = "Export all animations to a Godot scene" +theme_override_colors/font_color = Color(0, 1, 0.0392157, 1) +text = "Export Character to Godot Scene" +icon = ExtResource("32") + +[node name="name_colorrect4" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer2/VBoxContainer"] +layout_mode = 2 +color = Color(0.215686, 0.478431, 0.235294, 1) + +[node name="empty_node2" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 280) +layout_mode = 2 + +[node name="MarginContainer3" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3"] +layout_mode = 2 +theme_override_constants/h_separation = 15 +columns = 3 + +[node name="help_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer"] +layout_mode = 2 +tooltip_text = "Export all animations to a Godot scene" +text = "Help" + +[node name="reset_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer"] +layout_mode = 2 +text = "Reset" + +[node name="main_menu" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer"] +layout_mode = 2 +text = "Main Menu" + +[node name="ImageFileDialog" type="FileDialog" parent="."] +title = "Open a File" +ok_button_text = "Open" +file_mode = 0 +access = 2 +filters = PackedStringArray("*.png, *.jpg, *.jpeg ; Supported Images") + +[node name="CharacterPathFileDialog" type="FileDialog" parent="."] +mode = 2 +title = "Open a Directory" +ok_button_text = "Select Current Folder" +file_mode = 2 + +[node name="InformationWindows" type="Control" parent="."] +visible = false +layout_mode = 2 + +[node name="unstored_changes_window" type="Window" parent="InformationWindows"] +position = Vector2i(0, 36) +visible = false +exclusive = true + +[node name="Label" type="Label" parent="InformationWindows/unstored_changes_window"] +offset_left = 86.0 +offset_top = 57.0 +offset_right = 605.0 +offset_bottom = 156.0 +text = "WARNING : You have made changes to this animation which haven't been stored. + +You can +* Commit the changes, and then change to the selected direction. +* Lose the changes, and then change to the selected direction. +* Cancel the request to change directions and keep editing this direction." + +[node name="commit_button" type="Button" parent="InformationWindows/unstored_changes_window"] +offset_left = 45.0 +offset_top = 250.0 +offset_right = 215.0 +offset_bottom = 270.0 +text = "Commit changes" + +[node name="lose_button" type="Button" parent="InformationWindows/unstored_changes_window"] +offset_left = 260.0 +offset_top = 250.0 +offset_right = 430.0 +offset_bottom = 270.0 +text = "Lose changes" + +[node name="cancel_button" type="Button" parent="InformationWindows/unstored_changes_window"] +offset_left = 475.0 +offset_top = 250.0 +offset_right = 645.0 +offset_bottom = 270.0 +text = "Cancel and keep editing" + +[node name="unstored_changes_window_anim_change" type="Window" parent="InformationWindows"] +position = Vector2i(0, 36) +visible = false +exclusive = true + +[node name="Label" type="Label" parent="InformationWindows/unstored_changes_window_anim_change"] +offset_left = 86.0 +offset_top = 57.0 +offset_right = 605.0 +offset_bottom = 156.0 +text = "WARNING : You have made changes to this animation which haven't been stored. + +You can +* Commit the changes, and then change to the selected direction. +* Lose the changes, and then change to the selected direction. +* Cancel the request to change directions and keep editing this direction." + +[node name="commit_button" type="Button" parent="InformationWindows/unstored_changes_window_anim_change"] +offset_left = 45.0 +offset_top = 250.0 +offset_right = 215.0 +offset_bottom = 270.0 +text = "Commit changes" + +[node name="lose_button" type="Button" parent="InformationWindows/unstored_changes_window_anim_change"] +offset_left = 260.0 +offset_top = 250.0 +offset_right = 430.0 +offset_bottom = 270.0 +text = "Lose changes" + +[node name="cancel_button" type="Button" parent="InformationWindows/unstored_changes_window_anim_change"] +offset_left = 475.0 +offset_top = 250.0 +offset_right = 645.0 +offset_bottom = 270.0 +text = "Cancel and keep editing" + +[node name="generic_error_window" type="AcceptDialog" parent="InformationWindows"] +dialog_text = "Please load a spritesheet to begin." + +[node name="export_progress" type="Window" parent="InformationWindows"] +position = Vector2i(0, 36) +size = Vector2i(400, 200) +visible = false +wrap_controls = true +exclusive = true + +[node name="progress_label" type="Label" parent="InformationWindows/export_progress"] +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -200.0 +offset_top = 30.0 +offset_right = 200.0 +offset_bottom = 78.0 +size_flags_horizontal = 3 +size_flags_vertical = 5 +text = "Exporting animations to ESCPlayer node." +horizontal_alignment = 1 + +[node name="progress_bar" type="ProgressBar" parent="InformationWindows/export_progress"] +offset_left = 50.0 +offset_top = 100.0 +offset_right = 350.0 +offset_bottom = 130.0 +max_value = 31.0 +rounded = true + +[node name="export_complete" type="AcceptDialog" parent="InformationWindows"] +size = Vector2i(640, 574) +dialog_text = "The new ESCPlayer node has now been exported. +For your convenience, it has been automatically opened in the Godot editor. + +Some actions you may now want to perform: + +* A dialog position node has been created. This is where text will appear when +the player talks. You will want to move this either above the character's head +or below their feet. + +* The CollisionShape3D child node defines the bounds of the character. You may +want to resize or change the shape assigned to this node. + +* You should move all child nodes down so the crosshairs are directly between +the character's feet. This ensures that where you click lines up with where the +character walks to. + +* If you want to swap a player character to an NPC, change the parent node +from an ESCPlayer to an ESCItem. Similarly, change an NPC to a player +character by changing the parent node to an ESCPlayer node. +" + +[node name="help_window" parent="InformationWindows" instance=ExtResource("38")] +position = Vector2i(0, 36) +visible = false + +[node name="ConfirmationDialog" type="ConfirmationDialog" parent="InformationWindows"] +dialog_text = "WARNING! + +If you continue you will lose the current character." + +[node name="MainMenuConfirmation" type="ConfirmationDialog" parent="InformationWindows"] +dialog_text = "If you return to the main menu +you will lose your current character. +" + +[connection signal="text_changed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer/node_name" to="." method="nodename_on_node_name_text_changed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer/character_path_change_button" to="." method="_on_character_path_change_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer/player" to="." method="_on_player_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer/npc" to="." method="_on_npc_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer/one_direction" to="." method="directions_on_one_direction_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer/two_directions" to="." method="directions_on_two_directions_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer/four_directions" to="." method="directions_on_four_directions_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer/eight_directions" to="." method="directions_on_eight_directions_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer/walk_checkbox" to="." method="animation_on_walk_checkbox_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer/talk_checkbox" to="." method="animation_on_talk_checkbox_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer/idle_checkbox" to="." method="animation_on_idle_checkbox_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upleft/set_dir_upleft" to="." method="animation_on_dir_upleft_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upleft/unset_dir_upleft" to="." method="animation_on_dir_upleft_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up/set_dir_up" to="." method="animation_on_dir_up_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up/unset_dir_up" to="." method="animation_on_dir_up_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upright/unset_dir_upright" to="." method="animation_on_dir_upright_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upright/set_dir_upright" to="." method="animation_on_dir_upright_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left/set_dir_left" to="." method="animation_on_dir_left_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left/unset_dir_left" to="." method="animation_on_dir_left_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_right/set_dir_right" to="." method="animation_on_dir_right_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_right/unset_dir_right" to="." method="animation_on_dir_right_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downleft/unset_dir_downleft" to="." method="animation_on_dir_downleft_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downleft/set_dir_downleft" to="." method="animation_on_dir_downleft_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down/set_dir_down" to="." method="animation_on_dir_down_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down/unset_dir_down" to="." method="animation_on_dir_down_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downright/set_dir_downright" to="." method="animation_on_dir_downright_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downright/unset_dir_downright" to="." method="animation_on_dir_downright_pressed"] +[connection signal="toggled" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer3/mirror_checkbox" to="." method="animation_on_mirror_checkbox_toggled"] +[connection signal="toggled" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/HBoxContainer/AutoStoreCheckBox" to="." method="_on_AutoStoreCheckBox_toggled"] +[connection signal="gui_input" from="VBoxContainer/HBoxContainer/spritesheet" to="." method="_on_spritesheet_gui_input"] +[connection signal="gui_input" from="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control" to="." method="_on_control_gui_input"] +[connection signal="value_changed" from="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll/MarginContainer/zoom_scrollbar" to="." method="spritesheet_on_zoom_scrollbar_value_changed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll/zoom_reset_button" to="." method="spritesheet_on_zoom_reset_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/CenterContainer/load_spritesheet_button" to="." method="spritesheet_on_load_spritesheet_button_pressed"] +[connection signal="changed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer/anim_speed_scroll_bar" to="." method="controls_on_anim_speed_scroll_bar_value_changed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/CenterContainer2/anim_store_button" to="." method="store_on_anim_store_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer2/VBoxContainer/export_button" to="." method="spritesheet_on_export_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer/help_button" to="." method="spritesheet_on_help_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer/reset_button" to="." method="spritesheet_on_reset_button_pressed"] +[connection signal="button_up" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer/main_menu" to="." method="spritesheet_on_main_menu_button_up"] +[connection signal="file_selected" from="ImageFileDialog" to="." method="controls_on_FileDialog_file_selected"] +[connection signal="dir_selected" from="CharacterPathFileDialog" to="." method="_on_CharacterPathFileDialog_dir_selected"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window/commit_button" to="." method="unstored_warning_on_commit_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window/lose_button" to="." method="unstored_warning_on_lose_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window/cancel_button" to="." method="unstored_warning_on_cancel_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window_anim_change/commit_button" to="." method="unstored_animchange_warning_on_commit_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window_anim_change/lose_button" to="." method="unstored_animchange_warning_on_lose_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window_anim_change/cancel_button" to="." method="unstored_animchange_warning_on_cancel_button_pressed"] +[connection signal="confirmed" from="InformationWindows/ConfirmationDialog" to="." method="spritesheet_on_reset_confirmed"] +[connection signal="confirmed" from="InformationWindows/MainMenuConfirmation" to="." method="spritesheet_on_MainMenuConfirmation_confirmed"] diff --git a/addons/escoria-wizard/CharacterCreator.tscn.bak b/addons/escoria-wizard/CharacterCreator.tscn.bak new file mode 100644 index 0000000..9b01bf2 --- /dev/null +++ b/addons/escoria-wizard/CharacterCreator.tscn.bak @@ -0,0 +1,1544 @@ +[gd_scene load_steps=40 format=2] + +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreenarrow_down.png" type="Texture" id=1] +[ext_resource path="res://addons/escoria-wizard/graphics/greyarrow_r.png" type="Texture" id=2] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreenarrow_right.png" type="Texture" id=3] +[ext_resource path="res://addons/escoria-wizard/graphics/greenarrow_right.png" type="Texture" id=4] +[ext_resource path="res://addons/escoria-wizard/graphics/greenarrow_down.png" type="Texture" id=5] +[ext_resource path="res://addons/escoria-wizard/graphics/greyarrow_ul.png" type="Texture" id=6] +[ext_resource path="res://addons/escoria-wizard/graphics/greyarrow_dl.png" type="Texture" id=7] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreyarrow_d.png" type="Texture" id=8] +[ext_resource path="res://addons/escoria-wizard/graphics/no_animation.png" type="Texture" id=9] +[ext_resource path="res://addons/escoria-wizard/graphics/greenarrow_downleft.png" type="Texture" id=10] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreenarrow_downleft.png" type="Texture" id=11] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreenarrow_left.png" type="Texture" id=12] +[ext_resource path="res://addons/escoria-wizard/graphics/greyarrow_l.png" type="Texture" id=13] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreyarrow_ur.png" type="Texture" id=14] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreyarrow_ul.png" type="Texture" id=15] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreenarrow_upright.png" type="Texture" id=16] +[ext_resource path="res://addons/escoria-wizard/graphics/greyarrow_ur.png" type="Texture" id=17] +[ext_resource path="res://addons/escoria-wizard/graphics/greyarrow_u.png" type="Texture" id=18] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreyarrow_r.png" type="Texture" id=19] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreyarrow_u.png" type="Texture" id=20] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreyarrow_l.png" type="Texture" id=21] +[ext_resource path="res://addons/escoria-wizard/graphics/greyarrow_dr.png" type="Texture" id=22] +[ext_resource path="res://addons/escoria-wizard/graphics/greenarrow_up.png" type="Texture" id=23] +[ext_resource path="res://addons/escoria-wizard/graphics/greenarrow_upright.png" type="Texture" id=24] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreenarrow_downright.png" type="Texture" id=25] +[ext_resource path="res://addons/escoria-wizard/graphics/greenarrow_left.png" type="Texture" id=26] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreenarrow_upleft.png" type="Texture" id=27] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreenarrow_up.png" type="Texture" id=28] +[ext_resource path="res://addons/escoria-wizard/graphics/no_spritesheet.png" type="Texture" id=29] +[ext_resource path="res://addons/escoria-wizard/graphics/greenarrow_upleft.png" type="Texture" id=30] +[ext_resource path="res://addons/escoria-wizard/graphics/greenarrow_downright.png" type="Texture" id=31] +[ext_resource path="res://addons/escoria-wizard/graphics/icon.png" type="Texture" id=32] +[ext_resource path="res://addons/escoria-wizard/graphics/greyarrow_d.png" type="Texture" id=33] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreyarrow_dr.png" type="Texture" id=34] +[ext_resource path="res://addons/escoria-wizard/graphics/darkgreyarrow_dl.png" type="Texture" id=35] +[ext_resource path="res://addons/escoria-wizard/draw_frame_rects.gd" type="Script" id=36] +[ext_resource path="res://addons/escoria-wizard/CharacterCreator.gd" type="Script" id=37] +[ext_resource path="res://addons/escoria-wizard/help_window.tscn" type="PackedScene" id=38] + +[sub_resource type="SpriteFrames" id=1] +animations = [ { +"frames": [ ], +"loop": true, +"name": "default", +"speed": 5.0 +}, { +"frames": [ ], +"loop": true, +"name": "in_progress", +"speed": 5.0 +} ] + +[node name="CharacterCreator" type="MarginContainer"] +margin_right = 1290.0 +margin_bottom = 900.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +custom_constants/margin_right = 50 +custom_constants/margin_top = 50 +custom_constants/margin_left = 50 +custom_constants/margin_bottom = 50 +script = ExtResource( 37 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +margin_left = 50.0 +margin_top = 50.0 +margin_right = 1240.0 +margin_bottom = 850.0 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] +margin_right = 1190.0 +margin_bottom = 14.0 + +[node name="node_name_colorrect2" type="ColorRect" parent="VBoxContainer/MarginContainer"] +margin_right = 1190.0 +margin_bottom = 14.0 +rect_min_size = Vector2( 400, 0 ) +color = Color( 0.235294, 0.341176, 0.290196, 1 ) + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/MarginContainer"] +margin_right = 1190.0 +margin_bottom = 14.0 + +[node name="Label" type="Label" parent="VBoxContainer/MarginContainer/CenterContainer"] +margin_left = 527.0 +margin_right = 663.0 +margin_bottom = 14.0 +custom_colors/font_color = Color( 0.921569, 1, 0, 1 ) +custom_colors/font_color_shadow = Color( 0, 0, 0, 1 ) +text = "Character Creator" +uppercase = true + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 18.0 +margin_right = 1190.0 +margin_bottom = 800.0 +rect_min_size = Vector2( 1190, 550 ) +size_flags_horizontal = 3 +size_flags_vertical = 3 +custom_constants/separation = 20 + +[node name="configuration" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +margin_right = 400.0 +margin_bottom = 782.0 +rect_min_size = Vector2( 400, 550 ) +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="node_name_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration"] +margin_right = 400.0 +margin_bottom = 782.0 +rect_min_size = Vector2( 400, 0 ) +color = Color( 0.235294, 0.341176, 0.290196, 1 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration"] +margin_right = 400.0 +margin_bottom = 782.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="node_name" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer"] +margin_right = 400.0 +margin_bottom = 130.0 +rect_min_size = Vector2( 400, 130 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name"] +margin_right = 400.0 +margin_bottom = 24.0 + +[node name="name_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +size_flags_vertical = 3 +color = Color( 0.215686, 0.478431, 0.235294, 1 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 +custom_constants/margin_bottom = 5 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer/MarginContainer"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 395.0 +margin_bottom = 19.0 +size_flags_vertical = 6 +text = "Node Details" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name"] +margin_top = 28.0 +margin_right = 400.0 +margin_bottom = 108.0 +custom_constants/margin_left = 5 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2"] +margin_left = 5.0 +margin_right = 400.0 +margin_bottom = 80.0 +columns = 3 + +[node name="node_name_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_top = 5.0 +margin_right = 91.0 +margin_bottom = 19.0 +text = "Name:" + +[node name="node_name" type="LineEdit" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_left = 95.0 +margin_right = 295.0 +margin_bottom = 24.0 +rect_min_size = Vector2( 200, 0 ) +hint_tooltip = "The will be the name of the node in your scene tree." +text = "replace_me" +caret_blink = true +caret_blink_speed = 0.5 + +[node name="Spacer" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_left = 299.0 +margin_right = 357.0 +margin_bottom = 24.0 + +[node name="global_id_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_top = 33.0 +margin_right = 91.0 +margin_bottom = 47.0 +text = "Global ID:" + +[node name="global_id" type="LineEdit" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_left = 95.0 +margin_top = 28.0 +margin_right = 295.0 +margin_bottom = 52.0 +rect_min_size = Vector2( 200, 0 ) +hint_tooltip = "The global id for the character to be used in ESC scripts." +caret_blink = true +caret_blink_speed = 0.5 + +[node name="Spacer2" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_left = 299.0 +margin_top = 28.0 +margin_right = 357.0 +margin_bottom = 52.0 + +[node name="character_path_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_top = 61.0 +margin_right = 91.0 +margin_bottom = 75.0 +text = "Save to folder:" + +[node name="character_path" type="LineEdit" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_left = 95.0 +margin_top = 56.0 +margin_right = 295.0 +margin_bottom = 80.0 +rect_min_size = Vector2( 200, 0 ) +hint_tooltip = "The directory on disk where the character scene will be saved." +text = "res://" +editable = false +caret_blink = true +caret_blink_speed = 0.5 + +[node name="character_path_change_button" type="Button" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer"] +margin_left = 299.0 +margin_top = 56.0 +margin_right = 357.0 +margin_bottom = 80.0 +text = "Change" + +[node name="charactertype" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer"] +margin_top = 134.0 +margin_right = 400.0 +margin_bottom = 186.0 +rect_min_size = Vector2( 400, 50 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype"] +margin_right = 400.0 +margin_bottom = 24.0 + +[node name="directions_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +color = Color( 0.215686, 0.478431, 0.235294, 1 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 +custom_constants/margin_bottom = 5 + +[node name="direction_number_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/MarginContainer/MarginContainer"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 395.0 +margin_bottom = 19.0 +text = "Player or NPC?" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype"] +margin_top = 28.0 +margin_right = 400.0 +margin_bottom = 52.0 +custom_constants/separation = 50 +alignment = 1 + +[node name="player" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer"] +margin_left = 116.0 +margin_right = 183.0 +margin_bottom = 24.0 +hint_tooltip = "Create a user-controlled character" +pressed = true +text = "player" + +[node name="npc" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer"] +margin_left = 233.0 +margin_right = 284.0 +margin_bottom = 24.0 +hint_tooltip = "Create a non-player character" +text = "npc" + +[node name="directions" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer"] +margin_top = 190.0 +margin_right = 400.0 +margin_bottom = 242.0 +rect_min_size = Vector2( 400, 50 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions"] +margin_right = 400.0 +margin_bottom = 24.0 + +[node name="directions_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +color = Color( 0.215686, 0.478431, 0.235294, 1 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 +custom_constants/margin_bottom = 5 + +[node name="direction_number_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/MarginContainer/MarginContainer"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 395.0 +margin_bottom = 19.0 +text = "Number of Directions" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions"] +margin_top = 28.0 +margin_right = 400.0 +margin_bottom = 52.0 +custom_constants/separation = 50 +alignment = 1 + +[node name="one_direction" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer"] +margin_left = 53.0 +margin_right = 89.0 +margin_bottom = 24.0 +hint_tooltip = "Create 4 directions of animation" +text = "1" + +[node name="two_directions" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer"] +margin_left = 139.0 +margin_right = 175.0 +margin_bottom = 24.0 +hint_tooltip = "Create 4 directions of animation" +text = "2" + +[node name="four_directions" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer"] +margin_left = 225.0 +margin_right = 261.0 +margin_bottom = 24.0 +hint_tooltip = "Create 4 directions of animation" +pressed = true +text = "4" + +[node name="eight_directions" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer"] +margin_left = 311.0 +margin_right = 347.0 +margin_bottom = 24.0 +hint_tooltip = "Create 8 directions of animation" +text = "8" + +[node name="animation" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer"] +margin_top = 246.0 +margin_right = 400.0 +margin_bottom = 684.0 +rect_min_size = Vector2( 400, 200 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +margin_right = 400.0 +margin_bottom = 24.0 + +[node name="animation_type_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +color = Color( 0.215686, 0.478431, 0.235294, 1 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 +custom_constants/margin_bottom = 5 + +[node name="animation_type_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer/MarginContainer"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 395.0 +margin_bottom = 19.0 +text = "Animation" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +margin_top = 28.0 +margin_right = 400.0 +margin_bottom = 52.0 +custom_constants/separation = 20 +alignment = 1 + +[node name="walk_checkbox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer"] +margin_left = 100.0 +margin_right = 156.0 +margin_bottom = 24.0 +hint_tooltip = "Configure walk animations" +pressed = true +text = "walk" + +[node name="talk_checkbox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer"] +margin_left = 176.0 +margin_right = 227.0 +margin_bottom = 24.0 +hint_tooltip = "Configure talk animations" +text = "talk" + +[node name="idle_checkbox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer"] +margin_left = 247.0 +margin_right = 299.0 +margin_bottom = 24.0 +hint_tooltip = "Configure idle animations" +text = "idle" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +margin_top = 56.0 +margin_right = 400.0 +margin_bottom = 106.0 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer2"] +margin_right = 400.0 +margin_bottom = 50.0 +rect_min_size = Vector2( 0, 50 ) + +[node name="HBoxContainer2" type="GridContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +margin_top = 110.0 +margin_right = 400.0 +margin_bottom = 328.0 +columns = 5 + +[node name="Control" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_right = 50.0 +margin_bottom = 200.0 +rect_min_size = Vector2( 50, 0 ) + +[node name="HBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_left = 54.0 +margin_right = 164.0 +margin_bottom = 200.0 + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer"] +margin_right = 110.0 +margin_bottom = 92.0 +custom_constants/margin_left = 5 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2"] +margin_left = 5.0 +margin_right = 110.0 +margin_bottom = 92.0 +columns = 3 + +[node name="Container_upleft" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_right = 28.0 +margin_bottom = 28.0 + +[node name="set_dir_upleft" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upleft" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Up left animation (configured)" +toggle_mode = true +texture_normal = ExtResource( 27 ) +texture_pressed = ExtResource( 30 ) +texture_hover = ExtResource( 30 ) + +[node name="unset_dir_upleft" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upleft" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Up left animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource( 15 ) +texture_pressed = ExtResource( 6 ) +texture_hover = ExtResource( 6 ) + +[node name="Container_up" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_left = 32.0 +margin_right = 60.0 +margin_bottom = 28.0 + +[node name="set_dir_up" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up" groups=["direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Up animation (configured)" +toggle_mode = true +texture_normal = ExtResource( 28 ) +texture_pressed = ExtResource( 23 ) +texture_hover = ExtResource( 23 ) + +[node name="unset_dir_up" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up" groups=["direction_buttons"]] +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Up animation (unconfigured)" +toggle_mode = true +pressed = true +texture_normal = ExtResource( 20 ) +texture_pressed = ExtResource( 18 ) +texture_hover = ExtResource( 18 ) + +[node name="ColorRectSpacer" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up"] +visible = false +modulate = Color( 0.235294, 0.341176, 0.290196, 1 ) +margin_right = 28.0 +margin_bottom = 28.0 +rect_min_size = Vector2( 28, 28 ) + +[node name="Container_upright" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_left = 64.0 +margin_right = 92.0 +margin_bottom = 28.0 + +[node name="unset_dir_upright" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upright" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Up right animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource( 14 ) +texture_pressed = ExtResource( 17 ) +texture_hover = ExtResource( 17 ) + +[node name="set_dir_upright" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upright" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Up right animation (configured)" +toggle_mode = true +texture_normal = ExtResource( 16 ) +texture_pressed = ExtResource( 24 ) +texture_hover = ExtResource( 24 ) + +[node name="Container_left" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_top = 32.0 +margin_right = 28.0 +margin_bottom = 60.0 + +[node name="set_dir_left" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left" groups=["direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Left animation (configured)" +toggle_mode = true +texture_normal = ExtResource( 12 ) +texture_pressed = ExtResource( 26 ) +texture_hover = ExtResource( 26 ) + +[node name="unset_dir_left" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left" groups=["direction_buttons"]] +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Left animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource( 21 ) +texture_pressed = ExtResource( 13 ) +texture_hover = ExtResource( 13 ) + +[node name="ColorRectSpacer" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left"] +visible = false +modulate = Color( 0.235294, 0.341176, 0.290196, 1 ) +margin_right = 28.0 +margin_bottom = 28.0 +rect_min_size = Vector2( 28, 28 ) + +[node name="Container_centre" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_left = 32.0 +margin_top = 32.0 +margin_right = 60.0 +margin_bottom = 60.0 + +[node name="Container_right" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_left = 64.0 +margin_top = 32.0 +margin_right = 92.0 +margin_bottom = 60.0 + +[node name="set_dir_right" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_right" groups=["direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Right animation (configured)" +toggle_mode = true +texture_normal = ExtResource( 3 ) +texture_pressed = ExtResource( 4 ) +texture_hover = ExtResource( 4 ) + +[node name="unset_dir_right" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_right" groups=["direction_buttons"]] +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Right animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource( 19 ) +texture_pressed = ExtResource( 2 ) +texture_hover = ExtResource( 2 ) + +[node name="Container_downleft" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_top = 64.0 +margin_right = 28.0 +margin_bottom = 92.0 + +[node name="unset_dir_downleft" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downleft" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Down left animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource( 35 ) +texture_pressed = ExtResource( 7 ) +texture_hover = ExtResource( 7 ) + +[node name="set_dir_downleft" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downleft" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Down left animation (configured)" +toggle_mode = true +texture_normal = ExtResource( 11 ) +texture_pressed = ExtResource( 10 ) +texture_hover = ExtResource( 10 ) + +[node name="Container_down" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_left = 32.0 +margin_top = 64.0 +margin_right = 60.0 +margin_bottom = 92.0 + +[node name="set_dir_down" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down" groups=["direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Down animation (configured)" +toggle_mode = true +texture_normal = ExtResource( 1 ) +texture_pressed = ExtResource( 5 ) +texture_hover = ExtResource( 5 ) + +[node name="unset_dir_down" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down" groups=["direction_buttons"]] +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Down animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource( 8 ) +texture_pressed = ExtResource( 33 ) +texture_hover = ExtResource( 33 ) + +[node name="ColorRectSpacer" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down"] +visible = false +modulate = Color( 0.235294, 0.341176, 0.290196, 1 ) +margin_right = 28.0 +margin_bottom = 28.0 +rect_min_size = Vector2( 28, 28 ) + +[node name="Container_downright" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer"] +margin_left = 64.0 +margin_top = 64.0 +margin_right = 92.0 +margin_bottom = 92.0 + +[node name="set_dir_downright" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downright" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Down right animation (configured)" +toggle_mode = true +texture_normal = ExtResource( 25 ) +texture_pressed = ExtResource( 31 ) +texture_hover = ExtResource( 31 ) + +[node name="unset_dir_downright" type="TextureButton" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downright" groups=["8_direction_buttons", "direction_buttons"]] +visible = false +margin_right = 28.0 +margin_bottom = 28.0 +hint_tooltip = "Down right animation (unconfigured)" +toggle_mode = true +texture_normal = ExtResource( 34 ) +texture_pressed = ExtResource( 22 ) +texture_hover = ExtResource( 22 ) + +[node name="MarginContainer3" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer"] +margin_top = 96.0 +margin_right = 110.0 +margin_bottom = 161.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +custom_constants/margin_left = 15 + +[node name="mirror_checkbox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer3"] +visible = false +margin_left = 15.0 +margin_right = 110.0 +margin_bottom = 65.0 +hint_tooltip = "Mirror opposite direction's animation" +text = "Mirror" + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer"] +margin_top = 165.0 +margin_right = 110.0 +margin_bottom = 200.0 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer"] +margin_right = 110.0 +margin_bottom = 35.0 +rect_min_size = Vector2( 0, 35 ) + +[node name="Control2" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_left = 168.0 +margin_right = 218.0 +margin_bottom = 200.0 +rect_min_size = Vector2( 50, 0 ) + +[node name="preview" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_left = 222.0 +margin_right = 342.0 +margin_bottom = 200.0 +rect_min_size = Vector2( 100, 200 ) + +[node name="anim_preview_background" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview"] +margin_right = 120.0 +margin_bottom = 200.0 +hint_tooltip = "Animation preview based on current animation settings" +color = Color( 0.215686, 0.207843, 0.207843, 1 ) + +[node name="MarginContainer" type="CenterContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview"] +margin_right = 120.0 +margin_bottom = 200.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="anim_preview_sprite" type="AnimatedSprite" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview/MarginContainer"] +visible = false +scale = Vector2( 0.05, 0.05 ) +frames = SubResource( 1 ) +animation = "in_progress" +playing = true +centered = false + +[node name="no_anim_found_sprite" type="TextureRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/preview/MarginContainer"] +margin_left = 10.0 +margin_right = 110.0 +margin_bottom = 200.0 +texture = ExtResource( 9 ) + +[node name="Control3" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_left = 346.0 +margin_right = 396.0 +margin_bottom = 200.0 +rect_min_size = Vector2( 50, 0 ) + +[node name="Control4" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_top = 204.0 +margin_right = 50.0 +margin_bottom = 218.0 +rect_min_size = Vector2( 50, 0 ) + +[node name="current_direction_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_left = 54.0 +margin_top = 204.0 +margin_right = 164.0 +margin_bottom = 218.0 +text = "Current Direction" + +[node name="Control5" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_left = 168.0 +margin_top = 204.0 +margin_right = 218.0 +margin_bottom = 218.0 +rect_min_size = Vector2( 50, 0 ) + +[node name="anim_preview_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_left = 222.0 +margin_top = 204.0 +margin_right = 342.0 +margin_bottom = 218.0 +text = "Animation Preview" + +[node name="Control6" type="Control" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2"] +margin_left = 346.0 +margin_top = 204.0 +margin_right = 396.0 +margin_bottom = 218.0 +rect_min_size = Vector2( 50, 0 ) + +[node name="MarginContainer3" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +margin_top = 332.0 +margin_right = 400.0 +margin_bottom = 382.0 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/MarginContainer3"] +margin_right = 400.0 +margin_bottom = 50.0 +rect_min_size = Vector2( 0, 50 ) + +[node name="autosave" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation"] +margin_top = 386.0 +margin_right = 400.0 +margin_bottom = 438.0 +rect_min_size = Vector2( 400, 50 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave"] +margin_right = 400.0 +margin_bottom = 24.0 + +[node name="autosave_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +color = Color( 0.215686, 0.478431, 0.235294, 1 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 +custom_constants/margin_bottom = 5 + +[node name="Autosave_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer/MarginContainer"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 395.0 +margin_bottom = 19.0 +text = "Autosave changes" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer"] +margin_right = 400.0 +margin_bottom = 24.0 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 +custom_constants/margin_bottom = 5 + +[node name="Autosave_label" type="Label" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/MarginContainer/MarginContainer2"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 395.0 +margin_bottom = 19.0 +text = "Autosave changes" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave"] +margin_top = 28.0 +margin_right = 400.0 +margin_bottom = 52.0 +custom_constants/separation = 50 +alignment = 1 + +[node name="AutoStoreCheckBox" type="CheckBox" parent="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/HBoxContainer"] +margin_left = 60.0 +margin_right = 339.0 +margin_bottom = 24.0 +hint_tooltip = "All changes in this tool are automatically saved for a quicker workflow." +text = "Auto-store all changes to this character" + +[node name="spritesheet" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +margin_left = 420.0 +margin_right = 870.0 +margin_bottom = 782.0 +rect_min_size = Vector2( 440, 550 ) +mouse_filter = 1 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="spritesheet_background" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet"] +margin_right = 450.0 +margin_bottom = 782.0 +mouse_filter = 1 +color = Color( 0.235294, 0.341176, 0.290196, 1 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet"] +margin_right = 450.0 +margin_bottom = 782.0 +rect_min_size = Vector2( 450, 550 ) +mouse_filter = 1 +size_flags_horizontal = 3 +size_flags_vertical = 3 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 +custom_constants/margin_bottom = 5 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 445.0 +margin_bottom = 777.0 + +[node name="spritesheet_scroll_container" type="ScrollContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer"] +margin_right = 440.0 +margin_bottom = 714.0 +hint_tooltip = "Loaded spritesheet" +mouse_filter = 1 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="control" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container"] +margin_right = 440.0 +margin_bottom = 714.0 +rect_min_size = Vector2( 192, 210 ) +mouse_filter = 1 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="spritesheet_area" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control"] +margin_right = 440.0 +margin_bottom = 714.0 +mouse_filter = 1 +color = Color( 0.203922, 0.184314, 0.184314, 1 ) + +[node name="spritesheet_sprite" type="TextureRect" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control"] +margin_right = 240.0 +margin_bottom = 25.0 +rect_min_size = Vector2( 240, 25 ) +size_flags_horizontal = 0 +size_flags_vertical = 0 +expand = true + +[node name="frame_rectangles" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control"] +margin_right = 440.0 +margin_bottom = 714.0 +mouse_filter = 1 +script = ExtResource( 36 ) +total_num_rows = 1 +total_num_columns = 1 +cell_size = Vector2( 1, 1 ) +zoom_factor = 0.01 + +[node name="MarginContainer" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control"] +margin_right = 440.0 +margin_bottom = 714.0 +mouse_filter = 1 + +[node name="no_spritesheet_found_sprite" type="TextureRect" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control/MarginContainer"] +margin_left = 20.0 +margin_top = 157.0 +margin_right = 420.0 +margin_bottom = 557.0 +texture = ExtResource( 29 ) + +[node name="zoom_scroll" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer"] +margin_top = 718.0 +margin_right = 440.0 +margin_bottom = 738.0 +rect_min_size = Vector2( 440, 0 ) + +[node name="zoom_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll"] +margin_top = 3.0 +margin_right = 80.0 +margin_bottom = 17.0 +rect_min_size = Vector2( 80, 0 ) +text = "Zoom: 1x" + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll"] +margin_left = 84.0 +margin_right = 388.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="zoom_scrollbar" type="HScrollBar" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll/MarginContainer"] +margin_right = 304.0 +margin_bottom = 20.0 +hint_tooltip = "Current spritesheet zoom level" +size_flags_horizontal = 3 +size_flags_vertical = 3 +min_value = 0.1 +max_value = 5.0 +value = 1.0 + +[node name="zoom_reset_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll"] +margin_left = 392.0 +margin_right = 440.0 +margin_bottom = 20.0 +hint_tooltip = "Reset spritesheet zoom" +text = "Reset" + +[node name="zoom_current" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer"] +margin_top = 742.0 +margin_right = 440.0 +margin_bottom = 772.0 +rect_min_size = Vector2( 440, 30 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current"] +margin_right = 128.0 +margin_bottom = 30.0 + +[node name="current_spritesheet" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current/MarginContainer"] +margin_top = 8.0 +margin_right = 128.0 +margin_bottom = 22.0 +text = "Current spritesheet:" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current"] +margin_left = 132.0 +margin_right = 440.0 +margin_bottom = 30.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="current_spritesheet_label" type="TextEdit" parent="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_current/MarginContainer2"] +margin_right = 308.0 +margin_bottom = 30.0 +size_flags_horizontal = 3 +text = "No spritesheet loaded." +readonly = true + +[node name="spritesheet_controls" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +margin_left = 890.0 +margin_right = 1190.0 +margin_bottom = 782.0 +rect_min_size = Vector2( 300, 450 ) +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="background_colorrect" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet_controls"] +margin_right = 300.0 +margin_bottom = 782.0 +color = Color( 0.235294, 0.341176, 0.290196, 1 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls"] +margin_right = 300.0 +margin_bottom = 782.0 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +margin_right = 300.0 +margin_bottom = 30.0 +custom_constants/margin_bottom = 6 + +[node name="name_colorrect3" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer"] +margin_right = 300.0 +margin_bottom = 24.0 +color = Color( 0.215686, 0.478431, 0.235294, 1 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer"] +margin_right = 300.0 +margin_bottom = 24.0 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 +custom_constants/margin_bottom = 5 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer/MarginContainer"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 295.0 +margin_bottom = 19.0 +text = "Spritesheet Details" + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +margin_top = 34.0 +margin_right = 300.0 +margin_bottom = 54.0 + +[node name="load_spritesheet_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/CenterContainer"] +margin_left = 90.0 +margin_right = 209.0 +margin_bottom = 20.0 +hint_tooltip = "Load a new spritesheet to create animations" +text = "Load spritesheet" + +[node name="spritesheet_details_container" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +margin_top = 58.0 +margin_right = 300.0 +margin_bottom = 256.0 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container"] +margin_left = 20.0 +margin_right = 280.0 +margin_bottom = 198.0 +rect_min_size = Vector2( 260, 0 ) +custom_constants/vseparation = 10 +custom_constants/hseparation = 8 +columns = 2 + +[node name="h_frames_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_top = 5.0 +margin_right = 154.0 +margin_bottom = 19.0 +text = "Horizontal frames:" +align = 2 + +[node name="h_frames_spin_box" type="SpinBox" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_left = 162.0 +margin_right = 236.0 +margin_bottom = 24.0 +hint_tooltip = "Divide spritesheet into this many horizontal frames" +min_value = 1.0 +value = 1.0 +exp_edit = true +rounded = true + +[node name="v_frames_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_top = 39.0 +margin_right = 154.0 +margin_bottom = 53.0 +text = "Vertical frames:" +align = 2 + +[node name="v_frames_spin_box" type="SpinBox" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_left = 162.0 +margin_top = 34.0 +margin_right = 236.0 +margin_bottom = 58.0 +hint_tooltip = "Divide spritesheet into this many vertical frames" +min_value = 1.0 +value = 1.0 +rounded = true + +[node name="start_frame_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_top = 73.0 +margin_right = 154.0 +margin_bottom = 87.0 +text = "Start frame:" +align = 2 + +[node name="start_frame" type="SpinBox" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_left = 162.0 +margin_top = 68.0 +margin_right = 236.0 +margin_bottom = 92.0 +hint_tooltip = "Start frame to use for this animation" +max_value = 1000.0 +rounded = true + +[node name="end_frame_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_top = 107.0 +margin_right = 154.0 +margin_bottom = 121.0 +text = "End frame:" +align = 2 + +[node name="end_frame" type="SpinBox" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_left = 162.0 +margin_top = 102.0 +margin_right = 236.0 +margin_bottom = 126.0 +hint_tooltip = "End frame to use for this animation" +max_value = 1000.0 +rounded = true + +[node name="anim_speed_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_top = 136.0 +margin_right = 154.0 +margin_bottom = 150.0 +text = "Animation speed: 5 FPS" +align = 2 + +[node name="anim_speed_scroll_bar" type="HScrollBar" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_left = 162.0 +margin_top = 136.0 +margin_right = 236.0 +margin_bottom = 148.0 +hint_tooltip = "Speed of the current animation" +min_value = 1.0 +max_value = 60.0 +step = 1.0 +value = 5.0 +rounded = true + +[node name="original_size_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_top = 160.0 +margin_right = 154.0 +margin_bottom = 174.0 +text = "Source sprite size: (0, 0)" +align = 2 + +[node name="empty_node2" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_left = 162.0 +margin_top = 160.0 +margin_right = 236.0 +margin_bottom = 174.0 + +[node name="frame_size_label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_top = 184.0 +margin_right = 154.0 +margin_bottom = 198.0 +text = "Frame size: (0, 0)" +align = 2 + +[node name="empty_node3" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer"] +margin_left = 162.0 +margin_top = 184.0 +margin_right = 236.0 +margin_bottom = 198.0 + +[node name="empty_node" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +margin_top = 260.0 +margin_right = 300.0 +margin_bottom = 310.0 +rect_min_size = Vector2( 0, 50 ) + +[node name="store_anim" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +visible = false +margin_top = 314.0 +margin_right = 300.0 +margin_bottom = 414.0 +rect_min_size = Vector2( 300, 100 ) +custom_constants/separation = 10 + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim"] +margin_right = 300.0 +margin_bottom = 36.0 + +[node name="name_colorrect2" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/MarginContainer"] +margin_right = 300.0 +margin_bottom = 36.0 +color = Color( 0.215686, 0.478431, 0.235294, 1 ) + +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/MarginContainer"] +margin_right = 300.0 +margin_bottom = 36.0 +custom_constants/margin_right = 5 +custom_constants/margin_top = 5 +custom_constants/margin_left = 5 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/MarginContainer/MarginContainer"] +margin_left = 5.0 +margin_top = 5.0 +margin_right = 295.0 +margin_bottom = 36.0 +size_flags_vertical = 6 +text = "Store Animation +" +valign = 1 + +[node name="CenterContainer2" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim"] +margin_top = 46.0 +margin_right = 300.0 +margin_bottom = 66.0 + +[node name="anim_store_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/CenterContainer2"] +margin_left = 92.0 +margin_right = 207.0 +margin_bottom = 20.0 +text = "Store Animation" + +[node name="MarginContainer2" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +margin_top = 314.0 +margin_right = 300.0 +margin_bottom = 379.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer2"] +margin_left = 17.0 +margin_right = 283.0 +margin_bottom = 65.0 + +[node name="export_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer2/VBoxContainer"] +margin_right = 266.0 +margin_bottom = 61.0 +hint_tooltip = "Export all animations to a Godot scene" +custom_colors/font_color = Color( 0, 1, 0.0392157, 1 ) +text = "Export Character to Godot Scene" +icon = ExtResource( 32 ) + +[node name="name_colorrect4" type="ColorRect" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer2/VBoxContainer"] +margin_top = 65.0 +margin_right = 266.0 +margin_bottom = 65.0 +color = Color( 0.215686, 0.478431, 0.235294, 1 ) + +[node name="empty_node2" type="Control" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +margin_top = 383.0 +margin_right = 300.0 +margin_bottom = 663.0 +rect_min_size = Vector2( 0, 280 ) + +[node name="MarginContainer3" type="CenterContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer"] +margin_top = 667.0 +margin_right = 300.0 +margin_bottom = 687.0 + +[node name="HBoxContainer" type="GridContainer" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3"] +margin_left = 48.0 +margin_right = 251.0 +margin_bottom = 20.0 +custom_constants/hseparation = 15 +columns = 3 + +[node name="help_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer"] +margin_right = 42.0 +margin_bottom = 20.0 +hint_tooltip = "Export all animations to a Godot scene" +text = "Help" + +[node name="reset_button" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer"] +margin_left = 57.0 +margin_right = 105.0 +margin_bottom = 20.0 +text = "Reset" + +[node name="main_menu" type="Button" parent="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer"] +margin_left = 120.0 +margin_right = 203.0 +margin_bottom = 20.0 +text = "Main Menu" + +[node name="ImageFileDialog" type="FileDialog" parent="."] +margin_left = 50.0 +margin_top = 50.0 +margin_right = 1240.0 +margin_bottom = 850.0 +popup_exclusive = true +window_title = "Open a File" +mode = 0 +access = 2 +filters = PoolStringArray( "*.png, *.jpg, *.jpeg ; Supported Images" ) + +[node name="CharacterPathFileDialog" type="FileDialog" parent="."] +margin_left = 50.0 +margin_top = 50.0 +margin_right = 1240.0 +margin_bottom = 850.0 +window_title = "Open a Directory" +mode = 2 + +[node name="InformationWindows" type="Control" parent="."] +visible = false +margin_left = 50.0 +margin_top = 50.0 +margin_right = 1240.0 +margin_bottom = 850.0 + +[node name="unstored_changes_window" type="WindowDialog" parent="InformationWindows"] +margin_left = 295.0 +margin_top = 154.0 +margin_right = 985.0 +margin_bottom = 454.0 +popup_exclusive = true +window_title = "WARNING : Unstored changes" + +[node name="Label" type="Label" parent="InformationWindows/unstored_changes_window"] +margin_left = 86.0 +margin_top = 57.0 +margin_right = 605.0 +margin_bottom = 156.0 +text = "WARNING : You have made changes to this animation which haven't been stored. + +You can +* Commit the changes, and then change to the selected direction. +* Lose the changes, and then change to the selected direction. +* Cancel the request to change directions and keep editing this direction." + +[node name="commit_button" type="Button" parent="InformationWindows/unstored_changes_window"] +margin_left = 45.0 +margin_top = 250.0 +margin_right = 215.0 +margin_bottom = 270.0 +text = "Commit changes" + +[node name="lose_button" type="Button" parent="InformationWindows/unstored_changes_window"] +margin_left = 260.0 +margin_top = 250.0 +margin_right = 430.0 +margin_bottom = 270.0 +text = "Lose changes" + +[node name="cancel_button" type="Button" parent="InformationWindows/unstored_changes_window"] +margin_left = 475.0 +margin_top = 250.0 +margin_right = 645.0 +margin_bottom = 270.0 +text = "Cancel and keep editing" + +[node name="unstored_changes_window_anim_change" type="WindowDialog" parent="InformationWindows"] +margin_left = 295.0 +margin_top = 154.0 +margin_right = 985.0 +margin_bottom = 454.0 +popup_exclusive = true +window_title = "WARNING : Unstored changes" + +[node name="Label" type="Label" parent="InformationWindows/unstored_changes_window_anim_change"] +margin_left = 86.0 +margin_top = 57.0 +margin_right = 605.0 +margin_bottom = 156.0 +text = "WARNING : You have made changes to this animation which haven't been stored. + +You can +* Commit the changes, and then change to the selected direction. +* Lose the changes, and then change to the selected direction. +* Cancel the request to change directions and keep editing this direction." + +[node name="commit_button" type="Button" parent="InformationWindows/unstored_changes_window_anim_change"] +margin_left = 45.0 +margin_top = 250.0 +margin_right = 215.0 +margin_bottom = 270.0 +text = "Commit changes" + +[node name="lose_button" type="Button" parent="InformationWindows/unstored_changes_window_anim_change"] +margin_left = 260.0 +margin_top = 250.0 +margin_right = 430.0 +margin_bottom = 270.0 +text = "Lose changes" + +[node name="cancel_button" type="Button" parent="InformationWindows/unstored_changes_window_anim_change"] +margin_left = 475.0 +margin_top = 250.0 +margin_right = 645.0 +margin_bottom = 270.0 +text = "Cancel and keep editing" + +[node name="generic_error_window" type="AcceptDialog" parent="InformationWindows"] +margin_left = 343.0 +margin_top = 242.0 +margin_right = 943.0 +margin_bottom = 402.0 +input_pass_on_modal_close_click = false +popup_exclusive = true +dialog_text = "Please load a spritesheet to begin." + +[node name="export_progress" type="WindowDialog" parent="InformationWindows"] +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +margin_left = -151.0 +margin_top = -87.0 +margin_right = 249.0 +margin_bottom = 113.0 +size_flags_horizontal = 3 +popup_exclusive = true +window_title = "Export Status" + +[node name="progress_label" type="Label" parent="InformationWindows/export_progress"] +anchor_left = 0.5 +anchor_right = 0.5 +margin_left = -200.0 +margin_top = 30.0 +margin_right = 200.0 +margin_bottom = 78.0 +size_flags_horizontal = 3 +size_flags_vertical = 5 +text = "Exporting animations to ESCPlayer node." +align = 1 + +[node name="progress_bar" type="ProgressBar" parent="InformationWindows/export_progress"] +margin_left = 50.0 +margin_top = 100.0 +margin_right = 350.0 +margin_bottom = 130.0 +max_value = 31.0 +rounded = true + +[node name="export_complete" type="AcceptDialog" parent="InformationWindows"] +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +margin_left = -208.0 +margin_top = -132.0 +margin_right = 309.0 +margin_bottom = 147.0 +window_title = "Export complete" +dialog_text = "The new ESCPlayer node has now been exported. +For your convenience, it has been automatically opened in the Godot editor. + +Some actions you may now want to perform: + +* A dialog position node has been created. This is where text will appear when +the player talks. You will want to move this either above the character's head +or below their feet. + +* The CollisionShape child node defines the bounds of the character. You may +want to resize or change the shape assigned to this node. + +* You should move all child nodes down so the crosshairs are directly between +the character's feet. This ensures that where you click lines up with where the +character walks to. + +* If you want to swap a player character to an NPC, change the parent node +from an ESCPlayer to an ESCItem. Similarly, change an NPC to a player +character by changing the parent node to an ESCPlayer node. +" + +[node name="help_window" parent="InformationWindows" instance=ExtResource( 38 )] + +[node name="ConfirmationDialog" type="ConfirmationDialog" parent="InformationWindows"] +margin_left = 479.0 +margin_top = 312.0 +margin_right = 817.0 +margin_bottom = 404.0 +popup_exclusive = true +dialog_text = "WARNING! + +If you continue you will lose the current character." + +[node name="MainMenuConfirmation" type="ConfirmationDialog" parent="InformationWindows"] +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +margin_left = -122.5 +margin_top = -46.0 +margin_right = 122.5 +margin_bottom = 46.0 +dialog_text = "If you return to the main menu +you will lose your current character. +" + +[connection signal="text_changed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer/node_name" to="." method="nodename_on_node_name_text_changed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/node_name/MarginContainer2/GridContainer/character_path_change_button" to="." method="_on_character_path_change_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer/player" to="." method="_on_player_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/charactertype/HBoxContainer/npc" to="." method="_on_npc_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer/one_direction" to="." method="directions_on_one_direction_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer/two_directions" to="." method="directions_on_two_directions_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer/four_directions" to="." method="directions_on_four_directions_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/directions/HBoxContainer/eight_directions" to="." method="directions_on_eight_directions_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer/walk_checkbox" to="." method="animation_on_walk_checkbox_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer/talk_checkbox" to="." method="animation_on_talk_checkbox_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer/idle_checkbox" to="." method="animation_on_idle_checkbox_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upleft/set_dir_upleft" to="." method="animation_on_dir_upleft_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upleft/unset_dir_upleft" to="." method="animation_on_dir_upleft_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up/set_dir_up" to="." method="animation_on_dir_up_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_up/unset_dir_up" to="." method="animation_on_dir_up_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upright/unset_dir_upright" to="." method="animation_on_dir_upright_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_upright/set_dir_upright" to="." method="animation_on_dir_upright_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left/set_dir_left" to="." method="animation_on_dir_left_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_left/unset_dir_left" to="." method="animation_on_dir_left_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_right/set_dir_right" to="." method="animation_on_dir_right_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_right/unset_dir_right" to="." method="animation_on_dir_right_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downleft/unset_dir_downleft" to="." method="animation_on_dir_downleft_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downleft/set_dir_downleft" to="." method="animation_on_dir_downleft_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down/set_dir_down" to="." method="animation_on_dir_down_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_down/unset_dir_down" to="." method="animation_on_dir_down_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downright/set_dir_downright" to="." method="animation_on_dir_downright_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer2/GridContainer/Container_downright/unset_dir_downright" to="." method="animation_on_dir_downright_pressed"] +[connection signal="toggled" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/HBoxContainer2/HBoxContainer/MarginContainer3/mirror_checkbox" to="." method="animation_on_mirror_checkbox_toggled"] +[connection signal="toggled" from="VBoxContainer/HBoxContainer/configuration/VBoxContainer/animation/autosave/HBoxContainer/AutoStoreCheckBox" to="." method="_on_AutoStoreCheckBox_toggled"] +[connection signal="gui_input" from="VBoxContainer/HBoxContainer/spritesheet" to="." method="_on_spritesheet_gui_input"] +[connection signal="gui_input" from="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/spritesheet_scroll_container/control" to="." method="_on_control_gui_input"] +[connection signal="value_changed" from="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll/MarginContainer/zoom_scrollbar" to="." method="spritesheet_on_zoom_scrollbar_value_changed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet/MarginContainer/VBoxContainer/zoom_scroll/zoom_reset_button" to="." method="spritesheet_on_zoom_reset_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/CenterContainer/load_spritesheet_button" to="." method="spritesheet_on_load_spritesheet_button_pressed"] +[connection signal="changed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/spritesheet_details_container/GridContainer/anim_speed_scroll_bar" to="." method="controls_on_anim_speed_scroll_bar_value_changed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/store_anim/CenterContainer2/anim_store_button" to="." method="store_on_anim_store_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer2/VBoxContainer/export_button" to="." method="spritesheet_on_export_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer/help_button" to="." method="spritesheet_on_help_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer/reset_button" to="." method="spritesheet_on_reset_button_pressed"] +[connection signal="button_up" from="VBoxContainer/HBoxContainer/spritesheet_controls/VBoxContainer/MarginContainer3/HBoxContainer/main_menu" to="." method="spritesheet_on_main_menu_button_up"] +[connection signal="file_selected" from="ImageFileDialog" to="." method="controls_on_FileDialog_file_selected"] +[connection signal="dir_selected" from="CharacterPathFileDialog" to="." method="_on_CharacterPathFileDialog_dir_selected"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window/commit_button" to="." method="unstored_warning_on_commit_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window/lose_button" to="." method="unstored_warning_on_lose_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window/cancel_button" to="." method="unstored_warning_on_cancel_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window_anim_change/commit_button" to="." method="unstored_animchange_warning_on_commit_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window_anim_change/lose_button" to="." method="unstored_animchange_warning_on_lose_button_pressed"] +[connection signal="pressed" from="InformationWindows/unstored_changes_window_anim_change/cancel_button" to="." method="unstored_animchange_warning_on_cancel_button_pressed"] +[connection signal="confirmed" from="InformationWindows/ConfirmationDialog" to="." method="spritesheet_on_reset_confirmed"] +[connection signal="confirmed" from="InformationWindows/MainMenuConfirmation" to="." method="spritesheet_on_MainMenuConfirmation_confirmed"] diff --git a/addons/escoria-wizard/ItemCreator.tscn b/addons/escoria-wizard/ItemCreator.tscn new file mode 100644 index 0000000..22d735a --- /dev/null +++ b/addons/escoria-wizard/ItemCreator.tscn @@ -0,0 +1,313 @@ +[gd_scene load_steps=4 format=3 uid="uid://beawklamapteq"] + +[ext_resource type="Script" uid="uid://bxj8cgfbk5r8f" path="res://addons/escoria-wizard/item_creator.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://b7ax2phlsg1x6" path="res://addons/escoria-wizard/graphics/inventory_preview.png" id="2"] +[ext_resource type="Texture2D" uid="uid://bf47xbq6w4hou" path="res://addons/escoria-wizard/graphics/object_preview.png" id="3"] + +[node name="ItemCreator" type="MarginContainer"] +custom_minimum_size = Vector2(500, 500) +offset_left = 395.0 +offset_top = 50.0 +offset_right = 895.0 +offset_bottom = 850.0 +size_flags_horizontal = 4 +size_flags_vertical = 4 +script = ExtResource("1") + +[node name="window_background_colour" type="ColorRect" parent="."] +custom_minimum_size = Vector2(500, 800) +layout_mode = 2 +color = Color(0.235294, 0.341176, 0.290196, 1) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="Control" type="CenterContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 60) +layout_mode = 2 + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/Control"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Control/CenterContainer"] +layout_mode = 2 + +[node name="BackgroundObjectCheckBox" type="CheckBox" parent="VBoxContainer/Control/CenterContainer/HBoxContainer"] +layout_mode = 2 +text = "Create background object" + +[node name="InventoryItemCheckBox" type="CheckBox" parent="VBoxContainer/Control/CenterContainer/HBoxContainer"] +layout_mode = 2 +text = "Create inventory item" + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +layout_mode = 2 + +[node name="HelperHeading" type="MarginContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 40) +layout_mode = 2 + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/HelperHeading"] +layout_mode = 2 + +[node name="ObjectHeading" type="Label" parent="VBoxContainer/HelperHeading/CenterContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.592157, 0.87451, 0.533333, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +text = "Object Creator" +uppercase = true + +[node name="InventoryHeading" type="Label" parent="VBoxContainer/HelperHeading/CenterContainer"] +visible = false +layout_mode = 2 +theme_override_colors/font_color = Color(0.592157, 0.87451, 0.533333, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +text = "Inventory Item Creator" +uppercase = true + +[node name="Description" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="ObjectDescription" type="Label" parent="VBoxContainer/Description"] +layout_mode = 2 +text = "The object creator is used to create background objects +that the player can interact with, but that will not become +part of their inventory. + +NOTE: The node will be created as a child of whichever node +is currently selected in the scene tree. +" +horizontal_alignment = 1 + +[node name="InventoryDescription" type="Label" parent="VBoxContainer/Description"] +visible = false +layout_mode = 2 +text = "The inventory item creator is used to create objects +that the player can pick up to add +to their inventory. + +NOTE: The node will be created in the inventory +folder shown below. All inventory items for your +game must live in the same folder. +" +horizontal_alignment = 1 + +[node name="Content" type="MarginContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 500) +layout_mode = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 0 +theme_override_constants/margin_right = 20 + +[node name="GridContainer" type="GridContainer" parent="VBoxContainer/Content"] +custom_minimum_size = Vector2(460, 500) +layout_mode = 2 +columns = 3 + +[node name="ItemNameLabel" type="Label" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +text = "Item name:" + +[node name="ItemName" type="LineEdit" parent="VBoxContainer/Content/GridContainer"] +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 +text = "replace_me" + +[node name="BlankItem" type="Control" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 + +[node name="ItemGlobalIDLabel" type="Label" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +text = "Global ID:" + +[node name="ItemGlobalID" type="LineEdit" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 + +[node name="BlankItem2" type="Control" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 + +[node name="StartsInteractiveLabel" type="Label" parent="VBoxContainer/Content/GridContainer"] +custom_minimum_size = Vector2(110, 0) +layout_mode = 2 +text = "Is 'Interactive':" + +[node name="StartsInteractiveCheckBox" type="CheckBox" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +tooltip_text = "When the room first loads, can the player interact with this?" +button_pressed = true + +[node name="BlankItem3" type="Control" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 + +[node name="DefaultActionLabel" type="Label" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +text = "Default action: +" +vertical_alignment = 1 + +[node name="DefaultActionOption" type="OptionButton" parent="VBoxContainer/Content/GridContainer"] +custom_minimum_size = Vector2(200, 31) +layout_mode = 2 + +[node name="BlankItem4" type="Control" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 + +[node name="ImagePathLabel" type="Label" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +text = "Item graphic:" + +[node name="ImagePath" type="LineEdit" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +editable = false + +[node name="ChangeImageButton" type="Button" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +text = "Change Image" + +[node name="PreviewLabel" type="Label" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +text = "Preview:" + +[node name="Preview" type="ColorRect" parent="VBoxContainer/Content/GridContainer"] +custom_minimum_size = Vector2(240, 240) +layout_mode = 2 +color = Color(0.121569, 0.196078, 0.0823529, 1) + +[node name="BackgroundColour" type="ColorRect" parent="VBoxContainer/Content/GridContainer/Preview"] +custom_minimum_size = Vector2(232, 232) +layout_mode = 0 +offset_left = 4.0 +offset_top = 4.0 +offset_right = 236.0 +offset_bottom = 236.0 +color = Color(0.254902, 0.231373, 0.231373, 1) + +[node name="InventoryPreview" type="TextureRect" parent="VBoxContainer/Content/GridContainer/Preview"] +visible = false +layout_mode = 0 +offset_left = 4.0 +offset_top = 4.0 +offset_right = 236.0 +offset_bottom = 236.0 +texture = ExtResource("2") + +[node name="ObjectPreview" type="TextureRect" parent="VBoxContainer/Content/GridContainer/Preview"] +layout_mode = 0 +offset_left = 4.0 +offset_top = 4.0 +offset_right = 236.0 +offset_bottom = 236.0 +texture = ExtResource("3") + +[node name="Preview" type="TextureRect" parent="VBoxContainer/Content/GridContainer/Preview"] +custom_minimum_size = Vector2(232, 232) +layout_mode = 0 +offset_left = 4.0 +offset_top = 4.0 +offset_right = 236.0 +offset_bottom = 236.0 + +[node name="BlankItem5" type="Control" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 + +[node name="ImageSizeLabel" type="Label" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +text = "Image size:" + +[node name="ImageSize" type="Label" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 +text = "(0, 0)" + +[node name="BlankItem6" type="Control" parent="VBoxContainer/Content/GridContainer"] +layout_mode = 2 + +[node name="InventoryPathLabel" type="Label" parent="VBoxContainer/Content/GridContainer"] +visible = false +layout_mode = 2 +text = "Inventory path:" + +[node name="InventoryPath" type="Label" parent="VBoxContainer/Content/GridContainer"] +visible = false +layout_mode = 2 +text = "res://" + +[node name="ChangePathButton" type="Button" parent="VBoxContainer/Content/GridContainer"] +visible = false +layout_mode = 2 +text = "Change Path3D" + +[node name="BlankItem7" type="Control" parent="VBoxContainer/Content/GridContainer"] +visible = false +layout_mode = 2 + +[node name="Buttons" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/margin_bottom = 10 + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer/Buttons"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Buttons/CenterContainer"] +layout_mode = 2 + +[node name="CreateButton" type="Button" parent="VBoxContainer/Buttons/CenterContainer/HBoxContainer"] +layout_mode = 2 +text = "Create Object" + +[node name="Spacer" type="Control" parent="VBoxContainer/Buttons/CenterContainer/HBoxContainer"] +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 + +[node name="ClearButton" type="Button" parent="VBoxContainer/Buttons/CenterContainer/HBoxContainer"] +layout_mode = 2 +text = "Clear All" + +[node name="Spacer2" type="Control" parent="VBoxContainer/Buttons/CenterContainer/HBoxContainer"] +custom_minimum_size = Vector2(20, 0) +layout_mode = 2 + +[node name="ExitButton" type="Button" parent="VBoxContainer/Buttons/CenterContainer/HBoxContainer"] +layout_mode = 2 +text = "Main Menu" + +[node name="LoadObjectGraphic" type="CenterContainer" parent="."] +visible = false +layout_mode = 2 +mouse_filter = 2 + +[node name="LoadObjectFileDialog" type="FileDialog" parent="LoadObjectGraphic"] +filters = PackedStringArray("*.png", "*.bmp", "*.jpg", "*.jpeg", "*.webp", "*.tga") + +[node name="Windows" type="CenterContainer" parent="."] +visible = false +layout_mode = 2 + +[node name="ConfirmationDialog" type="ConfirmationDialog" parent="Windows"] +dialog_text = "WARNING! + +If you continue you will lose the current object." + +[node name="ErrorDialog" type="AcceptDialog" parent="Windows"] + +[node name="CreateCompleteDialog" type="AcceptDialog" parent="Windows"] + +[node name="FileDialog" type="FileDialog" parent="Windows"] +mode = 2 +position = Vector2i(0, 36) + +[connection signal="toggled" from="VBoxContainer/Control/CenterContainer/HBoxContainer/BackgroundObjectCheckBox" to="." method="_on_BackgroundObjectCheckBox_toggled"] +[connection signal="toggled" from="VBoxContainer/Control/CenterContainer/HBoxContainer/InventoryItemCheckBox" to="." method="_on_InventoryItemCheckBox_toggled"] +[connection signal="text_changed" from="VBoxContainer/Content/GridContainer/ItemName" to="." method="background_on_ItemName_text_changed"] +[connection signal="text_changed" from="VBoxContainer/Content/GridContainer/ItemGlobalID" to="." method="_on_ItemGlobalID_text_changed"] +[connection signal="pressed" from="VBoxContainer/Content/GridContainer/StartsInteractiveCheckBox" to="." method="_on_StartsInteractiveCheckBox_pressed"] +[connection signal="item_selected" from="VBoxContainer/Content/GridContainer/DefaultActionOption" to="." method="_on_DefaultActionOption_item_selected"] +[connection signal="pressed" from="VBoxContainer/Content/GridContainer/ChangeImageButton" to="." method="load_button_pressed"] +[connection signal="pressed" from="VBoxContainer/Content/GridContainer/ChangePathButton" to="." method="_on_ChangePathButton_pressed"] +[connection signal="pressed" from="VBoxContainer/Buttons/CenterContainer/HBoxContainer/CreateButton" to="." method="_on_CreateButton_pressed"] +[connection signal="pressed" from="VBoxContainer/Buttons/CenterContainer/HBoxContainer/ClearButton" to="." method="Item_on_ClearButton_pressed"] +[connection signal="pressed" from="VBoxContainer/Buttons/CenterContainer/HBoxContainer/ExitButton" to="." method="Item_on_ExitButton_pressed"] +[connection signal="file_selected" from="LoadObjectGraphic/LoadObjectFileDialog" to="." method="LoadObjectFileDialog_file_selected"] +[connection signal="confirmed" from="Windows/ConfirmationDialog" to="." method="_on_ObjectConfirmationDialog_confirmed"] +[connection signal="confirmed" from="Windows/CreateCompleteDialog" to="." method="_on_CreateCompleteDialog_confirmed"] +[connection signal="dir_selected" from="Windows/FileDialog" to="." method="_on_FileDialog_dir_selected"] diff --git a/addons/escoria-wizard/RoomCreator.gd b/addons/escoria-wizard/RoomCreator.gd new file mode 100644 index 0000000..aadda0e --- /dev/null +++ b/addons/escoria-wizard/RoomCreator.gd @@ -0,0 +1,325 @@ +@tool +extends Control + +const ROOM_NAME = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/RoomName" +const GLOBAL_ID = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/GlobalID" +const PLAYER_SCENE = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/PlayerScene" +const SELECT_PLAYER_SCENE = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/SelectPlayerScene" +const SELECT_PLAYER_SCENE_SPACER = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/SelectPlayerSceneSpacer" +const USE_EMPTY_PLAYER_BUTTON = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/UseEmptyPlayerButton" +const ESC_SCRIPT = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/ESCScript" +const USE_EMPTY_ROOM_SCRIPT = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/UseEmptyRoomScript" +const USE_EMPTY_ROOM_SPACER = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/UseEmptyRoomSpacer" +const BACKGROUND_IMAGE = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/BackgroundImage" +const USE_EMPTY_BACKGROUND = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/UseEmptyBackground" +const SELECT_BACKGROUND = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/SelectBackground" +const SELECT_BACKGROUNDSPACER = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/SelectBackgroundSpacer" +const ROOM_FOLDER_PATH = "MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/RoomFolder" +const ROOM_BACKGROUND = "MarginContainer/MarginContainer/VBoxContainer/PreviewSection/CenterContainer/RoomBackground" +const BACKGROUND_PREVIEW = "MarginContainer/MarginContainer/VBoxContainer/PreviewSection/Control/BackgroundPreview" + +const SCRIPT_BLANK_TEXT = "Room will not have a script configured." +const SCRIPT_SELECT_TEXT = "Please select script." +const PLAYER_BLANK_TEXT = "Scene will be left blank." +const PLAYER_SELECT_TEXT = "Please select scene." +const BACKGROUND_BLANK_TEXT = "Image will be left blank." +const BACKGROUND_SELECT_TEXT = "Please select image." + +const ROOM_PATH_SETTING = "escoria/wizard/path_to_rooms" + +var settings_modified: bool + + +func _ready() -> void: + $"InformationWindows/PlayerSceneFileDialog".get_cancel_button().connect("pressed", Callable(self, "PlayerSceneCancelled")) + $"InformationWindows/BackgroundImageFileDialog".get_cancel_button().connect("pressed", Callable(self, "BackgroundFileCancelled")) + + +func PlayerSceneCancelled() -> void: + if get_node(PLAYER_SCENE).text == PLAYER_SELECT_TEXT: + get_node(USE_EMPTY_PLAYER_BUTTON).button_pressed = true + + +func BackgroundFileCancelled() -> void: + if get_node(BACKGROUND_IMAGE).text == BACKGROUND_SELECT_TEXT: + get_node(USE_EMPTY_BACKGROUND).button_pressed = true + + +func room_creator_reset() -> void: + get_node(ROOM_NAME).text = "" + get_node(GLOBAL_ID).text = "" + get_node(PLAYER_SCENE).text = PLAYER_BLANK_TEXT + get_node(SELECT_PLAYER_SCENE).visible = false + get_node(SELECT_PLAYER_SCENE_SPACER).visible = true + get_node(USE_EMPTY_PLAYER_BUTTON).button_pressed = true + get_node(ESC_SCRIPT).editable = false + get_node(ESC_SCRIPT).text = SCRIPT_BLANK_TEXT + get_node(USE_EMPTY_ROOM_SCRIPT).button_pressed = true + get_node(BACKGROUND_IMAGE).text = BACKGROUND_BLANK_TEXT + get_node(USE_EMPTY_ROOM_SPACER).visible = true + get_node(USE_EMPTY_BACKGROUND).button_pressed = true + get_node(SELECT_BACKGROUND).visible = false + get_node(SELECT_BACKGROUNDSPACER).visible = true + get_node(BACKGROUND_PREVIEW).visible = true + get_node(ROOM_BACKGROUND).visible = true + get_node(BACKGROUND_PREVIEW).texture = null + get_node(ROOM_FOLDER_PATH).text = ProjectSettings.get_setting(ROOM_PATH_SETTING) + $InformationWindows/RoomFolderDialog.current_dir = ProjectSettings.get_setting(ROOM_PATH_SETTING) + settings_modified = false + + +func _on_RoomName_text_changed(new_text: String) -> void: + get_node(GLOBAL_ID).text = new_text + settings_modified = true + + +func _on_GlobalID_text_changed(new_text: String) -> void: + settings_modified = true + + +func _on_UseEmptyPlayerButton_toggled(button_pressed: bool) -> void: + if button_pressed == true: + get_node(SELECT_PLAYER_SCENE).visible = false + get_node(SELECT_PLAYER_SCENE_SPACER).visible = true + get_node(PLAYER_SCENE).text = PLAYER_BLANK_TEXT + else: + get_node(SELECT_PLAYER_SCENE).visible = true + get_node(SELECT_PLAYER_SCENE_SPACER).visible = false + get_node(PLAYER_SCENE).text = PLAYER_SELECT_TEXT + $"InformationWindows/PlayerSceneFileDialog".popup_centered() + + +func _on_SelectPlayerScene_pressed() -> void: + $"InformationWindows/PlayerSceneFileDialog".visible = true + $"InformationWindows/PlayerSceneFileDialog".invalidate() + + +func _on_PlayerSceneFileDialog_file_selected(path: String) -> void: + settings_modified = true + get_node(PLAYER_SCENE).text = path + + +func _on_UseEmptyRoomScript_toggled(button_pressed: bool) -> void: + if button_pressed == true: + get_node(ESC_SCRIPT).editable = false + get_node(ESC_SCRIPT).text = SCRIPT_BLANK_TEXT + else: + get_node(ESC_SCRIPT).editable = true + get_node(ESC_SCRIPT).text = "%s.esc" % get_node(GLOBAL_ID).text + + +func _on_SelectRoomScript_pressed() -> void: + $"InformationWindows/ESCScriptFileDialog".visible = true + $"InformationWindows/ESCScriptFileDialog".invalidate() + + +func _on_ESCScriptFileDialog_file_selected(path: String) -> void: + settings_modified = true + get_node(ESC_SCRIPT).text = path + + +func _on_UseEmptyBackground_toggled(button_pressed: bool) -> void: + if button_pressed == true: + get_node(SELECT_BACKGROUND).visible = false + get_node(SELECT_BACKGROUNDSPACER).visible = true + get_node(BACKGROUND_IMAGE).text = BACKGROUND_BLANK_TEXT + get_node(BACKGROUND_PREVIEW).texture = null + get_node(ROOM_BACKGROUND).visible = true + else: + get_node(SELECT_BACKGROUND).visible = true + get_node(SELECT_BACKGROUNDSPACER).visible = false + get_node(BACKGROUND_IMAGE).text = BACKGROUND_SELECT_TEXT + + var viewport_centre: Vector2 = get_viewport_rect().size / 2 + var dialog_start: Vector2 = $"InformationWindows/BackgroundImageFileDialog".size / 2 + var dialog_pos: Vector2 = viewport_centre - dialog_start + $"InformationWindows/BackgroundImageFileDialog".position = dialog_pos + + $"InformationWindows/BackgroundImageFileDialog".popup_centered() + + +func _on_SelectBackground_pressed() -> void: + var viewport_centre: Vector2 = get_viewport_rect().size / 2 + var dialog_start: Vector2 = $"InformationWindows/BackgroundImageFileDialog".size / 2 + var dialog_pos: Vector2 = viewport_centre - dialog_start + $"InformationWindows/BackgroundImageFileDialog".position = dialog_pos + + $"InformationWindows/BackgroundImageFileDialog".visible = true + $"InformationWindows/BackgroundImageFileDialog".invalidate() + + +func _on_BackgroundImageFileDialog_file_selected(path: String) -> void: + settings_modified = true + + get_node(BACKGROUND_IMAGE).text = path + + var image_stream_texture:CompressedTexture2D + + image_stream_texture = load(path) + + get_node(BACKGROUND_PREVIEW).texture = image_stream_texture + get_node(ROOM_BACKGROUND).visible = false + set_preview_scale() + + +func set_preview_scale() -> void: + var preview_scale = Vector2.ONE + # Calculate the scale to make the preview as big as possible in the preview window depending on + # the height to width ratio of the frame + var preview_size = get_node(ROOM_BACKGROUND).get_size() +# get_node(BACKGROUND_PREVIEW).rect_scale = Vector2.ONE + var image_size = get_node(BACKGROUND_PREVIEW).texture.get_size() + + preview_scale.x = preview_size.x / image_size.x + preview_scale.y = preview_size.y / image_size.y + +# print("scale = "+str(preview_scale)+", preview size = "+str(preview_size)+", image_size = "+str(image_size)) + if preview_scale.y > preview_scale.x: + get_node(BACKGROUND_PREVIEW).scale = Vector2(preview_scale.x, preview_scale.x) + else: + # Image width will hit the preview boundary before the height will + get_node(BACKGROUND_PREVIEW).scale = Vector2(preview_scale.y, preview_scale.y) + + +func _on_ClearButton_pressed() -> void: + if settings_modified: + $InformationWindows/ClearConfirmationDialog.popup_centered() + + +func _on_MainMenuButton_pressed() -> void: + if settings_modified: + $InformationWindows/MainMenuConfirmationDialog.popup_centered() + else: + get_node("../Menu").visible = true + get_node("../RoomCreator").visible = false + + +func _on_ClearConfirmationDialog_confirmed() -> void: + room_creator_reset() + + +func _on_MainMenuConfirmationDialog_confirmed() -> void: + get_node("../Menu").visible = true + get_node("../RoomCreator").visible = false + + +func _on_ChangeRoomFolderButton_pressed() -> void: + $"InformationWindows/RoomFolderDialog".popup_centered() + + +func _on_RoomFolderDialog_dir_selected(dir: String) -> void: + ProjectSettings.set_setting(ROOM_PATH_SETTING, dir) + var property_info = { + "name": ROOM_PATH_SETTING, + "type": TYPE_STRING + } + ProjectSettings.add_property_info(property_info) + get_node(ROOM_FOLDER_PATH).text = dir + + +func _on_CreateButton_pressed() -> void: + var RoomName = get_node(ROOM_NAME).text + + if RoomName.length() < 1: + $"InformationWindows/GenericErrorDialog".dialog_text = "Error!\n\nRoom name must be specified." + $"InformationWindows/GenericErrorDialog".popup_centered() + return + + var ScriptName = get_node(ESC_SCRIPT).text + + if get_node(USE_EMPTY_ROOM_SCRIPT).button_pressed == false: + if ScriptName.length() < 5 or ! ScriptName.substr(ScriptName.length() - 4) == ".esc": + $"InformationWindows/GenericErrorDialog".dialog_text = "Error!\n\n" \ + + "Room ESC script must be a filename ending in '.esc'" + $"InformationWindows/GenericErrorDialog".popup_centered() + return + + if "/" in get_node(ESC_SCRIPT).text: + $"InformationWindows/GenericErrorDialog".dialog_text = "Error!\n\n" \ + + "Please remove any '/' characters from the name of the Room ESC script." + $"InformationWindows/GenericErrorDialog".popup_centered() + return + + var BaseDir = ProjectSettings.get_setting(ROOM_PATH_SETTING) + var ImageSize = Vector2(1,1) + var NewRoom = ESCRoom.new() + + NewRoom.name = RoomName + NewRoom.global_id = get_node(GLOBAL_ID).text + + if ! get_node(ESC_SCRIPT).text == SCRIPT_SELECT_TEXT and ! get_node(ESC_SCRIPT).text == SCRIPT_BLANK_TEXT: + NewRoom.esc_script = "%s/%s/scripts/%s" % [BaseDir, RoomName, get_node(ESC_SCRIPT).text] + + if ! get_node(PLAYER_SCENE).text == PLAYER_SELECT_TEXT and ! get_node(PLAYER_SCENE).text == PLAYER_BLANK_TEXT: + var player_scene = load(get_node(PLAYER_SCENE).text) + NewRoom.player_scene = player_scene + + var Background = ESCBackground.new() + Background.name = "Background" + + var BackgroundSize = Vector2.ONE + + if ! get_node(BACKGROUND_IMAGE).text == BACKGROUND_SELECT_TEXT and ! get_node(BACKGROUND_IMAGE).text == BACKGROUND_BLANK_TEXT: + Background.texture = get_node(BACKGROUND_PREVIEW).texture + BackgroundSize = Background.texture.get_size() + else: + # Set TextureRect to have the same size as the Viewport so that the room + # works even if no texture is set in the TextureRect + BackgroundSize = Vector2(ProjectSettings.get_setting("display/window/size/viewport_width"), \ + ProjectSettings.get_setting("display/window/size/viewport_height")) + Background.size = BackgroundSize + + NewRoom.add_child(Background) + + var NewTerrain = ESCTerrain.new() + NewTerrain.name = "WalkableArea" + var NewNavigationPolygonInstance = NavigationRegion2D.new() + + var NewNavigationPolygon = NavigationPolygon.new() + NewNavigationPolygonInstance.navigation_polygon = NewNavigationPolygon + + NewRoom.add_child(NewTerrain) + + NewTerrain.add_child(NewNavigationPolygonInstance) + + var Objects = Node2D.new() + Objects.name = "RoomObjects" + NewRoom.add_child(Objects) + + var StartPos = ESCLocation.new() + StartPos.name = "StartPos" + StartPos.is_start_location = true + StartPos.global_id = "%s_start_pos" % RoomName + StartPos.position = Vector2(int(BackgroundSize.x / 2), int(BackgroundSize.y / 2)) + NewRoom.add_child(StartPos) + + get_tree().edited_scene_root.add_child(NewRoom) + NewRoom.set_owner(get_tree().edited_scene_root) + NewNavigationPolygonInstance.set_owner(NewRoom) + NewTerrain.set_owner(NewRoom) + Background.set_owner(NewRoom) + Objects.set_owner(NewRoom) + StartPos.set_owner(NewRoom) + + DirAccess.make_dir_recursive_absolute("%s/%s/scripts" % [BaseDir, RoomName]) + DirAccess.make_dir_recursive_absolute("%s/%s/objects" % [BaseDir, RoomName]) + DirAccess.copy_absolute("res://addons/escoria-wizard/room_script_template.esc", "%s/%s/scripts/%s" % \ + [BaseDir, RoomName, get_node(ESC_SCRIPT).text]) + + # Export scene + var packed_scene = PackedScene.new() + packed_scene.pack(get_tree().edited_scene_root.get_node(NewRoom.name)) + + # Flag suggestions from https://godotengine.org/qa/50437/how-to-turn-a-node-into-a-packedscene-via-gdscript + ResourceSaver.save(packed_scene, "%s/%s/%s.tscn" % [BaseDir, RoomName, RoomName], \ + ResourceSaver.FLAG_CHANGE_PATH|ResourceSaver.FLAG_REPLACE_SUBRESOURCE_PATHS) + + NewRoom.queue_free() + get_tree().edited_scene_root.get_node(NewRoom.name).queue_free() + # Scan the filesystem so that the new folders show up in the file browser. + # Without this you might not see the objects/scripts folders in the filetree. + var ep = EditorPlugin.new() + ep.get_editor_interface().get_resource_filesystem().scan() + ep.free() + + $InformationWindows/CreateCompleteDialog.popup_centered() diff --git a/addons/escoria-wizard/RoomCreator.gd.uid b/addons/escoria-wizard/RoomCreator.gd.uid new file mode 100644 index 0000000..106131a --- /dev/null +++ b/addons/escoria-wizard/RoomCreator.gd.uid @@ -0,0 +1 @@ +uid://bqxah4gdmfyu6 diff --git a/addons/escoria-wizard/RoomCreator.tscn b/addons/escoria-wizard/RoomCreator.tscn new file mode 100644 index 0000000..b7a4210 --- /dev/null +++ b/addons/escoria-wizard/RoomCreator.tscn @@ -0,0 +1,278 @@ +[gd_scene load_steps=3 format=3 uid="uid://b72fr6t0ed4s0"] + +[ext_resource type="Script" uid="uid://bqxah4gdmfyu6" path="res://addons/escoria-wizard/RoomCreator.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://clm4n1qg8fidx" path="res://addons/escoria-wizard/graphics/background_preview.png" id="2"] + +[node name="CenterContainer" type="CenterContainer"] +clip_contents = true +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("1") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 + +[node name="ColorRect" type="ColorRect" parent="MarginContainer"] +layout_mode = 2 +color = Color(0.235294, 0.341176, 0.290196, 1) + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MarginContainer"] +layout_mode = 2 + +[node name="CenterContainer" type="CenterContainer" parent="MarginContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 40) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/MarginContainer/VBoxContainer/CenterContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.921569, 1, 0, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +text = "Room Creator" +uppercase = true + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(460, 200) +layout_mode = 2 + +[node name="GridContainer" type="GridContainer" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer"] +custom_minimum_size = Vector2(300, 200) +layout_mode = 2 +theme_override_constants/h_separation = 10 +columns = 4 + +[node name="RoomNameLabel" type="Label" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Room name:" + +[node name="RoomName" type="LineEdit" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +caret_blink = true +caret_blink_interval = 0.5 + +[node name="Spacer3" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 + +[node name="Spacer6" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 + +[node name="GlobalIDLabel" type="Label" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Global ID:" + +[node name="GlobalID" type="LineEdit" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +caret_blink = true +caret_blink_interval = 0.5 + +[node name="Spacer4" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 + +[node name="Spacer7" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 + +[node name="PlayerSceneLabel" type="Label" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Player scene:" + +[node name="PlayerScene" type="LineEdit" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Scene will be left blank." +editable = false +caret_blink = true +caret_blink_interval = 0.5 + +[node name="UseEmptyPlayerButton" type="CheckButton" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Use empty player scene" + +[node name="SelectPlayerScene" type="Button" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +visible = false +layout_mode = 2 +text = "Select Player Scene" + +[node name="SelectPlayerSceneSpacer" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 + +[node name="ESCScriptLabel" type="Label" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Room ESC script:" + +[node name="ESCScript" type="LineEdit" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +tooltip_text = "In the room's scripts folder +an empty file will be created +with this name." +text = "Room will not have a script configured." +editable = false +caret_blink = true +caret_blink_interval = 0.5 + +[node name="UseEmptyRoomScript" type="CheckButton" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "No script" + +[node name="UseEmptyRoomSpacer" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 + +[node name="BackgroundImageLabel" type="Label" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Background image:" + +[node name="BackgroundImage" type="LineEdit" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Image will be left blank." +editable = false +caret_blink = true +caret_blink_interval = 0.5 + +[node name="UseEmptyBackground" type="CheckButton" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Use empty background" + +[node name="SelectBackground" type="Button" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +visible = false +layout_mode = 2 +text = "Select Background" + +[node name="SelectBackgroundSpacer" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 + +[node name="RoomFolderLabel" type="Label" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Room folder parent:" + +[node name="RoomFolder" type="LineEdit" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +tooltip_text = "A folder () for your room will be created inside this parent folder." +text = "res://game/rooms" +editable = false +caret_blink = true +caret_blink_interval = 0.5 + +[node name="ChangeRoomFolderButton" type="Button" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +layout_mode = 2 +text = "Change Room Folder" + +[node name="Spacer8" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer"] +custom_minimum_size = Vector2(40, 0) +layout_mode = 2 + +[node name="PreviewSection" type="MarginContainer" parent="MarginContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(1000, 400) +layout_mode = 2 + +[node name="PreviewFrame" type="ColorRect" parent="MarginContainer/MarginContainer/VBoxContainer/PreviewSection"] +custom_minimum_size = Vector2(1000, 400) +layout_mode = 2 +color = Color(0, 0, 0, 1) + +[node name="CenterContainer" type="MarginContainer" parent="MarginContainer/MarginContainer/VBoxContainer/PreviewSection"] +layout_mode = 2 + +[node name="RoomBackground" type="TextureRect" parent="MarginContainer/MarginContainer/VBoxContainer/PreviewSection/CenterContainer"] +custom_minimum_size = Vector2(990, 390) +layout_mode = 2 +texture = ExtResource("2") +expand_mode = 1 +stretch_mode = 7 + +[node name="Control" type="Control" parent="MarginContainer/MarginContainer/VBoxContainer/PreviewSection"] +layout_mode = 2 + +[node name="BackgroundPreview" type="TextureRect" parent="MarginContainer/MarginContainer/VBoxContainer/PreviewSection/Control"] +layout_mode = 0 +offset_right = 1000.0 +offset_bottom = 400.0 + +[node name="MarginContainer2" type="MarginContainer" parent="MarginContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 80) +layout_mode = 2 + +[node name="CenterContainer" type="CenterContainer" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer2"] +custom_minimum_size = Vector2(800, 80) +layout_mode = 2 + +[node name="GridContainer" type="GridContainer" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer2/CenterContainer"] +custom_minimum_size = Vector2(300, 40) +layout_mode = 2 +theme_override_constants/h_separation = 40 +columns = 3 + +[node name="CreateButton" type="Button" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer2/CenterContainer/GridContainer"] +layout_mode = 2 +text = "Create Room" + +[node name="ClearButton" type="Button" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer2/CenterContainer/GridContainer"] +layout_mode = 2 +text = "Clear" + +[node name="MainMenuButton" type="Button" parent="MarginContainer/MarginContainer/VBoxContainer/MarginContainer2/CenterContainer/GridContainer"] +layout_mode = 2 +text = "Main Menu" + +[node name="InformationWindows" type="Control" parent="."] +layout_mode = 2 + +[node name="PlayerSceneFileDialog" type="FileDialog" parent="InformationWindows"] +filters = PackedStringArray("*.tscn, *.scn ; Scene Files") + +[node name="ESCScriptFileDialog" type="FileDialog" parent="InformationWindows"] +filters = PackedStringArray("*.esc; Escoria script files") + +[node name="BackgroundImageFileDialog" type="FileDialog" parent="InformationWindows"] +filters = PackedStringArray("*.png, *.jpg, *.jpeg ; Supported Images") + +[node name="ClearConfirmationDialog" type="ConfirmationDialog" parent="InformationWindows"] +dialog_text = "WARNING! + +If you continue you will lose the current room setup." + +[node name="MainMenuConfirmationDialog" type="ConfirmationDialog" parent="InformationWindows"] +dialog_text = "WARNING! + +If you continue you will lose the current room setup." + +[node name="GenericErrorDialog" type="AcceptDialog" parent="InformationWindows"] +dialog_text = "ERROR! + +Please supply a name for the room." + +[node name="CreateCompleteDialog" type="AcceptDialog" parent="InformationWindows"] +dialog_text = "The room has been created." + +[node name="RoomFolderDialog" type="FileDialog" parent="InformationWindows"] +mode = 2 + +[connection signal="text_changed" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/RoomName" to="." method="_on_RoomName_text_changed"] +[connection signal="text_changed" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/GlobalID" to="." method="_on_GlobalID_text_changed"] +[connection signal="toggled" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/UseEmptyPlayerButton" to="." method="_on_UseEmptyPlayerButton_toggled"] +[connection signal="pressed" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/SelectPlayerScene" to="." method="_on_SelectPlayerScene_pressed"] +[connection signal="toggled" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/UseEmptyRoomScript" to="." method="_on_UseEmptyRoomScript_toggled"] +[connection signal="toggled" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/UseEmptyBackground" to="." method="_on_UseEmptyBackground_toggled"] +[connection signal="pressed" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/SelectBackground" to="." method="_on_SelectBackground_pressed"] +[connection signal="pressed" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer/GridContainer/ChangeRoomFolderButton" to="." method="_on_ChangeRoomFolderButton_pressed"] +[connection signal="pressed" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer2/CenterContainer/GridContainer/CreateButton" to="." method="_on_CreateButton_pressed"] +[connection signal="pressed" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer2/CenterContainer/GridContainer/ClearButton" to="." method="_on_ClearButton_pressed"] +[connection signal="pressed" from="MarginContainer/MarginContainer/VBoxContainer/MarginContainer2/CenterContainer/GridContainer/MainMenuButton" to="." method="_on_MainMenuButton_pressed"] +[connection signal="file_selected" from="InformationWindows/PlayerSceneFileDialog" to="." method="_on_PlayerSceneFileDialog_file_selected"] +[connection signal="file_selected" from="InformationWindows/ESCScriptFileDialog" to="." method="_on_ESCScriptFileDialog_file_selected"] +[connection signal="file_selected" from="InformationWindows/BackgroundImageFileDialog" to="." method="_on_BackgroundImageFileDialog_file_selected"] +[connection signal="confirmed" from="InformationWindows/ClearConfirmationDialog" to="." method="_on_ClearConfirmationDialog_confirmed"] +[connection signal="confirmed" from="InformationWindows/MainMenuConfirmationDialog" to="." method="_on_MainMenuConfirmationDialog_confirmed"] +[connection signal="dir_selected" from="InformationWindows/RoomFolderDialog" to="." method="_on_RoomFolderDialog_dir_selected"] diff --git a/addons/escoria-wizard/background.pdn b/addons/escoria-wizard/background.pdn new file mode 100644 index 0000000..9362b68 Binary files /dev/null and b/addons/escoria-wizard/background.pdn differ diff --git a/addons/escoria-wizard/draw_frame_rects.gd b/addons/escoria-wizard/draw_frame_rects.gd new file mode 100644 index 0000000..5d1ddc1 --- /dev/null +++ b/addons/escoria-wizard/draw_frame_rects.gd @@ -0,0 +1,44 @@ +@tool +extends Control + +const grid_colour = Color.BROWN +const highlight_colour = Color.GOLD #fuchsia + +# Number of rows to break the spritesheet into +@export var total_num_rows:int +# Number of columns to break the spritesheet into +@export var total_num_columns:int +# First animation cell. Boxes will be drawn around every cell from the start_cell to end_cell. +@export var start_cell:int +# Last animation cell. Boxes will be drawn around every cell from the start_cell to end_cell. +@export var end_cell:int +# The size of each animation cell in pixels. +@export var cell_size:Vector2 +# Zoom factor +@export var zoom_factor:float + +# This function will calculate where on the spritesheet the start and end frames for an animation +# are, and draw boxes around all used frames. The visual indicator makes frame selection easier. +func _draw() -> void: + if start_cell > 0: + # Draw grid for all frames in the spritesheet + for yloop in range(total_num_rows): + for xloop in range(total_num_columns): + var corner1 = Vector2(xloop * cell_size.x, yloop * cell_size.y) + var corner2 = Vector2(corner1.x + cell_size.x, corner1.y + cell_size.y) + draw_line(Vector2(corner1.x * zoom_factor, corner1.y * zoom_factor), Vector2(corner2.x * zoom_factor, corner1.y * zoom_factor), grid_colour, 2.0) + draw_line(Vector2(corner1.x * zoom_factor, corner1.y * zoom_factor), Vector2(corner1.x * zoom_factor, corner2.y * zoom_factor), grid_colour, 2.0) + draw_line(Vector2(corner1.x * zoom_factor, corner2.y * zoom_factor), Vector2(corner2.x * zoom_factor, corner2.y * zoom_factor), grid_colour, 2.0) + draw_line(Vector2(corner2.x * zoom_factor, corner1.y * zoom_factor), Vector2(corner2.x * zoom_factor, corner2.y * zoom_factor), grid_colour, 2.0) + + # Highlight selected frames in the spritesheet + for loop in range(end_cell - start_cell + 1): + var current_cell = start_cell + loop + var frame_row = (current_cell - 1) / total_num_columns + var frame_column = (current_cell - 1) % total_num_columns + var corner1 = Vector2(frame_column * cell_size.x, frame_row * cell_size.y) + var corner2 = Vector2(corner1.x + cell_size.x, corner1.y + cell_size.y) + draw_line(Vector2(corner1.x * zoom_factor, corner1.y * zoom_factor), Vector2(corner2.x * zoom_factor, corner1.y * zoom_factor), highlight_colour, 2.0) + draw_line(Vector2(corner1.x * zoom_factor, corner1.y * zoom_factor), Vector2(corner1.x * zoom_factor, corner2.y * zoom_factor), highlight_colour, 2.0) + draw_line(Vector2(corner1.x * zoom_factor, corner2.y * zoom_factor), Vector2(corner2.x * zoom_factor, corner2.y * zoom_factor), highlight_colour, 2.0) + draw_line(Vector2(corner2.x * zoom_factor, corner1.y * zoom_factor), Vector2(corner2.x * zoom_factor, corner2.y * zoom_factor), highlight_colour, 2.0) diff --git a/addons/escoria-wizard/draw_frame_rects.gd.uid b/addons/escoria-wizard/draw_frame_rects.gd.uid new file mode 100644 index 0000000..2127ae0 --- /dev/null +++ b/addons/escoria-wizard/draw_frame_rects.gd.uid @@ -0,0 +1 @@ +uid://ihbrgvupidxn diff --git a/addons/escoria-wizard/escoria_wizard.gd b/addons/escoria-wizard/escoria_wizard.gd new file mode 100644 index 0000000..9f7b151 --- /dev/null +++ b/addons/escoria-wizard/escoria_wizard.gd @@ -0,0 +1,43 @@ +@tool +extends Control + + +const GENERIC_ERROR_NODE = "Menu/InformationWindows/generic_error_window" + + +# This variable is set by plugin.gd and used to allow the plugin to interact with the Godot +# editor (open the ESCPlayer scene in the editor once it's created) +var plugin_reference + + +func _ready() -> void: + for section_loop in ["RoomCreator", "CharacterCreator", "ItemCreator"]: + get_node(section_loop).visible = false + + $Menu.visible = true + + +func NewGame_pressed() -> void: + pass # Replace with function body. + + +func NewRoom_pressed() -> void: + $Menu.visible = false + $RoomCreator.visible = true + $RoomCreator.room_creator_reset() + + +func CharacterCreator_pressed() -> void: + $Menu.visible = false + $CharacterCreator.visible = true + + if plugin_reference == null: + get_node(GENERIC_ERROR_NODE).dialog_text = "Warning!\n\nExporting your character will fail when\n" + get_node(GENERIC_ERROR_NODE).dialog_text += "running the character creator directly rather than\n" + get_node(GENERIC_ERROR_NODE).dialog_text += "as a plugin.\n\nPlease open this as a plugin." + get_node(GENERIC_ERROR_NODE).popup() + + +func InventoryItem_pressed() -> void: + $Menu.visible = false + $ItemCreator.visible = true diff --git a/addons/escoria-wizard/escoria_wizard.gd.uid b/addons/escoria-wizard/escoria_wizard.gd.uid new file mode 100644 index 0000000..3000ee7 --- /dev/null +++ b/addons/escoria-wizard/escoria_wizard.gd.uid @@ -0,0 +1 @@ +uid://c21ngbfgf3gcp diff --git a/addons/escoria-wizard/escoria_wizard.tscn b/addons/escoria-wizard/escoria_wizard.tscn new file mode 100644 index 0000000..aecc2fe --- /dev/null +++ b/addons/escoria-wizard/escoria_wizard.tscn @@ -0,0 +1,83 @@ +[gd_scene load_steps=9 format=3 uid="uid://ccuroi47r5yrl"] + +[ext_resource type="Texture2D" uid="uid://crv5q2cm67trm" path="res://addons/escoria-wizard/graphics/background.png" id="1"] +[ext_resource type="PackedScene" uid="uid://cmd72pi6txw2v" path="res://addons/escoria-wizard/CharacterCreator.tscn" id="2"] +[ext_resource type="PackedScene" uid="uid://beawklamapteq" path="res://addons/escoria-wizard/ItemCreator.tscn" id="3"] +[ext_resource type="PackedScene" uid="uid://b72fr6t0ed4s0" path="res://addons/escoria-wizard/RoomCreator.tscn" id="4"] +[ext_resource type="Script" uid="uid://c21ngbfgf3gcp" path="res://addons/escoria-wizard/escoria_wizard.gd" id="38"] +[ext_resource type="Texture2D" uid="uid://5oseq417h0ch" path="res://addons/escoria-wizard/graphics/character.png" id="40"] +[ext_resource type="Texture2D" uid="uid://6va32hw42yv" path="res://addons/escoria-wizard/graphics/room.png" id="42"] +[ext_resource type="Texture2D" uid="uid://cglfsrifcc8is" path="res://addons/escoria-wizard/graphics/inventory.png" id="44"] + +[node name="root" type="MarginContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = 10.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("38") + +[node name="background" type="TextureRect" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +texture = ExtResource("1") +expand_mode = 1 +stretch_mode = 1 + +[node name="Menu" type="MarginContainer" parent="."] +layout_mode = 2 + +[node name="CenterContainer" type="CenterContainer" parent="Menu"] +layout_mode = 2 + +[node name="GridContainer" type="GridContainer" parent="Menu/CenterContainer"] +layout_mode = 2 +theme_override_constants/v_separation = 20 + +[node name="NewRoom" type="Button" parent="Menu/CenterContainer/GridContainer"] +layout_mode = 2 +text = "Room Creator" +icon = ExtResource("42") + +[node name="CharacterCreator" type="Button" parent="Menu/CenterContainer/GridContainer"] +layout_mode = 2 +theme_override_constants/h_separation = 5 +text = "Character Creator" +icon = ExtResource("40") + +[node name="InventoryItem" type="Button" parent="Menu/CenterContainer/GridContainer"] +layout_mode = 2 +theme_override_constants/h_separation = 5 +text = "Item Creator" +icon = ExtResource("44") + +[node name="InformationWindows" type="Control" parent="Menu"] +visible = false +layout_mode = 2 + +[node name="generic_error_window" type="AcceptDialog" parent="Menu/InformationWindows"] +dialog_text = "Warning! + +Exporting your character will fail when +running the character creator directly rather than +as a plugin. + +Please open this as a plugin." + +[node name="RoomCreator" parent="." instance=ExtResource("4")] +visible = false +layout_mode = 2 + +[node name="CharacterCreator" parent="." instance=ExtResource("2")] +visible = false +layout_mode = 2 + +[node name="ItemCreator" parent="." instance=ExtResource("3")] +visible = false +layout_mode = 2 + +[connection signal="pressed" from="Menu/CenterContainer/GridContainer/NewRoom" to="." method="NewRoom_pressed"] +[connection signal="pressed" from="Menu/CenterContainer/GridContainer/CharacterCreator" to="." method="CharacterCreator_pressed"] +[connection signal="pressed" from="Menu/CenterContainer/GridContainer/InventoryItem" to="." method="InventoryItem_pressed"] diff --git a/addons/escoria-wizard/graphics/background.png b/addons/escoria-wizard/graphics/background.png new file mode 100644 index 0000000..94f6f92 Binary files /dev/null and b/addons/escoria-wizard/graphics/background.png differ diff --git a/addons/escoria-wizard/graphics/background.png.import b/addons/escoria-wizard/graphics/background.png.import new file mode 100644 index 0000000..ef76619 --- /dev/null +++ b/addons/escoria-wizard/graphics/background.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://6vprioalojku" +path="res://.godot/imported/background.png-673f19ac242599fde9866584d38ae81e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/background.png" +dest_files=["res://.godot/imported/background.png-673f19ac242599fde9866584d38ae81e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/background_preview.png b/addons/escoria-wizard/graphics/background_preview.png new file mode 100644 index 0000000..8ba4242 Binary files /dev/null and b/addons/escoria-wizard/graphics/background_preview.png differ diff --git a/addons/escoria-wizard/graphics/background_preview.png.import b/addons/escoria-wizard/graphics/background_preview.png.import new file mode 100644 index 0000000..6cc6be3 --- /dev/null +++ b/addons/escoria-wizard/graphics/background_preview.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b3wba8cg2si2b" +path="res://.godot/imported/background_preview.png-0d4e493f97e841e13a6461de66cb0ca8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/background_preview.png" +dest_files=["res://.godot/imported/background_preview.png-0d4e493f97e841e13a6461de66cb0ca8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/character.png b/addons/escoria-wizard/graphics/character.png new file mode 100644 index 0000000..11b0971 Binary files /dev/null and b/addons/escoria-wizard/graphics/character.png differ diff --git a/addons/escoria-wizard/graphics/character.png.import b/addons/escoria-wizard/graphics/character.png.import new file mode 100644 index 0000000..6ed9e12 --- /dev/null +++ b/addons/escoria-wizard/graphics/character.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cfboyueupkk6q" +path="res://.godot/imported/character.png-3bba517b2bbcff3cb6de715fbe25ac09.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/character.png" +dest_files=["res://.godot/imported/character.png-3bba517b2bbcff3cb6de715fbe25ac09.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_down.png b/addons/escoria-wizard/graphics/darkgreenarrow_down.png new file mode 100644 index 0000000..7c10740 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreenarrow_down.png differ diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_down.png.import b/addons/escoria-wizard/graphics/darkgreenarrow_down.png.import new file mode 100644 index 0000000..99e1d18 --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreenarrow_down.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://waj4y8264r16" +path="res://.godot/imported/darkgreenarrow_down.png-f80fef66366144c9912fd06af371b3cd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreenarrow_down.png" +dest_files=["res://.godot/imported/darkgreenarrow_down.png-f80fef66366144c9912fd06af371b3cd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_downleft.png b/addons/escoria-wizard/graphics/darkgreenarrow_downleft.png new file mode 100644 index 0000000..df369b6 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreenarrow_downleft.png differ diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_downleft.png.import b/addons/escoria-wizard/graphics/darkgreenarrow_downleft.png.import new file mode 100644 index 0000000..e35954d --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreenarrow_downleft.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bydfudl5urtjg" +path="res://.godot/imported/darkgreenarrow_downleft.png-9c941c084005f1f822625fd654248365.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreenarrow_downleft.png" +dest_files=["res://.godot/imported/darkgreenarrow_downleft.png-9c941c084005f1f822625fd654248365.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_downright.png b/addons/escoria-wizard/graphics/darkgreenarrow_downright.png new file mode 100644 index 0000000..bf24e3d Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreenarrow_downright.png differ diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_downright.png.import b/addons/escoria-wizard/graphics/darkgreenarrow_downright.png.import new file mode 100644 index 0000000..456b70e --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreenarrow_downright.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dfvo7e0v88w6v" +path="res://.godot/imported/darkgreenarrow_downright.png-8d44f42e48c22a568cc497bf0454add7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreenarrow_downright.png" +dest_files=["res://.godot/imported/darkgreenarrow_downright.png-8d44f42e48c22a568cc497bf0454add7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_left.png b/addons/escoria-wizard/graphics/darkgreenarrow_left.png new file mode 100644 index 0000000..8ab0141 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreenarrow_left.png differ diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_left.png.import b/addons/escoria-wizard/graphics/darkgreenarrow_left.png.import new file mode 100644 index 0000000..11c87a6 --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreenarrow_left.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dldiv8oq1umr7" +path="res://.godot/imported/darkgreenarrow_left.png-be610e9bb418b027126d43ef1100ef24.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreenarrow_left.png" +dest_files=["res://.godot/imported/darkgreenarrow_left.png-be610e9bb418b027126d43ef1100ef24.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_right.png b/addons/escoria-wizard/graphics/darkgreenarrow_right.png new file mode 100644 index 0000000..7716a28 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreenarrow_right.png differ diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_right.png.import b/addons/escoria-wizard/graphics/darkgreenarrow_right.png.import new file mode 100644 index 0000000..03285a7 --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreenarrow_right.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cgb0uo0gw8q7k" +path="res://.godot/imported/darkgreenarrow_right.png-7a31735948963e3b04973e75c9a268cc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreenarrow_right.png" +dest_files=["res://.godot/imported/darkgreenarrow_right.png-7a31735948963e3b04973e75c9a268cc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_up.png b/addons/escoria-wizard/graphics/darkgreenarrow_up.png new file mode 100644 index 0000000..c4637e8 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreenarrow_up.png differ diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_up.png.import b/addons/escoria-wizard/graphics/darkgreenarrow_up.png.import new file mode 100644 index 0000000..40a25c1 --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreenarrow_up.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnpgsl3qm35mp" +path="res://.godot/imported/darkgreenarrow_up.png-9b7f9cc898d46e982665a411d11df2d6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreenarrow_up.png" +dest_files=["res://.godot/imported/darkgreenarrow_up.png-9b7f9cc898d46e982665a411d11df2d6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_upleft.png b/addons/escoria-wizard/graphics/darkgreenarrow_upleft.png new file mode 100644 index 0000000..072f8ee Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreenarrow_upleft.png differ diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_upleft.png.import b/addons/escoria-wizard/graphics/darkgreenarrow_upleft.png.import new file mode 100644 index 0000000..5053772 --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreenarrow_upleft.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cy3vbgib4vsux" +path="res://.godot/imported/darkgreenarrow_upleft.png-48b91cfd1691287e7a3031caca0106f7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreenarrow_upleft.png" +dest_files=["res://.godot/imported/darkgreenarrow_upleft.png-48b91cfd1691287e7a3031caca0106f7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_upright.png b/addons/escoria-wizard/graphics/darkgreenarrow_upright.png new file mode 100644 index 0000000..f36a441 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreenarrow_upright.png differ diff --git a/addons/escoria-wizard/graphics/darkgreenarrow_upright.png.import b/addons/escoria-wizard/graphics/darkgreenarrow_upright.png.import new file mode 100644 index 0000000..18350ee --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreenarrow_upright.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cqp6080nc8p4n" +path="res://.godot/imported/darkgreenarrow_upright.png-7dbab373b347061d5531fa350c8a4922.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreenarrow_upright.png" +dest_files=["res://.godot/imported/darkgreenarrow_upright.png-7dbab373b347061d5531fa350c8a4922.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_d.png b/addons/escoria-wizard/graphics/darkgreyarrow_d.png new file mode 100644 index 0000000..756fb2d Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreyarrow_d.png differ diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_d.png.import b/addons/escoria-wizard/graphics/darkgreyarrow_d.png.import new file mode 100644 index 0000000..5ba9968 --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreyarrow_d.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bqj8vbp0qd3cb" +path="res://.godot/imported/darkgreyarrow_d.png-275b31a26265ef370a60ffacf531214f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreyarrow_d.png" +dest_files=["res://.godot/imported/darkgreyarrow_d.png-275b31a26265ef370a60ffacf531214f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_dl.png b/addons/escoria-wizard/graphics/darkgreyarrow_dl.png new file mode 100644 index 0000000..66bfea0 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreyarrow_dl.png differ diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_dl.png.import b/addons/escoria-wizard/graphics/darkgreyarrow_dl.png.import new file mode 100644 index 0000000..9fa5a4f --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreyarrow_dl.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b1yrfcgoqfdj8" +path="res://.godot/imported/darkgreyarrow_dl.png-5ff8764b0a1297d1139950acf8bbfbdc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreyarrow_dl.png" +dest_files=["res://.godot/imported/darkgreyarrow_dl.png-5ff8764b0a1297d1139950acf8bbfbdc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_dr.png b/addons/escoria-wizard/graphics/darkgreyarrow_dr.png new file mode 100644 index 0000000..d8174c4 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreyarrow_dr.png differ diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_dr.png.import b/addons/escoria-wizard/graphics/darkgreyarrow_dr.png.import new file mode 100644 index 0000000..c03ab25 --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreyarrow_dr.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dq7m0m7k7wmf8" +path="res://.godot/imported/darkgreyarrow_dr.png-a506c0b8ac50445c5b0b1a15a553ac12.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreyarrow_dr.png" +dest_files=["res://.godot/imported/darkgreyarrow_dr.png-a506c0b8ac50445c5b0b1a15a553ac12.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_l.png b/addons/escoria-wizard/graphics/darkgreyarrow_l.png new file mode 100644 index 0000000..42e28e5 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreyarrow_l.png differ diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_l.png.import b/addons/escoria-wizard/graphics/darkgreyarrow_l.png.import new file mode 100644 index 0000000..ed2028b --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreyarrow_l.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dxble0bnnrive" +path="res://.godot/imported/darkgreyarrow_l.png-b088f83855b02b1141bd62415c790e90.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreyarrow_l.png" +dest_files=["res://.godot/imported/darkgreyarrow_l.png-b088f83855b02b1141bd62415c790e90.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_r.png b/addons/escoria-wizard/graphics/darkgreyarrow_r.png new file mode 100644 index 0000000..9ef0036 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreyarrow_r.png differ diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_r.png.import b/addons/escoria-wizard/graphics/darkgreyarrow_r.png.import new file mode 100644 index 0000000..2abb107 --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreyarrow_r.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://glnalpy7omjy" +path="res://.godot/imported/darkgreyarrow_r.png-8d0c752a10f0c09580b2cf12f3efe6e9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreyarrow_r.png" +dest_files=["res://.godot/imported/darkgreyarrow_r.png-8d0c752a10f0c09580b2cf12f3efe6e9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_u.png b/addons/escoria-wizard/graphics/darkgreyarrow_u.png new file mode 100644 index 0000000..e0e85fc Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreyarrow_u.png differ diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_u.png.import b/addons/escoria-wizard/graphics/darkgreyarrow_u.png.import new file mode 100644 index 0000000..ba63fdf --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreyarrow_u.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dvvb52w2u7wpk" +path="res://.godot/imported/darkgreyarrow_u.png-cc74f39c28ae1810033392999d04a5bf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreyarrow_u.png" +dest_files=["res://.godot/imported/darkgreyarrow_u.png-cc74f39c28ae1810033392999d04a5bf.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_ul.png b/addons/escoria-wizard/graphics/darkgreyarrow_ul.png new file mode 100644 index 0000000..1331b46 Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreyarrow_ul.png differ diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_ul.png.import b/addons/escoria-wizard/graphics/darkgreyarrow_ul.png.import new file mode 100644 index 0000000..62d0efc --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreyarrow_ul.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://css60bdem1app" +path="res://.godot/imported/darkgreyarrow_ul.png-acc2b674d445717ec73baad13027bad1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreyarrow_ul.png" +dest_files=["res://.godot/imported/darkgreyarrow_ul.png-acc2b674d445717ec73baad13027bad1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_ur.png b/addons/escoria-wizard/graphics/darkgreyarrow_ur.png new file mode 100644 index 0000000..0b0ef7a Binary files /dev/null and b/addons/escoria-wizard/graphics/darkgreyarrow_ur.png differ diff --git a/addons/escoria-wizard/graphics/darkgreyarrow_ur.png.import b/addons/escoria-wizard/graphics/darkgreyarrow_ur.png.import new file mode 100644 index 0000000..eff0a3b --- /dev/null +++ b/addons/escoria-wizard/graphics/darkgreyarrow_ur.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ctj1beip4p24w" +path="res://.godot/imported/darkgreyarrow_ur.png-c57e3f20c2619cbb79b5314ef4fc447d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/darkgreyarrow_ur.png" +dest_files=["res://.godot/imported/darkgreyarrow_ur.png-c57e3f20c2619cbb79b5314ef4fc447d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greenarrow_down.png b/addons/escoria-wizard/graphics/greenarrow_down.png new file mode 100644 index 0000000..5610a96 Binary files /dev/null and b/addons/escoria-wizard/graphics/greenarrow_down.png differ diff --git a/addons/escoria-wizard/graphics/greenarrow_down.png.import b/addons/escoria-wizard/graphics/greenarrow_down.png.import new file mode 100644 index 0000000..8d41251 --- /dev/null +++ b/addons/escoria-wizard/graphics/greenarrow_down.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://0jiimpilb3im" +path="res://.godot/imported/greenarrow_down.png-cffa186cded1270a61e8f78415841946.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greenarrow_down.png" +dest_files=["res://.godot/imported/greenarrow_down.png-cffa186cded1270a61e8f78415841946.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greenarrow_downleft.png b/addons/escoria-wizard/graphics/greenarrow_downleft.png new file mode 100644 index 0000000..248f29f Binary files /dev/null and b/addons/escoria-wizard/graphics/greenarrow_downleft.png differ diff --git a/addons/escoria-wizard/graphics/greenarrow_downleft.png.import b/addons/escoria-wizard/graphics/greenarrow_downleft.png.import new file mode 100644 index 0000000..647b15a --- /dev/null +++ b/addons/escoria-wizard/graphics/greenarrow_downleft.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2kfayf3hmc4f" +path="res://.godot/imported/greenarrow_downleft.png-6fe6648304abb812f0dc3ab8022ab9a6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greenarrow_downleft.png" +dest_files=["res://.godot/imported/greenarrow_downleft.png-6fe6648304abb812f0dc3ab8022ab9a6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greenarrow_downright.png b/addons/escoria-wizard/graphics/greenarrow_downright.png new file mode 100644 index 0000000..a84d0b6 Binary files /dev/null and b/addons/escoria-wizard/graphics/greenarrow_downright.png differ diff --git a/addons/escoria-wizard/graphics/greenarrow_downright.png.import b/addons/escoria-wizard/graphics/greenarrow_downright.png.import new file mode 100644 index 0000000..d190999 --- /dev/null +++ b/addons/escoria-wizard/graphics/greenarrow_downright.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://6asacbpwcsrs" +path="res://.godot/imported/greenarrow_downright.png-2333c6b1bda44cace5db5e42e2b0e9c5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greenarrow_downright.png" +dest_files=["res://.godot/imported/greenarrow_downright.png-2333c6b1bda44cace5db5e42e2b0e9c5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greenarrow_left.png b/addons/escoria-wizard/graphics/greenarrow_left.png new file mode 100644 index 0000000..dfba2e9 Binary files /dev/null and b/addons/escoria-wizard/graphics/greenarrow_left.png differ diff --git a/addons/escoria-wizard/graphics/greenarrow_left.png.import b/addons/escoria-wizard/graphics/greenarrow_left.png.import new file mode 100644 index 0000000..c0fa796 --- /dev/null +++ b/addons/escoria-wizard/graphics/greenarrow_left.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnauos6ctby8u" +path="res://.godot/imported/greenarrow_left.png-3a8b427699a856e7d2799cd1aaae9a94.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greenarrow_left.png" +dest_files=["res://.godot/imported/greenarrow_left.png-3a8b427699a856e7d2799cd1aaae9a94.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greenarrow_right.png b/addons/escoria-wizard/graphics/greenarrow_right.png new file mode 100644 index 0000000..80827ac Binary files /dev/null and b/addons/escoria-wizard/graphics/greenarrow_right.png differ diff --git a/addons/escoria-wizard/graphics/greenarrow_right.png.import b/addons/escoria-wizard/graphics/greenarrow_right.png.import new file mode 100644 index 0000000..ceb0164 --- /dev/null +++ b/addons/escoria-wizard/graphics/greenarrow_right.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cil6i74lw86lx" +path="res://.godot/imported/greenarrow_right.png-a7660124d784cce0d8d7f77382ad0dcf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greenarrow_right.png" +dest_files=["res://.godot/imported/greenarrow_right.png-a7660124d784cce0d8d7f77382ad0dcf.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greenarrow_up.png b/addons/escoria-wizard/graphics/greenarrow_up.png new file mode 100644 index 0000000..6d94311 Binary files /dev/null and b/addons/escoria-wizard/graphics/greenarrow_up.png differ diff --git a/addons/escoria-wizard/graphics/greenarrow_up.png.import b/addons/escoria-wizard/graphics/greenarrow_up.png.import new file mode 100644 index 0000000..09287f5 --- /dev/null +++ b/addons/escoria-wizard/graphics/greenarrow_up.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dkbfrmqfpnwkn" +path="res://.godot/imported/greenarrow_up.png-b8c687a40e9ed8e8ca4b9f523c758680.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greenarrow_up.png" +dest_files=["res://.godot/imported/greenarrow_up.png-b8c687a40e9ed8e8ca4b9f523c758680.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greenarrow_upleft.png b/addons/escoria-wizard/graphics/greenarrow_upleft.png new file mode 100644 index 0000000..81e505f Binary files /dev/null and b/addons/escoria-wizard/graphics/greenarrow_upleft.png differ diff --git a/addons/escoria-wizard/graphics/greenarrow_upleft.png.import b/addons/escoria-wizard/graphics/greenarrow_upleft.png.import new file mode 100644 index 0000000..6760562 --- /dev/null +++ b/addons/escoria-wizard/graphics/greenarrow_upleft.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cpafptpjqghk7" +path="res://.godot/imported/greenarrow_upleft.png-399bc9a1dba0df13a24832ec2b5ce4a0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greenarrow_upleft.png" +dest_files=["res://.godot/imported/greenarrow_upleft.png-399bc9a1dba0df13a24832ec2b5ce4a0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greenarrow_upright.png b/addons/escoria-wizard/graphics/greenarrow_upright.png new file mode 100644 index 0000000..d65c24b Binary files /dev/null and b/addons/escoria-wizard/graphics/greenarrow_upright.png differ diff --git a/addons/escoria-wizard/graphics/greenarrow_upright.png.import b/addons/escoria-wizard/graphics/greenarrow_upright.png.import new file mode 100644 index 0000000..003d077 --- /dev/null +++ b/addons/escoria-wizard/graphics/greenarrow_upright.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dlkymayjd131l" +path="res://.godot/imported/greenarrow_upright.png-22f2ff1b03389bea31ce47ef57342806.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greenarrow_upright.png" +dest_files=["res://.godot/imported/greenarrow_upright.png-22f2ff1b03389bea31ce47ef57342806.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greyarrow_d.png b/addons/escoria-wizard/graphics/greyarrow_d.png new file mode 100644 index 0000000..08d46e9 Binary files /dev/null and b/addons/escoria-wizard/graphics/greyarrow_d.png differ diff --git a/addons/escoria-wizard/graphics/greyarrow_d.png.import b/addons/escoria-wizard/graphics/greyarrow_d.png.import new file mode 100644 index 0000000..79d6458 --- /dev/null +++ b/addons/escoria-wizard/graphics/greyarrow_d.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ckasjwjvy1516" +path="res://.godot/imported/greyarrow_d.png-b4e1334be4cbe80f6b2540a1dcfd993f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greyarrow_d.png" +dest_files=["res://.godot/imported/greyarrow_d.png-b4e1334be4cbe80f6b2540a1dcfd993f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greyarrow_dl.png b/addons/escoria-wizard/graphics/greyarrow_dl.png new file mode 100644 index 0000000..7653aef Binary files /dev/null and b/addons/escoria-wizard/graphics/greyarrow_dl.png differ diff --git a/addons/escoria-wizard/graphics/greyarrow_dl.png.import b/addons/escoria-wizard/graphics/greyarrow_dl.png.import new file mode 100644 index 0000000..b676cea --- /dev/null +++ b/addons/escoria-wizard/graphics/greyarrow_dl.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dhxnw7mr45s0n" +path="res://.godot/imported/greyarrow_dl.png-9cd21dea05fbbe8678af68dea7e46990.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greyarrow_dl.png" +dest_files=["res://.godot/imported/greyarrow_dl.png-9cd21dea05fbbe8678af68dea7e46990.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greyarrow_dr.png b/addons/escoria-wizard/graphics/greyarrow_dr.png new file mode 100644 index 0000000..0eebaa8 Binary files /dev/null and b/addons/escoria-wizard/graphics/greyarrow_dr.png differ diff --git a/addons/escoria-wizard/graphics/greyarrow_dr.png.import b/addons/escoria-wizard/graphics/greyarrow_dr.png.import new file mode 100644 index 0000000..8fe4869 --- /dev/null +++ b/addons/escoria-wizard/graphics/greyarrow_dr.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cf82by1gyhbkq" +path="res://.godot/imported/greyarrow_dr.png-2939a9b68f8e98de98cd569a9f768e18.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greyarrow_dr.png" +dest_files=["res://.godot/imported/greyarrow_dr.png-2939a9b68f8e98de98cd569a9f768e18.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greyarrow_l.png b/addons/escoria-wizard/graphics/greyarrow_l.png new file mode 100644 index 0000000..bdb58ad Binary files /dev/null and b/addons/escoria-wizard/graphics/greyarrow_l.png differ diff --git a/addons/escoria-wizard/graphics/greyarrow_l.png.import b/addons/escoria-wizard/graphics/greyarrow_l.png.import new file mode 100644 index 0000000..6415997 --- /dev/null +++ b/addons/escoria-wizard/graphics/greyarrow_l.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cu1yjsyiquy2s" +path="res://.godot/imported/greyarrow_l.png-f1196bd6fdd6e1de492b08580a00921d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greyarrow_l.png" +dest_files=["res://.godot/imported/greyarrow_l.png-f1196bd6fdd6e1de492b08580a00921d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greyarrow_r.png b/addons/escoria-wizard/graphics/greyarrow_r.png new file mode 100644 index 0000000..1e9895d Binary files /dev/null and b/addons/escoria-wizard/graphics/greyarrow_r.png differ diff --git a/addons/escoria-wizard/graphics/greyarrow_r.png.import b/addons/escoria-wizard/graphics/greyarrow_r.png.import new file mode 100644 index 0000000..919a6bc --- /dev/null +++ b/addons/escoria-wizard/graphics/greyarrow_r.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dyyeql7sk1ob1" +path="res://.godot/imported/greyarrow_r.png-36801ee769a198a9ad18bbbb5640331b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greyarrow_r.png" +dest_files=["res://.godot/imported/greyarrow_r.png-36801ee769a198a9ad18bbbb5640331b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greyarrow_u.png b/addons/escoria-wizard/graphics/greyarrow_u.png new file mode 100644 index 0000000..06d3194 Binary files /dev/null and b/addons/escoria-wizard/graphics/greyarrow_u.png differ diff --git a/addons/escoria-wizard/graphics/greyarrow_u.png.import b/addons/escoria-wizard/graphics/greyarrow_u.png.import new file mode 100644 index 0000000..d54087c --- /dev/null +++ b/addons/escoria-wizard/graphics/greyarrow_u.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bg46sy7i1lijb" +path="res://.godot/imported/greyarrow_u.png-658bb82dd18ea5c1deec11704a59f9f3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greyarrow_u.png" +dest_files=["res://.godot/imported/greyarrow_u.png-658bb82dd18ea5c1deec11704a59f9f3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greyarrow_ul.png b/addons/escoria-wizard/graphics/greyarrow_ul.png new file mode 100644 index 0000000..f839c4b Binary files /dev/null and b/addons/escoria-wizard/graphics/greyarrow_ul.png differ diff --git a/addons/escoria-wizard/graphics/greyarrow_ul.png.import b/addons/escoria-wizard/graphics/greyarrow_ul.png.import new file mode 100644 index 0000000..e1c1489 --- /dev/null +++ b/addons/escoria-wizard/graphics/greyarrow_ul.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dt0u6wqxs2aa4" +path="res://.godot/imported/greyarrow_ul.png-74f5cac4b9747b99cdd332621999778c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greyarrow_ul.png" +dest_files=["res://.godot/imported/greyarrow_ul.png-74f5cac4b9747b99cdd332621999778c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/greyarrow_ur.png b/addons/escoria-wizard/graphics/greyarrow_ur.png new file mode 100644 index 0000000..884afaf Binary files /dev/null and b/addons/escoria-wizard/graphics/greyarrow_ur.png differ diff --git a/addons/escoria-wizard/graphics/greyarrow_ur.png.import b/addons/escoria-wizard/graphics/greyarrow_ur.png.import new file mode 100644 index 0000000..ab3847c --- /dev/null +++ b/addons/escoria-wizard/graphics/greyarrow_ur.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cv8anqmqyxksu" +path="res://.godot/imported/greyarrow_ur.png-14476ac88541be05fe19c95b1ab9129a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/greyarrow_ur.png" +dest_files=["res://.godot/imported/greyarrow_ur.png-14476ac88541be05fe19c95b1ab9129a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/help_background.png b/addons/escoria-wizard/graphics/help/help_background.png new file mode 100644 index 0000000..b7cf95d Binary files /dev/null and b/addons/escoria-wizard/graphics/help/help_background.png differ diff --git a/addons/escoria-wizard/graphics/help/help_background.png.import b/addons/escoria-wizard/graphics/help/help_background.png.import new file mode 100644 index 0000000..87259c4 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/help_background.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dhv2eohnrknlu" +path="res://.godot/imported/help_background.png-515b6f2ebdc29a6a35bdfe271f845982.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/help_background.png" +dest_files=["res://.godot/imported/help_background.png-515b6f2ebdc29a6a35bdfe271f845982.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/left1.png b/addons/escoria-wizard/graphics/help/left1.png new file mode 100644 index 0000000..6b6ffb6 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/left1.png differ diff --git a/addons/escoria-wizard/graphics/help/left1.png.import b/addons/escoria-wizard/graphics/help/left1.png.import new file mode 100644 index 0000000..8b47a53 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/left1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b1uyarwcp4bf6" +path="res://.godot/imported/left1.png-89486b7ede1d0e0ebf218fc4229e6770.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/left1.png" +dest_files=["res://.godot/imported/left1.png-89486b7ede1d0e0ebf218fc4229e6770.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/left2.png b/addons/escoria-wizard/graphics/help/left2.png new file mode 100644 index 0000000..46173cb Binary files /dev/null and b/addons/escoria-wizard/graphics/help/left2.png differ diff --git a/addons/escoria-wizard/graphics/help/left2.png.import b/addons/escoria-wizard/graphics/help/left2.png.import new file mode 100644 index 0000000..ae0f69c --- /dev/null +++ b/addons/escoria-wizard/graphics/help/left2.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bplhphioxofhu" +path="res://.godot/imported/left2.png-0df5b36be356129d13f3723324a35761.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/left2.png" +dest_files=["res://.godot/imported/left2.png-0df5b36be356129d13f3723324a35761.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/left3.png b/addons/escoria-wizard/graphics/help/left3.png new file mode 100644 index 0000000..7d37685 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/left3.png differ diff --git a/addons/escoria-wizard/graphics/help/left3.png.import b/addons/escoria-wizard/graphics/help/left3.png.import new file mode 100644 index 0000000..aafb139 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/left3.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bg28euu0j04r2" +path="res://.godot/imported/left3.png-c479fa5870c49baa0a2ca626fb0ba922.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/left3.png" +dest_files=["res://.godot/imported/left3.png-c479fa5870c49baa0a2ca626fb0ba922.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/left4.png b/addons/escoria-wizard/graphics/help/left4.png new file mode 100644 index 0000000..f4e8aa8 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/left4.png differ diff --git a/addons/escoria-wizard/graphics/help/left4.png.import b/addons/escoria-wizard/graphics/help/left4.png.import new file mode 100644 index 0000000..bd63712 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/left4.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dmjp5m0c750mx" +path="res://.godot/imported/left4.png-7ca7de5707bddb2c0c9894a86a0f99dc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/left4.png" +dest_files=["res://.godot/imported/left4.png-7ca7de5707bddb2c0c9894a86a0f99dc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/left5.png b/addons/escoria-wizard/graphics/help/left5.png new file mode 100644 index 0000000..bd5b50a Binary files /dev/null and b/addons/escoria-wizard/graphics/help/left5.png differ diff --git a/addons/escoria-wizard/graphics/help/left5.png.import b/addons/escoria-wizard/graphics/help/left5.png.import new file mode 100644 index 0000000..6c11dbf --- /dev/null +++ b/addons/escoria-wizard/graphics/help/left5.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://3o1x3wvkhdma" +path="res://.godot/imported/left5.png-4c1120b18b486d82e0056f2b55e89aa6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/left5.png" +dest_files=["res://.godot/imported/left5.png-4c1120b18b486d82e0056f2b55e89aa6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/leftall.png b/addons/escoria-wizard/graphics/help/leftall.png new file mode 100644 index 0000000..d8f2c7e Binary files /dev/null and b/addons/escoria-wizard/graphics/help/leftall.png differ diff --git a/addons/escoria-wizard/graphics/help/leftall.png.import b/addons/escoria-wizard/graphics/help/leftall.png.import new file mode 100644 index 0000000..b0fcf6b --- /dev/null +++ b/addons/escoria-wizard/graphics/help/leftall.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://rtbpbeobfxx0" +path="res://.godot/imported/leftall.png-5cfb41d709e11f22a1c9265e3761956d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/leftall.png" +dest_files=["res://.godot/imported/leftall.png-5cfb41d709e11f22a1c9265e3761956d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/middle1.png b/addons/escoria-wizard/graphics/help/middle1.png new file mode 100644 index 0000000..c83e0be Binary files /dev/null and b/addons/escoria-wizard/graphics/help/middle1.png differ diff --git a/addons/escoria-wizard/graphics/help/middle1.png.import b/addons/escoria-wizard/graphics/help/middle1.png.import new file mode 100644 index 0000000..d0463bb --- /dev/null +++ b/addons/escoria-wizard/graphics/help/middle1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ds3omjexin008" +path="res://.godot/imported/middle1.png-a7df4ca20a30de2b55fc571e85d9cfb2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/middle1.png" +dest_files=["res://.godot/imported/middle1.png-a7df4ca20a30de2b55fc571e85d9cfb2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/middle2.png b/addons/escoria-wizard/graphics/help/middle2.png new file mode 100644 index 0000000..8f5ceb5 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/middle2.png differ diff --git a/addons/escoria-wizard/graphics/help/middle2.png.import b/addons/escoria-wizard/graphics/help/middle2.png.import new file mode 100644 index 0000000..fd3df13 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/middle2.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bsxmrn4f1cvqx" +path="res://.godot/imported/middle2.png-000877b0dd4fb548a9a584eb70ddb81a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/middle2.png" +dest_files=["res://.godot/imported/middle2.png-000877b0dd4fb548a9a584eb70ddb81a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/middle3.png b/addons/escoria-wizard/graphics/help/middle3.png new file mode 100644 index 0000000..5920827 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/middle3.png differ diff --git a/addons/escoria-wizard/graphics/help/middle3.png.import b/addons/escoria-wizard/graphics/help/middle3.png.import new file mode 100644 index 0000000..577d463 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/middle3.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cvvip8v3di2l7" +path="res://.godot/imported/middle3.png-486ca3eb9b328d77ad0b688b97592b3b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/middle3.png" +dest_files=["res://.godot/imported/middle3.png-486ca3eb9b328d77ad0b688b97592b3b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/middleall.png b/addons/escoria-wizard/graphics/help/middleall.png new file mode 100644 index 0000000..fd516b6 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/middleall.png differ diff --git a/addons/escoria-wizard/graphics/help/middleall.png.import b/addons/escoria-wizard/graphics/help/middleall.png.import new file mode 100644 index 0000000..0cc1647 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/middleall.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b5jr61i88mphk" +path="res://.godot/imported/middleall.png-2012405e80edc7f17713a7658887df29.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/middleall.png" +dest_files=["res://.godot/imported/middleall.png-2012405e80edc7f17713a7658887df29.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/right1.png b/addons/escoria-wizard/graphics/help/right1.png new file mode 100644 index 0000000..0625266 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/right1.png differ diff --git a/addons/escoria-wizard/graphics/help/right1.png.import b/addons/escoria-wizard/graphics/help/right1.png.import new file mode 100644 index 0000000..33f1b02 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/right1.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://3m4bhxiqfwdq" +path="res://.godot/imported/right1.png-eeec8fd1bc62d6a304c2f03f7bb00217.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/right1.png" +dest_files=["res://.godot/imported/right1.png-eeec8fd1bc62d6a304c2f03f7bb00217.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/right2.png b/addons/escoria-wizard/graphics/help/right2.png new file mode 100644 index 0000000..a4f3777 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/right2.png differ diff --git a/addons/escoria-wizard/graphics/help/right2.png.import b/addons/escoria-wizard/graphics/help/right2.png.import new file mode 100644 index 0000000..30b7d47 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/right2.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cuoas8vi6y73b" +path="res://.godot/imported/right2.png-3fc5e52cee1c2de2717d4c734f39abeb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/right2.png" +dest_files=["res://.godot/imported/right2.png-3fc5e52cee1c2de2717d4c734f39abeb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/right3.png b/addons/escoria-wizard/graphics/help/right3.png new file mode 100644 index 0000000..65b769b Binary files /dev/null and b/addons/escoria-wizard/graphics/help/right3.png differ diff --git a/addons/escoria-wizard/graphics/help/right3.png.import b/addons/escoria-wizard/graphics/help/right3.png.import new file mode 100644 index 0000000..5152f92 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/right3.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dlr1lyensi0t8" +path="res://.godot/imported/right3.png-3988c38e8f35d1117b4cc01ad281cbf4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/right3.png" +dest_files=["res://.godot/imported/right3.png-3988c38e8f35d1117b4cc01ad281cbf4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/right4.png b/addons/escoria-wizard/graphics/help/right4.png new file mode 100644 index 0000000..fffc948 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/right4.png differ diff --git a/addons/escoria-wizard/graphics/help/right4.png.import b/addons/escoria-wizard/graphics/help/right4.png.import new file mode 100644 index 0000000..51d5d92 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/right4.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dcyf25snp0mf6" +path="res://.godot/imported/right4.png-78ded730de12c818fbe34a5c8f2779a8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/right4.png" +dest_files=["res://.godot/imported/right4.png-78ded730de12c818fbe34a5c8f2779a8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/right5.png b/addons/escoria-wizard/graphics/help/right5.png new file mode 100644 index 0000000..0888c9d Binary files /dev/null and b/addons/escoria-wizard/graphics/help/right5.png differ diff --git a/addons/escoria-wizard/graphics/help/right5.png.import b/addons/escoria-wizard/graphics/help/right5.png.import new file mode 100644 index 0000000..d072352 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/right5.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dufhtnsvf7ogf" +path="res://.godot/imported/right5.png-7f7954fae8c6473362108406bdd97e73.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/right5.png" +dest_files=["res://.godot/imported/right5.png-7f7954fae8c6473362108406bdd97e73.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/help/rightall.png b/addons/escoria-wizard/graphics/help/rightall.png new file mode 100644 index 0000000..0873e13 Binary files /dev/null and b/addons/escoria-wizard/graphics/help/rightall.png differ diff --git a/addons/escoria-wizard/graphics/help/rightall.png.import b/addons/escoria-wizard/graphics/help/rightall.png.import new file mode 100644 index 0000000..1753e92 --- /dev/null +++ b/addons/escoria-wizard/graphics/help/rightall.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://djmqbtohl4dji" +path="res://.godot/imported/rightall.png-0aa3a9465809f8cae86f4edbe5865d53.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/help/rightall.png" +dest_files=["res://.godot/imported/rightall.png-0aa3a9465809f8cae86f4edbe5865d53.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/icon.png b/addons/escoria-wizard/graphics/icon.png new file mode 100644 index 0000000..712b006 Binary files /dev/null and b/addons/escoria-wizard/graphics/icon.png differ diff --git a/addons/escoria-wizard/graphics/icon.png.import b/addons/escoria-wizard/graphics/icon.png.import new file mode 100644 index 0000000..0332f4b --- /dev/null +++ b/addons/escoria-wizard/graphics/icon.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dr34yy0ohgg4u" +path="res://.godot/imported/icon.png-0fc9f0247cd9bd9b74909fcdcf025ff1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/icon.png" +dest_files=["res://.godot/imported/icon.png-0fc9f0247cd9bd9b74909fcdcf025ff1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/icon16x16.png b/addons/escoria-wizard/graphics/icon16x16.png new file mode 100644 index 0000000..8c4191b Binary files /dev/null and b/addons/escoria-wizard/graphics/icon16x16.png differ diff --git a/addons/escoria-wizard/graphics/icon16x16.png.import b/addons/escoria-wizard/graphics/icon16x16.png.import new file mode 100644 index 0000000..5494b95 --- /dev/null +++ b/addons/escoria-wizard/graphics/icon16x16.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cxvqwxjvbkptq" +path="res://.godot/imported/icon16x16.png-f59ac06156f3bfab23ee70a83c8944ab.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/icon16x16.png" +dest_files=["res://.godot/imported/icon16x16.png-f59ac06156f3bfab23ee70a83c8944ab.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/inventory.png b/addons/escoria-wizard/graphics/inventory.png new file mode 100644 index 0000000..52a6475 Binary files /dev/null and b/addons/escoria-wizard/graphics/inventory.png differ diff --git a/addons/escoria-wizard/graphics/inventory.png.import b/addons/escoria-wizard/graphics/inventory.png.import new file mode 100644 index 0000000..94af7b6 --- /dev/null +++ b/addons/escoria-wizard/graphics/inventory.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cjvh4sa5sj7r0" +path="res://.godot/imported/inventory.png-90f2b310a8cb2af1738f6f75f388cbd2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/inventory.png" +dest_files=["res://.godot/imported/inventory.png-90f2b310a8cb2af1738f6f75f388cbd2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/inventory_preview.png b/addons/escoria-wizard/graphics/inventory_preview.png new file mode 100644 index 0000000..b22db89 Binary files /dev/null and b/addons/escoria-wizard/graphics/inventory_preview.png differ diff --git a/addons/escoria-wizard/graphics/inventory_preview.png.import b/addons/escoria-wizard/graphics/inventory_preview.png.import new file mode 100644 index 0000000..e90154f --- /dev/null +++ b/addons/escoria-wizard/graphics/inventory_preview.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mxyijpemwkkg" +path="res://.godot/imported/inventory_preview.png-5619903504468752503977eeb94ee187.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/inventory_preview.png" +dest_files=["res://.godot/imported/inventory_preview.png-5619903504468752503977eeb94ee187.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/mark-animtest.png b/addons/escoria-wizard/graphics/mark-animtest.png new file mode 100644 index 0000000..1f64e03 Binary files /dev/null and b/addons/escoria-wizard/graphics/mark-animtest.png differ diff --git a/addons/escoria-wizard/graphics/mark-animtest.png.import b/addons/escoria-wizard/graphics/mark-animtest.png.import new file mode 100644 index 0000000..092bbe0 --- /dev/null +++ b/addons/escoria-wizard/graphics/mark-animtest.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdoo1k7bdq2hg" +path="res://.godot/imported/mark-animtest.png-ac4d6bcd6f112aeb51d9292e16dd2577.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/mark-animtest.png" +dest_files=["res://.godot/imported/mark-animtest.png-ac4d6bcd6f112aeb51d9292e16dd2577.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/no_animation.png b/addons/escoria-wizard/graphics/no_animation.png new file mode 100644 index 0000000..5408dca Binary files /dev/null and b/addons/escoria-wizard/graphics/no_animation.png differ diff --git a/addons/escoria-wizard/graphics/no_animation.png.import b/addons/escoria-wizard/graphics/no_animation.png.import new file mode 100644 index 0000000..ec1c9fa --- /dev/null +++ b/addons/escoria-wizard/graphics/no_animation.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cib3mguubyoj3" +path="res://.godot/imported/no_animation.png-2252638461f7a4b60b98f7dd010527d8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/no_animation.png" +dest_files=["res://.godot/imported/no_animation.png-2252638461f7a4b60b98f7dd010527d8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/no_spritesheet.png b/addons/escoria-wizard/graphics/no_spritesheet.png new file mode 100644 index 0000000..4fe2c70 Binary files /dev/null and b/addons/escoria-wizard/graphics/no_spritesheet.png differ diff --git a/addons/escoria-wizard/graphics/no_spritesheet.png.import b/addons/escoria-wizard/graphics/no_spritesheet.png.import new file mode 100644 index 0000000..5090216 --- /dev/null +++ b/addons/escoria-wizard/graphics/no_spritesheet.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://djktceumfjltt" +path="res://.godot/imported/no_spritesheet.png-87c148ee591540fc4f42a39c026f276f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/no_spritesheet.png" +dest_files=["res://.godot/imported/no_spritesheet.png-87c148ee591540fc4f42a39c026f276f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/object_preview.png b/addons/escoria-wizard/graphics/object_preview.png new file mode 100644 index 0000000..14c1e3b Binary files /dev/null and b/addons/escoria-wizard/graphics/object_preview.png differ diff --git a/addons/escoria-wizard/graphics/object_preview.png.import b/addons/escoria-wizard/graphics/object_preview.png.import new file mode 100644 index 0000000..52003c4 --- /dev/null +++ b/addons/escoria-wizard/graphics/object_preview.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d2chqmjr3qq3m" +path="res://.godot/imported/object_preview.png-f569773cc1459e3e251c4801a9f4826b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/object_preview.png" +dest_files=["res://.godot/imported/object_preview.png-f569773cc1459e3e251c4801a9f4826b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/idle_all.png b/addons/escoria-wizard/graphics/robot/idle_all.png new file mode 100644 index 0000000..da6757a Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/idle_all.png differ diff --git a/addons/escoria-wizard/graphics/robot/idle_all.png.import b/addons/escoria-wizard/graphics/robot/idle_all.png.import new file mode 100644 index 0000000..70aa398 --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/idle_all.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b62mc5dcmrkbd" +path="res://.godot/imported/idle_all.png-9a8340bac0d343b050967f0ea71dfc0b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/idle_all.png" +dest_files=["res://.godot/imported/idle_all.png-9a8340bac0d343b050967f0ea71dfc0b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/open_all.png b/addons/escoria-wizard/graphics/robot/open_all.png new file mode 100644 index 0000000..21750b4 Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/open_all.png differ diff --git a/addons/escoria-wizard/graphics/robot/open_all.png.import b/addons/escoria-wizard/graphics/robot/open_all.png.import new file mode 100644 index 0000000..ba9cbb1 --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/open_all.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://3w8vwngu01pw" +path="res://.godot/imported/open_all.png-ffff963e56cf82eb2f84a36d1fd460c4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/open_all.png" +dest_files=["res://.godot/imported/open_all.png-ffff963e56cf82eb2f84a36d1fd460c4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/talk_down.png b/addons/escoria-wizard/graphics/robot/talk_down.png new file mode 100644 index 0000000..132570f Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/talk_down.png differ diff --git a/addons/escoria-wizard/graphics/robot/talk_down.png.import b/addons/escoria-wizard/graphics/robot/talk_down.png.import new file mode 100644 index 0000000..4e4e01f --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/talk_down.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ca5oqruhuv1kb" +path="res://.godot/imported/talk_down.png-5082b90ed7a336a124dc77ba2e3bed52.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/talk_down.png" +dest_files=["res://.godot/imported/talk_down.png-5082b90ed7a336a124dc77ba2e3bed52.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/talk_downleft.png b/addons/escoria-wizard/graphics/robot/talk_downleft.png new file mode 100644 index 0000000..fb4c971 Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/talk_downleft.png differ diff --git a/addons/escoria-wizard/graphics/robot/talk_downleft.png.import b/addons/escoria-wizard/graphics/robot/talk_downleft.png.import new file mode 100644 index 0000000..03fb4bb --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/talk_downleft.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b815hauiy57np" +path="res://.godot/imported/talk_downleft.png-689f67c1d4947264d3540c46111087a7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/talk_downleft.png" +dest_files=["res://.godot/imported/talk_downleft.png-689f67c1d4947264d3540c46111087a7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/talk_downright.png b/addons/escoria-wizard/graphics/robot/talk_downright.png new file mode 100644 index 0000000..d09a52d Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/talk_downright.png differ diff --git a/addons/escoria-wizard/graphics/robot/talk_downright.png.import b/addons/escoria-wizard/graphics/robot/talk_downright.png.import new file mode 100644 index 0000000..70ea563 --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/talk_downright.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://rgfstas7a6jh" +path="res://.godot/imported/talk_downright.png-65e1006a017ae0ba38c0ffb5ea817d8c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/talk_downright.png" +dest_files=["res://.godot/imported/talk_downright.png-65e1006a017ae0ba38c0ffb5ea817d8c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/talk_right.png b/addons/escoria-wizard/graphics/robot/talk_right.png new file mode 100644 index 0000000..e5fa6a2 Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/talk_right.png differ diff --git a/addons/escoria-wizard/graphics/robot/talk_right.png.import b/addons/escoria-wizard/graphics/robot/talk_right.png.import new file mode 100644 index 0000000..64310b0 --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/talk_right.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5sg50gcj7rxi" +path="res://.godot/imported/talk_right.png-753c5c8409c7e42288bfcb2abd3dc38e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/talk_right.png" +dest_files=["res://.godot/imported/talk_right.png-753c5c8409c7e42288bfcb2abd3dc38e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/talk_up.png b/addons/escoria-wizard/graphics/robot/talk_up.png new file mode 100644 index 0000000..7d77d81 Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/talk_up.png differ diff --git a/addons/escoria-wizard/graphics/robot/talk_up.png.import b/addons/escoria-wizard/graphics/robot/talk_up.png.import new file mode 100644 index 0000000..d9321a8 --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/talk_up.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1vtpe0nteftd" +path="res://.godot/imported/talk_up.png-a8261bc8fecc951d47bfdd21fbe83dad.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/talk_up.png" +dest_files=["res://.godot/imported/talk_up.png-a8261bc8fecc951d47bfdd21fbe83dad.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/talk_upright.png b/addons/escoria-wizard/graphics/robot/talk_upright.png new file mode 100644 index 0000000..5a3fd6a Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/talk_upright.png differ diff --git a/addons/escoria-wizard/graphics/robot/talk_upright.png.import b/addons/escoria-wizard/graphics/robot/talk_upright.png.import new file mode 100644 index 0000000..bf4067c --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/talk_upright.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dpj6wc7vit68k" +path="res://.godot/imported/talk_upright.png-3f68d4b1de11fccedcb91e7bae129fd8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/talk_upright.png" +dest_files=["res://.godot/imported/talk_upright.png-3f68d4b1de11fccedcb91e7bae129fd8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/walk_down.png b/addons/escoria-wizard/graphics/robot/walk_down.png new file mode 100644 index 0000000..432cf82 Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/walk_down.png differ diff --git a/addons/escoria-wizard/graphics/robot/walk_down.png.import b/addons/escoria-wizard/graphics/robot/walk_down.png.import new file mode 100644 index 0000000..855c02a --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/walk_down.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://css8sjcyqi53p" +path="res://.godot/imported/walk_down.png-3a17cdb6f24f6bdfbb7833fcbf88b726.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/walk_down.png" +dest_files=["res://.godot/imported/walk_down.png-3a17cdb6f24f6bdfbb7833fcbf88b726.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/walk_downleft.png b/addons/escoria-wizard/graphics/robot/walk_downleft.png new file mode 100644 index 0000000..464a446 Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/walk_downleft.png differ diff --git a/addons/escoria-wizard/graphics/robot/walk_downleft.png.import b/addons/escoria-wizard/graphics/robot/walk_downleft.png.import new file mode 100644 index 0000000..39e4fd5 --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/walk_downleft.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dyee53lfe7ckd" +path="res://.godot/imported/walk_downleft.png-9072895e6f4c6b766dc06e4dc8cababb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/walk_downleft.png" +dest_files=["res://.godot/imported/walk_downleft.png-9072895e6f4c6b766dc06e4dc8cababb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/walk_downright.png b/addons/escoria-wizard/graphics/robot/walk_downright.png new file mode 100644 index 0000000..7eeea0f Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/walk_downright.png differ diff --git a/addons/escoria-wizard/graphics/robot/walk_downright.png.import b/addons/escoria-wizard/graphics/robot/walk_downright.png.import new file mode 100644 index 0000000..9398e5d --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/walk_downright.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d078iugc8w273" +path="res://.godot/imported/walk_downright.png-417dfc9e37f502fedd2805865d8cf34b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/walk_downright.png" +dest_files=["res://.godot/imported/walk_downright.png-417dfc9e37f502fedd2805865d8cf34b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/walk_right.png b/addons/escoria-wizard/graphics/robot/walk_right.png new file mode 100644 index 0000000..91c08dd Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/walk_right.png differ diff --git a/addons/escoria-wizard/graphics/robot/walk_right.png.import b/addons/escoria-wizard/graphics/robot/walk_right.png.import new file mode 100644 index 0000000..0a8f053 --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/walk_right.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dgya85a17aahi" +path="res://.godot/imported/walk_right.png-758b26b0d83968582e33ef99e097ff08.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/walk_right.png" +dest_files=["res://.godot/imported/walk_right.png-758b26b0d83968582e33ef99e097ff08.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/walk_up.png b/addons/escoria-wizard/graphics/robot/walk_up.png new file mode 100644 index 0000000..65dfcc0 Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/walk_up.png differ diff --git a/addons/escoria-wizard/graphics/robot/walk_up.png.import b/addons/escoria-wizard/graphics/robot/walk_up.png.import new file mode 100644 index 0000000..a727adb --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/walk_up.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://e6r4r6d4ocu4" +path="res://.godot/imported/walk_up.png-81c5017bffb8435853ff535307a5e78e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/walk_up.png" +dest_files=["res://.godot/imported/walk_up.png-81c5017bffb8435853ff535307a5e78e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/robot/walk_upright.png b/addons/escoria-wizard/graphics/robot/walk_upright.png new file mode 100644 index 0000000..2a61938 Binary files /dev/null and b/addons/escoria-wizard/graphics/robot/walk_upright.png differ diff --git a/addons/escoria-wizard/graphics/robot/walk_upright.png.import b/addons/escoria-wizard/graphics/robot/walk_upright.png.import new file mode 100644 index 0000000..95b9960 --- /dev/null +++ b/addons/escoria-wizard/graphics/robot/walk_upright.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://drf7lolbdc64j" +path="res://.godot/imported/walk_upright.png-189e982c4a677ef2d3b6f85e216135aa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/robot/walk_upright.png" +dest_files=["res://.godot/imported/walk_upright.png-189e982c4a677ef2d3b6f85e216135aa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/room.png b/addons/escoria-wizard/graphics/room.png new file mode 100644 index 0000000..42746fa Binary files /dev/null and b/addons/escoria-wizard/graphics/room.png differ diff --git a/addons/escoria-wizard/graphics/room.png.import b/addons/escoria-wizard/graphics/room.png.import new file mode 100644 index 0000000..e2197f6 --- /dev/null +++ b/addons/escoria-wizard/graphics/room.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cuu4ihojqyukt" +path="res://.godot/imported/room.png-abcfefaa0828c105b122b5aeeebca3be.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/room.png" +dest_files=["res://.godot/imported/room.png-abcfefaa0828c105b122b5aeeebca3be.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/roomitem.png b/addons/escoria-wizard/graphics/roomitem.png new file mode 100644 index 0000000..7770f7c Binary files /dev/null and b/addons/escoria-wizard/graphics/roomitem.png differ diff --git a/addons/escoria-wizard/graphics/roomitem.png.import b/addons/escoria-wizard/graphics/roomitem.png.import new file mode 100644 index 0000000..2e79bfd --- /dev/null +++ b/addons/escoria-wizard/graphics/roomitem.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://s0ja26dxrp32" +path="res://.godot/imported/roomitem.png-3c529d8979242743952bf5f6bf48d347.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/roomitem.png" +dest_files=["res://.godot/imported/roomitem.png-3c529d8979242743952bf5f6bf48d347.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/graphics/wand.png b/addons/escoria-wizard/graphics/wand.png new file mode 100644 index 0000000..d692dc4 Binary files /dev/null and b/addons/escoria-wizard/graphics/wand.png differ diff --git a/addons/escoria-wizard/graphics/wand.png.import b/addons/escoria-wizard/graphics/wand.png.import new file mode 100644 index 0000000..c8b5690 --- /dev/null +++ b/addons/escoria-wizard/graphics/wand.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://xfiqwetvhpea" +path="res://.godot/imported/wand.png-e5925732df1884bddb4cab3c31b74849.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/escoria-wizard/graphics/wand.png" +dest_files=["res://.godot/imported/wand.png-e5925732df1884bddb4cab3c31b74849.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/escoria-wizard/help_window.gd b/addons/escoria-wizard/help_window.gd new file mode 100644 index 0000000..73abe5c --- /dev/null +++ b/addons/escoria-wizard/help_window.gd @@ -0,0 +1,54 @@ +@tool +extends Window + + +# Declare member variables here. Examples: +# var a: int = 2 +# var b: String = "text" + +const LAST_PAGE = 15 + +@export var current_page = 1 + + +func help_on_prev_button_pressed() -> void: + current_page -= 1 + if current_page < 1: + current_page = 1 + show_page() + + +func help_on_next_button_pressed() -> void: + current_page += 1 + if current_page > LAST_PAGE: + current_page = LAST_PAGE + show_page() + + +func show_page() -> void: + for loop in get_tree().get_nodes_in_group("masks"): + loop.visible = false + for loop in get_tree().get_nodes_in_group("pagetext"): + loop.visible = false + + $masks/leftall.visible = true + $masks/middleall.visible = true + $masks/rightall.visible = true + + get_node("masks").get_node("page%s" % current_page).visible = true + get_node("text").get_node("page%s" % current_page).visible = true + + match current_page: + 2: $masks/leftall.visible = false + 3: $masks/rightall.visible = false + 4: $masks/leftall.visible = false + 5: $masks/leftall.visible = false + 6: $masks/leftall.visible = false + 7: $masks/leftall.visible = false + 8: $masks/middleall.visible = false + 9: $masks/middleall.visible = false + 10: $masks/middleall.visible = false + 11: $masks/rightall.visible = false + 12: $masks/rightall.visible = false + 13: $masks/rightall.visible = false + 14: $masks/rightall.visible = false diff --git a/addons/escoria-wizard/help_window.gd.uid b/addons/escoria-wizard/help_window.gd.uid new file mode 100644 index 0000000..f71b1aa --- /dev/null +++ b/addons/escoria-wizard/help_window.gd.uid @@ -0,0 +1 @@ +uid://de8nuechh5y3b diff --git a/addons/escoria-wizard/help_window.tscn b/addons/escoria-wizard/help_window.tscn new file mode 100644 index 0000000..abc9957 --- /dev/null +++ b/addons/escoria-wizard/help_window.tscn @@ -0,0 +1,556 @@ +[gd_scene load_steps=19 format=3 uid="uid://b1mr4ghmqeyo"] + +[ext_resource type="Texture2D" uid="uid://cwcpgpol7rsy3" path="res://addons/escoria-wizard/graphics/help/left3.png" id="1"] +[ext_resource type="Texture2D" uid="uid://c2je46nm3myer" path="res://addons/escoria-wizard/graphics/help/left4.png" id="2"] +[ext_resource type="Texture2D" uid="uid://b22tpg7go03h3" path="res://addons/escoria-wizard/graphics/help/left5.png" id="3"] +[ext_resource type="Texture2D" uid="uid://cnn5s8yo5o7k3" path="res://addons/escoria-wizard/graphics/help/middle3.png" id="4"] +[ext_resource type="Texture2D" uid="uid://25m5reo66c0j" path="res://addons/escoria-wizard/graphics/help/right4.png" id="5"] +[ext_resource type="Texture2D" uid="uid://b4bksi1cyftii" path="res://addons/escoria-wizard/graphics/help/middleall.png" id="6"] +[ext_resource type="Texture2D" uid="uid://o8iyddkcksk2" path="res://addons/escoria-wizard/graphics/help/middle2.png" id="7"] +[ext_resource type="Texture2D" uid="uid://wmuc3djfhrkl" path="res://addons/escoria-wizard/graphics/help/left2.png" id="8"] +[ext_resource type="Texture2D" uid="uid://mtv6n8gydrk" path="res://addons/escoria-wizard/graphics/help/rightall.png" id="9"] +[ext_resource type="Texture2D" uid="uid://djqcdi16bwtj0" path="res://addons/escoria-wizard/graphics/help/right5.png" id="10"] +[ext_resource type="Texture2D" uid="uid://t5qsqywb4k14" path="res://addons/escoria-wizard/graphics/help/right2.png" id="11"] +[ext_resource type="Texture2D" uid="uid://7xs3y7f65wgr" path="res://addons/escoria-wizard/graphics/help/right3.png" id="12"] +[ext_resource type="Texture2D" uid="uid://e8xb7mcifx7d" path="res://addons/escoria-wizard/graphics/help/leftall.png" id="13"] +[ext_resource type="Texture2D" uid="uid://duonunl0qli8g" path="res://addons/escoria-wizard/graphics/help/help_background.png" id="14"] +[ext_resource type="Texture2D" uid="uid://bcefyvrgk7v26" path="res://addons/escoria-wizard/graphics/help/left1.png" id="15"] +[ext_resource type="Texture2D" uid="uid://b1dojyktesnp3" path="res://addons/escoria-wizard/graphics/help/middle1.png" id="16"] +[ext_resource type="Texture2D" uid="uid://dw3aw81agyx7p" path="res://addons/escoria-wizard/graphics/help/right1.png" id="17"] +[ext_resource type="Script" uid="uid://de8nuechh5y3b" path="res://addons/escoria-wizard/help_window.gd" id="18"] + +[node name="help_window" type="Window"] +exclusive = true +script = ExtResource("18") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +offset_left = 4.0 +offset_top = 4.0 +offset_right = 908.0 +offset_bottom = 638.0 + +[node name="Background" type="TextureRect" parent="VBoxContainer"] +layout_mode = 2 +texture = ExtResource("14") + +[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/CenterContainer"] +layout_mode = 2 + +[node name="prev_button" type="Button" parent="VBoxContainer/CenterContainer/HBoxContainer"] +layout_mode = 2 +text = "<- Previous Page" + +[node name="Control" type="Control" parent="VBoxContainer/CenterContainer/HBoxContainer"] +custom_minimum_size = Vector2(40, 20) +layout_mode = 2 + +[node name="next_button" type="Button" parent="VBoxContainer/CenterContainer/HBoxContainer"] +layout_mode = 2 +text = "Next Page ->" + +[node name="masks" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="leftall" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = 309.0 +offset_bottom = 610.0 +texture = ExtResource("13") + +[node name="middleall" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 324.0 +offset_top = 8.0 +offset_right = 663.0 +offset_bottom = 610.0 +texture = ExtResource("6") + +[node name="rightall" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 678.0 +offset_top = 8.0 +offset_right = 904.0 +offset_bottom = 610.0 +texture = ExtResource("9") + +[node name="page1" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 4.0 +offset_top = 4.0 +offset_right = 305.0 +offset_bottom = 606.0 + +[node name="page2" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = 309.0 +offset_bottom = 610.0 +texture = ExtResource("15") + +[node name="page3" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 678.0 +offset_top = 8.0 +offset_right = 904.0 +offset_bottom = 610.0 +texture = ExtResource("17") + +[node name="page4" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = 309.0 +offset_bottom = 610.0 +texture = ExtResource("8") + +[node name="page5" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = 309.0 +offset_bottom = 610.0 +texture = ExtResource("1") + +[node name="page6" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = 309.0 +offset_bottom = 610.0 +texture = ExtResource("2") + +[node name="page7" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 8.0 +offset_top = 8.0 +offset_right = 309.0 +offset_bottom = 610.0 +texture = ExtResource("3") + +[node name="page8" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 324.0 +offset_top = 8.0 +offset_right = 663.0 +offset_bottom = 610.0 +texture = ExtResource("16") + +[node name="page9" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 324.0 +offset_top = 8.0 +offset_right = 663.0 +offset_bottom = 610.0 +texture = ExtResource("7") + +[node name="page10" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 324.0 +offset_top = 8.0 +offset_right = 663.0 +offset_bottom = 610.0 +texture = ExtResource("4") + +[node name="page11" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 678.0 +offset_top = 8.0 +offset_right = 904.0 +offset_bottom = 610.0 +texture = ExtResource("11") + +[node name="page12" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 678.0 +offset_top = 8.0 +offset_right = 904.0 +offset_bottom = 610.0 +texture = ExtResource("12") + +[node name="page13" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 678.0 +offset_top = 8.0 +offset_right = 904.0 +offset_bottom = 610.0 +texture = ExtResource("5") + +[node name="page14" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.882353) +layout_mode = 0 +offset_left = 678.0 +offset_top = 8.0 +offset_right = 904.0 +offset_bottom = 610.0 +texture = ExtResource("10") + +[node name="page15" type="TextureRect" parent="masks" groups=["masks"]] +visible = false +modulate = Color(1, 1, 1, 0.784314) +layout_mode = 0 +offset_left = 678.0 +offset_top = 8.0 +offset_right = 979.0 +offset_bottom = 610.0 + +[node name="text" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="page1" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 229.0 +offset_top = 137.0 +offset_right = 699.0 +offset_bottom = 440.0 +text = "Escoria Player Creator + +Game characters in Escoria are Godot scenes with an ESCPlayer node. +The ESCPlayer requires configuration to define things like how many +directions the character has animations defined for, and which +movement direction in the game world corresponds to each animation. + +While Escoria can create characters that can move in as many +directions as you like, most games will feature characters that can +move in either 4 or 8 directions. For each direction, walk, talk and idle +animations are required. This tool simplifies the process for configuring +all these settings. + +These help pages will guide you through the process, but essentially you +give your character a name, choose 4 or 8 directions, then choose frames +from sprite sheets for walk, talk and idle animations in each direction. + +Click the \"Next Page\" button below to progress to the next help page." + +[node name="page2" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 301.0 +offset_top = 6.0 +offset_right = 657.0 +offset_bottom = 156.0 +text = "Use the name field to supply a name for your character. +Any changes to the name will be reflected to the +global_id field. The global_id is used to identify the +character in scripts. + +Note that you can change the global_id to be different +from the name if you choose. It's generally easiest +to keep them the same but there's no requirement to. +" + +[node name="page3" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 299.0 +offset_top = 10.0 +offset_right = 654.0 +offset_bottom = 194.0 +text = "Use the \"Load Spritesheet\" button to load a spritesheet +graphic. The spritesheet is an image that contains one +or more sprite frames that will be used for the walk, +talk and/or idle animation frames for the character. + +The character creator remembers which spritesheet is +used for each type and direction of animation, so you +can use one spritesheet for all the animations, one +spritesheet per animation, or any combination +inbetween. +" + +[node name="page4" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 298.0 +offset_top = 59.0 +offset_right = 591.0 +offset_bottom = 124.0 +text = "Use these buttons to indicate whether your +character will have animations facing in either +4 or 8 directions. This can be changed at any +time while creating the character." + +[node name="page5" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 303.0 +offset_top = 105.0 +offset_right = 596.0 +offset_bottom = 170.0 +text = "You will use these buttons to choose whether +you are currently creating walk, talk, or idle +animations." + +[node name="page6" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 133.0 +offset_top = 126.0 +offset_right = 523.0 +offset_bottom = 378.0 +text = "Click the arrow matching the direction you wish to create +an animation for. For example, if you select walk from the +options above, and the right arrow, you'll be creating the +animation for the character walking right. + +Once you store (save) the animation, the arrow will change +to a green colour to show you the animation has been +configured. It can still be edited once it has been stored. + +When you select an direction that isn't up or down, the +mirror button will appear. If you click this button, the +animation will be created automatically as a mirror of the +opposite side's animation. e.g. If you create the right side +animation, you can click the left arrow then the mirror +button to quickly create the left hand animation." + +[node name="page7" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 63.0 +offset_top = 369.0 +offset_right = 334.0 +offset_bottom = 417.0 +text = "When you select the animation frames to +define a particular animation, a preview +of the animation will be shown in this box." + +[node name="page8" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 37.0 +offset_top = 21.0 +offset_right = 308.0 +offset_bottom = 103.0 +text = "This is the currently loaded spritesheet. +As you select particular frames to be +part of the current animation, a box will +be drawn around them to help you see +what is included in the animation." + +[node name="page9" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 339.0 +offset_top = 422.0 +offset_right = 610.0 +offset_bottom = 538.0 +text = "Use this slider to reset the zoom level +on the spritesheet. The zoom is only +used in this tool and will not affect the +zoom level of your character in game. + +Use the reset button to reset the zoom +level." + +[node name="page10" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 354.0 +offset_top = 511.0 +offset_right = 594.0 +offset_bottom = 559.0 +text = "This information window tells you the +filename of the currently loaded +spritesheet." + +[node name="page11" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 297.0 +offset_top = 28.0 +offset_right = 622.0 +offset_bottom = 229.0 +text = "Once you've loaded your spritesheet you need to +tell the character creator how to divide it into +individual frames with the horizontal/vertical +frames selectors. + +The start/end frame selectors tell the tool which +frames to use from the spritesheet to create the +animation. + +The speed slider defines the speed of this +animation in frames per second. Each animation +can have a different speed value if desired." + +[node name="page12" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 374.0 +offset_top = 124.0 +offset_right = 651.0 +offset_bottom = 189.0 +text = "These lines are informational. They tell you +how big the current spritesheet is, and how +big each animation frame is based on the +frame settings above." + +[node name="page13" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 374.0 +offset_top = 171.0 +offset_right = 648.0 +offset_bottom = 321.0 +text = "When you change the settings for the +current animation, the \"store animation\" +button will appear. This saves the current +settings to the selected animation. + +If the settings are changed back to how +they were, the store button will +disappear to tell you that there is currently +no change to save." + +[node name="page14" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 332.0 +offset_top = 228.0 +offset_right = 660.0 +offset_bottom = 429.0 +text = "Click this button to export the completed character +out to a Godot scene for use by Escoria. +Note that a walk, talk and idle animation for each of +the specified (4 or 8) directions must have been +set up before you can export the character. + +If any animations haven't been configured, it will +tell you that animations are missing. + +Direction arrows that haven't turned green are a +visual indicator of the directions still requiring +animations." + +[node name="page15" type="Label" parent="text" groups=["pagetext"]] +visible = false +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = 273.0 +offset_top = 143.0 +offset_right = 663.0 +offset_bottom = 412.0 +text = "Once you have exported your character they will be a saved +ESCPlayer scene in your filesystem. The scene will be named +based on the name you gave your character in the Name +field. + +If this is a user-controllable character, this character can now +be included in your game by : + +* Creating a Godot scene for your room +* Creating an ESCRoom node +* Selecting the created ESCPlayer scene as the Player Scene + property in that node. + +If they are an NPC, add the scene to your scene tree, and +program them in your ESC scripts using the global_id you +have configured." + +[connection signal="pressed" from="VBoxContainer/CenterContainer/HBoxContainer/prev_button" to="." method="help_on_prev_button_pressed"] +[connection signal="pressed" from="VBoxContainer/CenterContainer/HBoxContainer/next_button" to="." method="help_on_next_button_pressed"] diff --git a/addons/escoria-wizard/item_creator.gd b/addons/escoria-wizard/item_creator.gd new file mode 100644 index 0000000..6a279b8 --- /dev/null +++ b/addons/escoria-wizard/item_creator.gd @@ -0,0 +1,318 @@ +@tool +extends MarginContainer + + +const ITEM_NAME_NODE = "VBoxContainer/Content/GridContainer/ItemName" +const GLOBAL_ID_NODE = "VBoxContainer/Content/GridContainer/ItemGlobalID" +const INTERACTIVE_NODE = "VBoxContainer/Content/GridContainer/StartsInteractiveCheckBox" +const ACTION_NODE = "VBoxContainer/Content/GridContainer/DefaultActionOption" +const PREVIEW_NODE = "VBoxContainer/Content/GridContainer/Preview/Preview" +const IMAGE_SIZE_NODE = "VBoxContainer/Content/GridContainer/ImageSize" +const IMAGE_PATH_NODE = "VBoxContainer/Content/GridContainer/ImagePath" +const LOAD_NODE = "LoadObjectGraphic/LoadObjectFileDialog" +const CONFIRM_NODE = "Windows/ConfirmationDialog" +const OBJECT_HEADING_NODE = "VBoxContainer/HelperHeading/CenterContainer/ObjectHeading" +const OBJECT_DESC_NODE = "VBoxContainer/Description/ObjectDescription" +const INVENTORY_HEADING_NODE = "VBoxContainer/HelperHeading/CenterContainer/InventoryHeading" +const INVENTORY_DESC_NODE = "VBoxContainer/Description/InventoryDescription" +const INVENTORY_PATH_NODE = "VBoxContainer/Content/GridContainer/InventoryPath" +const INVENTORY_PATH_LABEL_NODE = "VBoxContainer/Content/GridContainer/InventoryPathLabel" +const INVENTORY_PATH_SPACER_NODE = "VBoxContainer/Content/GridContainer/BlankItem7" +const CREATE_BUTTON_NODE = "VBoxContainer/Buttons/CenterContainer/HBoxContainer/CreateButton" +const ERROR_WINDOW_NODE = "Windows/ErrorDialog" +const INVENTORY_PREV_NODE = "VBoxContainer/Content/GridContainer/Preview/InventoryPreview" +const OBJECT_PREV_NODE = "VBoxContainer/Content/GridContainer/Preview/ObjectPreview" +const BACKGROUND_OBJ_NODE = "VBoxContainer/Control/CenterContainer/HBoxContainer/BackgroundObjectCheckBox" +const CHANGE_PATH_NODE = "VBoxContainer/Content/GridContainer/ChangePathButton" + +var source_image:Image +var image_stream_texture:CompressedTexture2D +var image_has_been_loaded:bool +var image_size:Vector2 +var main_menu_requested:bool +var inventory_mode:bool +var settings_modified:bool +var preview_size:Vector2 + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + # Capture the size of the window before we update its contents so we have + # the absolute size before it gets scaled contents applied to it + preview_size = get_node(PREVIEW_NODE).size + inventory_mode = not get_node(BACKGROUND_OBJ_NODE).pressed + item_creator_reset() + + +func item_creator_reset() -> void: + get_node(ITEM_NAME_NODE).text = "replace_me" + get_node(GLOBAL_ID_NODE).text = "" + get_node(IMAGE_PATH_NODE).text = "" + get_node(INTERACTIVE_NODE).button_pressed = true + + if get_node(ACTION_NODE).get_item_count() > 0: + get_node(ACTION_NODE).clear() + + for option_list in ["look", "pick up", "open", "close", "use", "push", "pull", "talk"]: + get_node(ACTION_NODE).add_item(option_list) + + get_node(ACTION_NODE).selected = 0 + image_size = Vector2.ZERO + image_has_been_loaded = false + main_menu_requested = false + settings_modified = false + get_node(PREVIEW_NODE).texture = null + + if inventory_mode: + get_node(INVENTORY_PATH_NODE).text = ProjectSettings.get_setting("escoria/ui/inventory_items_path") + get_node(CREATE_BUTTON_NODE).text = "Create Inventory" + get_node(INVENTORY_PREV_NODE).visible = true + get_node(OBJECT_PREV_NODE).visible = false + + for loop in [INVENTORY_PATH_NODE, INVENTORY_PATH_LABEL_NODE, INVENTORY_PATH_SPACER_NODE, \ + CHANGE_PATH_NODE]: + get_node(loop).visible = true + else: + get_node(CREATE_BUTTON_NODE).text = "Create Object" + get_node(INVENTORY_PREV_NODE).visible = false + get_node(OBJECT_PREV_NODE).visible = true + for loop in [INVENTORY_PATH_NODE, INVENTORY_PATH_LABEL_NODE, INVENTORY_PATH_SPACER_NODE, \ + CHANGE_PATH_NODE]: + get_node(loop).visible = false + + for loop in [OBJECT_HEADING_NODE, OBJECT_DESC_NODE]: + get_node(loop).visible = not inventory_mode + + for loop in [INVENTORY_HEADING_NODE, INVENTORY_DESC_NODE, INVENTORY_PATH_NODE]: + get_node(loop).visible = inventory_mode + $Windows/FileDialog.current_dir = ProjectSettings.get_setting("escoria/ui/inventory_items_path") + +func resize_image() -> void: + # Calculate the scale to make the preview as big as possible in the preview window depending on + # the height to width ratio of the frame + image_size = image_stream_texture.get_size() + var preview_scale = Vector2.ONE + preview_scale.x = preview_size.x / image_size.x + preview_scale.y = preview_size.y / image_size.y + + if preview_scale.y > preview_scale.x: + get_node(PREVIEW_NODE).scale = Vector2(preview_scale.x, preview_scale.x) + else: + get_node(PREVIEW_NODE).scale = Vector2(preview_scale.y, preview_scale.y) + +func background_on_ItemName_text_changed(new_text: String) -> void: + get_node(GLOBAL_ID_NODE).text = new_text + settings_modified = true + + +func load_button_pressed() -> void: + get_node(LOAD_NODE).popup_centered() + + +func LoadObjectFileDialog_file_selected(path: String) -> void: + image_stream_texture = load(path) + + get_node(PREVIEW_NODE).texture = image_stream_texture + + resize_image() + + get_node(IMAGE_SIZE_NODE).text = "(%s, %s)" % [image_size.x, image_size.y] + + get_node(IMAGE_PATH_NODE).text = path + image_has_been_loaded = true + settings_modified = true + get_node(INVENTORY_PREV_NODE).visible = false + get_node(OBJECT_PREV_NODE).visible = false + + +func _on_CreateButton_pressed() -> void: + var inventory_path = ProjectSettings.get_setting("escoria/ui/inventory_items_path") + if not DirAccess.dir_exists_absolute(inventory_path): + get_node(ERROR_WINDOW_NODE).dialog_text = \ + "Folder %s does not exist. Please create or change the destination" % inventory_path + get_node(ERROR_WINDOW_NODE).popup_centered() + return + + if not image_has_been_loaded: + get_node(ERROR_WINDOW_NODE).dialog_text = \ + "No image has been loaded." + get_node(ERROR_WINDOW_NODE).popup_centered() + return + + if get_node(ITEM_NAME_NODE).text == "replace_me": + get_node(ERROR_WINDOW_NODE).dialog_text = \ + "Please change the object name." + get_node(ERROR_WINDOW_NODE).popup_centered() + return + + if inventory_mode == false: + if not EditorPlugin.new().get_editor_interface().get_selection().get_selected_nodes(): + get_node(ERROR_WINDOW_NODE).dialog_text = \ + "Please select a node in the scene tree\nto attach the object to." + get_node(ERROR_WINDOW_NODE).popup_centered() + return + + var item = ESCItem.new() + item.name = get_node(ITEM_NAME_NODE).text + item.global_id = get_node(GLOBAL_ID_NODE).text + item.is_interactive = get_node(INTERACTIVE_NODE).pressed + item.tooltip_name = get_node(ITEM_NAME_NODE).text + + var selected_index = get_node(ACTION_NODE).selected + item.default_action = get_node(ACTION_NODE).get_item_text(selected_index) + + # Make the item by default it's usable straight out of the inventory + if inventory_mode == true: + var new_pool_array: PackedStringArray = item.combine_when_selected_action_is_in + new_pool_array.append("use") + item.combine_when_selected_action_is_in = new_pool_array + + # Add Dialog Position to the background item + var interact_position = ESCLocation.new() + interact_position.name = "interact_position" + interact_position.position.y = image_size.y * 2 + item.add_child(interact_position) + + # Add Collision shape to the background item + var rectangle_shape = RectangleShape2D.new() + var collision_shape = CollisionShape2D.new() + + collision_shape.shape = rectangle_shape + collision_shape.shape.extents = image_size / 2 + item.add_child(collision_shape) + + # Add sprite to the background item + var item_sprite = Sprite2D.new() + item_sprite.texture = get_node(PREVIEW_NODE).texture + item.add_child(item_sprite) + + if not inventory_mode: + # Create in scene tree + # Attach to currently selected node in scene tree + var current_node = EditorPlugin.new().get_editor_interface().get_selection().get_selected_nodes()[0] + current_node.add_child(item) + var owning_node = get_tree().edited_scene_root + item.set_owner(owning_node) + # Make it so all the nodes can be seen in the scene tree + collision_shape.set_owner(owning_node) + interact_position.set_owner(owning_node) + item_sprite.set_owner(owning_node) + + get_node("Windows/CreateCompleteDialog").dialog_text = \ + "Background object %s created.\n\n" % item + \ + "Note that you can right-click the node in the\n" + \ + "scene tree and select \"Save branch as scene\"\n" + \ + "if you'd like to reuse this item." + print("Background object %s created." % item) + get_node("Windows/CreateCompleteDialog").popup_centered() + else: + get_tree().edited_scene_root.add_child(item) + # Make it so all the nodes can be seen in the scene tree + collision_shape.set_owner(item) + interact_position.set_owner(item) + item_sprite.set_owner(item) + + item.set_owner(get_tree().edited_scene_root) + # Export scene - create in inventory folder + var packed_scene = PackedScene.new() + + packed_scene.pack(get_tree().edited_scene_root.get_node(item.name)) + + # Flag suggestions from https://godotengine.org/qa/50437/how-to-turn-a-node-into-a-packedscene-via-gdscript + var err = ResourceSaver.save(packed_scene, "%s/%s.tscn" % [inventory_path, get_node(ITEM_NAME_NODE).text], \ + ResourceSaver.FLAG_CHANGE_PATH|ResourceSaver.FLAG_REPLACE_SUBRESOURCE_PATHS) + if err: + get_node(ERROR_WINDOW_NODE).dialog_text = \ + "Failed to save the item. Please make sure you can\n" + \ + "write to the destination folder" % inventory_path + get_node(ERROR_WINDOW_NODE).popup_centered() + return + else: + item.queue_free() + get_tree().edited_scene_root.get_node(item.name).queue_free() + get_node("Windows/CreateCompleteDialog").dialog_text = \ + "Inventory item %s/%s.tscn created." % [inventory_path, get_node(ITEM_NAME_NODE).text] + print("Inventory item %s/%s.tscn created." % [inventory_path, get_node(ITEM_NAME_NODE).text]) + get_node("Windows/CreateCompleteDialog").popup_centered() + + +func Item_on_ClearButton_pressed() -> void: + if settings_modified == true: + main_menu_requested = false + get_node(CONFIRM_NODE).dialog_text = "WARNING!\n\n" + \ + "If you continue you will lose the current object." + get_node(CONFIRM_NODE).popup_centered() + + +func _on_ObjectConfirmationDialog_confirmed() -> void: + if main_menu_requested == true: + switch_to_main_menu() + else: + item_creator_reset() + + +func Item_on_ExitButton_pressed() -> void: + if settings_modified == true: + main_menu_requested = true + get_node(CONFIRM_NODE).dialog_text = "WARNING!\n\n" + \ + "If you continue you will lose the current object." + get_node(CONFIRM_NODE).popup_centered() + else: + switch_to_main_menu() + + +func switch_to_main_menu() -> void: + get_node("../Menu").visible = true + get_node("../ItemCreator").visible = false + + +func _on_StartsInteractiveCheckBox_pressed() -> void: + settings_modified = true + + +func _on_ItemGlobalID_text_changed(_new_text: String) -> void: + settings_modified = true + + +func _on_DefaultActionOption_item_selected(_index: int) -> void: + settings_modified = true + + +func _on_CreateCompleteDialog_confirmed() -> void: + item_creator_reset() + + +func _on_BackgroundObjectCheckBox_toggled(button_pressed: bool) -> void: + if button_pressed == false: + $VBoxContainer/Control/CenterContainer/HBoxContainer/InventoryItemCheckBox.set_pressed_no_signal(true) + inventory_mode = true + else: + $VBoxContainer/Control/CenterContainer/HBoxContainer/InventoryItemCheckBox.set_pressed_no_signal(false) + inventory_mode = false + + item_creator_reset() + + +func _on_InventoryItemCheckBox_toggled(button_pressed: bool) -> void: + if button_pressed == false: + $VBoxContainer/Control/CenterContainer/HBoxContainer/BackgroundObjectCheckBox.set_pressed_no_signal(true) + inventory_mode = false + else: + $VBoxContainer/Control/CenterContainer/HBoxContainer/BackgroundObjectCheckBox.set_pressed_no_signal(false) + inventory_mode = true + + item_creator_reset() + + +func _on_ChangePathButton_pressed(): + $"Windows/FileDialog".popup_centered() + + +func _on_FileDialog_dir_selected(dir: String) -> void: + ProjectSettings.set_setting("escoria/ui/inventory_items_path", dir) + var property_info = { + "name": "escoria/ui/inventory_items_path", + "type": TYPE_STRING + } + ProjectSettings.add_property_info(property_info) + get_node(INVENTORY_PATH_NODE).text = dir diff --git a/addons/escoria-wizard/item_creator.gd.uid b/addons/escoria-wizard/item_creator.gd.uid new file mode 100644 index 0000000..e151a3c --- /dev/null +++ b/addons/escoria-wizard/item_creator.gd.uid @@ -0,0 +1 @@ +uid://bxj8cgfbk5r8f diff --git a/addons/escoria-wizard/plugin.cfg b/addons/escoria-wizard/plugin.cfg new file mode 100644 index 0000000..ad7e2a2 --- /dev/null +++ b/addons/escoria-wizard/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Escoria Wizard" +description="This plugin is used to make the creation of Escoria resources easier for developers." +author="BalloonPopper" +version="1.0.0" +script="plugin.gd" diff --git a/addons/escoria-wizard/plugin.gd b/addons/escoria-wizard/plugin.gd new file mode 100644 index 0000000..5bb65f0 --- /dev/null +++ b/addons/escoria-wizard/plugin.gd @@ -0,0 +1,59 @@ +@tool +extends EditorPlugin + +const helper_ui = preload("res://addons/escoria-wizard/escoria_wizard.tscn") + +# This is the instance of the plugin code that is instantiated by the plugin. +var helper_instance + +func _enter_tree() -> void: + helper_instance = helper_ui.instantiate() + helper_instance.plugin_reference = self + # Add the panel to the main viewport + get_editor_interface().get_editor_main_screen().add_child(helper_instance) + _make_visible(false) + + +func _exit_tree() -> void: + if helper_instance: + helper_instance.queue_free() + + +func _has_main_screen() -> bool: + # Add the button to the Godot interface ribbon + return true + + +func _make_visible(visible: bool) -> void: + if helper_instance: + helper_instance.visible = visible + + +func _get_plugin_name() -> String: + return "Escoria Wizard" + + +func _get_plugin_icon() -> Texture2D: + return (preload("res://addons/escoria-wizard/graphics/icon16x16.png")) + + +func open_scene(path: String) -> void: + get_editor_interface().open_scene_from_path(path) + +# Unregister ourselves +func _disable_plugin(): + print("Disabling Escoria Wizard plugin") + ESCProjectSettingsManager.remove_setting( + "escoria/wizard/path_to_rooms" + ) + +# Register ourselves +func _enable_plugin(): + print("Enabling Escoria Wizard plugin") + ESCProjectSettingsManager.register_setting( + "escoria/wizard/path_to_rooms", + "res://rooms", + { + "type": TYPE_STRING + } + ) diff --git a/addons/escoria-wizard/plugin.gd.uid b/addons/escoria-wizard/plugin.gd.uid new file mode 100644 index 0000000..32b5559 --- /dev/null +++ b/addons/escoria-wizard/plugin.gd.uid @@ -0,0 +1 @@ +uid://oi80mj12gpkd diff --git a/addons/escoria-wizard/room_script_template.esc b/addons/escoria-wizard/room_script_template.esc new file mode 100644 index 0000000..713f0c6 --- /dev/null +++ b/addons/escoria-wizard/room_script_template.esc @@ -0,0 +1,39 @@ +# Room script template + +# ":setup" is used to configure anything you want in place before the +# transition-in event runs (i.e. anything you need to set up before the player +# sees the room). Reset movable objects to their start positions here. + +:setup + + + +# ":ready" runs after any events configured in setup complete. It is used to +# configure everything that happens after the transition-in (if any) completes. +# runs. Story telling events go here (e.g. if you want the character to walk to +# a specific location in the room before giving control to the player.) + +:ready + + + +# Code examples - feel free to delete this section +# +# 1) Run this code when the player enters this room for the first time only. +# This assumes your room is called "room_hallway". Also resets the "window" +# to closed by playing the animation "close_window" using 'set_state'. +# +# > [!room_hallway_visited] +# set_global room_hallway_visited true +# set_state window close_window true +# +# +# 2) Change where the player starts depending on which doorway they entered +# the room from. Assumes your character is called "cleaner" +# +# > [eq ESC_LAST_SCENE room2] +# teleport cleaner r1_r_exit +# # Set cleaner look left +# set_angle cleaner 270 +# +# diff --git a/build_escoria_docs.sh b/build_escoria_docs.sh new file mode 100644 index 0000000..c4bdf5f --- /dev/null +++ b/build_escoria_docs.sh @@ -0,0 +1,76 @@ +#!/bin/bash +set -euo pipefail + +# ---- usage & args ----------------------------------------------------------- +if [[ $# -ne 2 ]]; then + echo "Usage: $0 " + exit 1 +fi + +escoria_docs="$1" +godot_bin="$2" + +# Normalize to absolute path for safety +if [[ ! "$escoria_docs" = /* ]]; then + escoria_docs="$(cd "$(dirname "$escoria_docs")" && pwd)/$(basename "$escoria_docs")" +fi + +# ---- step 1: run Godot doctool from current directory ----------------------- +if [[ ! -x "$godot_bin" ]]; then + echo "Error: Godot binary not found at: $godot_bin" + exit 1 +fi + +echo "Running Godot doctool..." +"$godot_bin" \ + --doctool ./docs \ + --gdscript-docs res://addons/escoria-core/ \ + --headless --quit --quiet 2>/dev/null + +# ---- step 2: copy ./docs -> /docsource (overwrite fully) ----- +echo "Syncing ./docs -> \"$escoria_docs/docsource\" (overwriting)..." + +# Ensure destination parent exists +mkdir -p "$escoria_docs" + +# If rsync is available, prefer it for a clean overwrite with --delete +if command -v rsync >/dev/null 2>&1; then + # Trailing slashes make rsync copy the *contents* of ./docs into docsource + rsync -a --delete "./docs/" "$escoria_docs/docsource/" +else + # Fallback: remove and copy + rm -rf "$escoria_docs/docsource" + mkdir -p "$escoria_docs/docsource" + cp -R ./docs/. "$escoria_docs/docsource/" +fi + +# ---- step 3: cd into escoria_docs ------------------------------------------ +echo "Changing directory to \"$escoria_docs\"..." +cd "$escoria_docs" + +# ---- step 4: run docker command -------------------------------------------- +echo "Running Docker extract step..." +docker run --rm \ + -v "$(pwd)":/app \ + -v ./scripting:/app/scripting \ + python-esc \ + python extractesc-g4.py + +# ---- step 5: run build.sh --------------------------------------------------- +if [[ ! -x "./build.sh" ]]; then + echo "Error: ./build.sh not found or not executable in \"$escoria_docs\"" + exit 1 +fi + +echo "Running build.sh..." +./build.sh + +# ---- step 6: serve _build via http.server ----------------------------------- +if [[ ! -d "_build" ]]; then + echo "Error: _build directory not found after build." + exit 1 +fi + +echo "Serving _build at http://localhost:8000 ..." +echo "(Press Ctrl+C to stop)" +python3 -m http.server --directory _build diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..7b5e36f --- /dev/null +++ b/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" format=3 uid="uid://cbkifp0msbthj"] + +[sub_resource type="Sky" id="1"] + +[resource] +background_mode = 2 +sky = SubResource("1") diff --git a/escoria-wizard.conf b/escoria-wizard.conf new file mode 100644 index 0000000..aad5325 Binary files /dev/null and b/escoria-wizard.conf differ diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..a2c60b8 --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,637 @@ +[preset.0] + +name="Windows Desktop x86" +platform="Windows Desktop" +runnable=false +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="*.esc, *.cfg" +exclude_filter="" +export_path="bin/win32/Escoria-reloaded-x86.exe" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=1 + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +binary_format/architecture="x86_64" +codesign/enable=false +codesign/timestamp=true +codesign/timestamp_server_url="" +codesign/digest_algorithm=1 +codesign/description="" +codesign/custom_options=PackedStringArray() +application/modify_resources=true +application/icon="" +application/console_wrapper_icon="" +application/icon_interpolation=4 +application/file_version="" +application/product_version="" +application/company_name="" +application/product_name="" +application/file_description="" +application/copyright="" +application/trademarks="" +application/export_angle=0 +application/export_d3d12=0 +application/d3d12_agility_sdk_multiarch=true +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}' +$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}' +$trigger = New-ScheduledTaskTrigger -Once -At 00:00 +$settings = New-ScheduledTaskSettingsSet +$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings +Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true +Start-ScheduledTask -TaskName godot_remote_debug +while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 } +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue" +ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue +Remove-Item -Recurse -Force '{temp_dir}'" +binary_format/64_bits=false +texture_format/bptc=false +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +texture_format/no_bptc_fallbacks=true + +[preset.1] + +name="Windows Desktop x64" +platform="Windows Desktop" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="*.esc, *.cfg" +exclude_filter="" +export_path="bin/win64/Escoria-x64.exe" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=1 + +[preset.1.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +binary_format/architecture="x86_64" +codesign/enable=false +codesign/timestamp=true +codesign/timestamp_server_url="" +codesign/digest_algorithm=1 +codesign/description="" +codesign/custom_options=PackedStringArray() +application/modify_resources=true +application/icon="" +application/console_wrapper_icon="" +application/icon_interpolation=4 +application/file_version="" +application/product_version="" +application/company_name="" +application/product_name="" +application/file_description="" +application/copyright="" +application/trademarks="" +application/export_angle=0 +application/export_d3d12=0 +application/d3d12_agility_sdk_multiarch=true +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}' +$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}' +$trigger = New-ScheduledTaskTrigger -Once -At 00:00 +$settings = New-ScheduledTaskSettingsSet +$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings +Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true +Start-ScheduledTask -TaskName godot_remote_debug +while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 } +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue" +ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue +Remove-Item -Recurse -Force '{temp_dir}'" +binary_format/64_bits=true +texture_format/bptc=false +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +texture_format/no_bptc_fallbacks=true + +[preset.2] + +name="Linux/X11" +platform="Linux" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="*.esc, *.cfg" +exclude_filter="" +export_path="bin/linux/Escoria-reloaded.x86_64" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=1 + +[preset.2.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=true +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" +binary_format/64_bits=true +texture_format/bptc=false +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +texture_format/no_bptc_fallbacks=true + +[preset.3] + +name="iOS" +platform="iOS" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="*.esc, *.cfg" +exclude_filter="" +export_path="../../../../Documents/godot_render/escoria/Escoria.ipa" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=1 + +[preset.3.options] + +custom_template/debug="" +custom_template/release="" +architectures/arm64=true +application/app_store_team_id="D85KFHH3UF" +application/code_sign_identity_debug="iPhone Developer" +application/export_method_debug=1 +application/code_sign_identity_release="" +application/export_method_release=0 +application/targeted_device_family=2 +application/bundle_identifier="com.escoria.demo" +application/signature="" +application/short_version="1.0" +application/version="1.0" +application/min_ios_version="12.0" +application/additional_plist_content="" +application/icon_interpolation=4 +application/export_project_only=false +application/delete_old_export_files_unconditionally=false +application/generate_simulator_library_if_missing=true +capabilities/access_wifi=false +capabilities/push_notifications=false +capabilities/performance_gaming_tier=false +capabilities/performance_a12=false +user_data/accessible_from_files_app=false +user_data/accessible_from_itunes_sharing=false +privacy/camera_usage_description="" +privacy/camera_usage_description_localized={} +privacy/microphone_usage_description="" +privacy/microphone_usage_description_localized={} +privacy/photolibrary_usage_description="" +privacy/photolibrary_usage_description_localized={} +privacy/file_timestamp_access_reasons=3 +privacy/system_boot_time_access_reasons=1 +privacy/disk_space_access_reasons=3 +privacy/active_keyboard_access_reasons=0 +privacy/user_defaults_access_reasons=0 +privacy/tracking_enabled=false +privacy/tracking_domains=PackedStringArray() +privacy/collected_data/name/collected=false +privacy/collected_data/name/linked_to_user=false +privacy/collected_data/name/used_for_tracking=false +privacy/collected_data/name/collection_purposes=0 +privacy/collected_data/email_address/collected=false +privacy/collected_data/email_address/linked_to_user=false +privacy/collected_data/email_address/used_for_tracking=false +privacy/collected_data/email_address/collection_purposes=0 +privacy/collected_data/phone_number/collected=false +privacy/collected_data/phone_number/linked_to_user=false +privacy/collected_data/phone_number/used_for_tracking=false +privacy/collected_data/phone_number/collection_purposes=0 +privacy/collected_data/physical_address/collected=false +privacy/collected_data/physical_address/linked_to_user=false +privacy/collected_data/physical_address/used_for_tracking=false +privacy/collected_data/physical_address/collection_purposes=0 +privacy/collected_data/other_contact_info/collected=false +privacy/collected_data/other_contact_info/linked_to_user=false +privacy/collected_data/other_contact_info/used_for_tracking=false +privacy/collected_data/other_contact_info/collection_purposes=0 +privacy/collected_data/health/collected=false +privacy/collected_data/health/linked_to_user=false +privacy/collected_data/health/used_for_tracking=false +privacy/collected_data/health/collection_purposes=0 +privacy/collected_data/fitness/collected=false +privacy/collected_data/fitness/linked_to_user=false +privacy/collected_data/fitness/used_for_tracking=false +privacy/collected_data/fitness/collection_purposes=0 +privacy/collected_data/payment_info/collected=false +privacy/collected_data/payment_info/linked_to_user=false +privacy/collected_data/payment_info/used_for_tracking=false +privacy/collected_data/payment_info/collection_purposes=0 +privacy/collected_data/credit_info/collected=false +privacy/collected_data/credit_info/linked_to_user=false +privacy/collected_data/credit_info/used_for_tracking=false +privacy/collected_data/credit_info/collection_purposes=0 +privacy/collected_data/other_financial_info/collected=false +privacy/collected_data/other_financial_info/linked_to_user=false +privacy/collected_data/other_financial_info/used_for_tracking=false +privacy/collected_data/other_financial_info/collection_purposes=0 +privacy/collected_data/precise_location/collected=false +privacy/collected_data/precise_location/linked_to_user=false +privacy/collected_data/precise_location/used_for_tracking=false +privacy/collected_data/precise_location/collection_purposes=0 +privacy/collected_data/coarse_location/collected=false +privacy/collected_data/coarse_location/linked_to_user=false +privacy/collected_data/coarse_location/used_for_tracking=false +privacy/collected_data/coarse_location/collection_purposes=0 +privacy/collected_data/sensitive_info/collected=false +privacy/collected_data/sensitive_info/linked_to_user=false +privacy/collected_data/sensitive_info/used_for_tracking=false +privacy/collected_data/sensitive_info/collection_purposes=0 +privacy/collected_data/contacts/collected=false +privacy/collected_data/contacts/linked_to_user=false +privacy/collected_data/contacts/used_for_tracking=false +privacy/collected_data/contacts/collection_purposes=0 +privacy/collected_data/emails_or_text_messages/collected=false +privacy/collected_data/emails_or_text_messages/linked_to_user=false +privacy/collected_data/emails_or_text_messages/used_for_tracking=false +privacy/collected_data/emails_or_text_messages/collection_purposes=0 +privacy/collected_data/photos_or_videos/collected=false +privacy/collected_data/photos_or_videos/linked_to_user=false +privacy/collected_data/photos_or_videos/used_for_tracking=false +privacy/collected_data/photos_or_videos/collection_purposes=0 +privacy/collected_data/audio_data/collected=false +privacy/collected_data/audio_data/linked_to_user=false +privacy/collected_data/audio_data/used_for_tracking=false +privacy/collected_data/audio_data/collection_purposes=0 +privacy/collected_data/gameplay_content/collected=false +privacy/collected_data/gameplay_content/linked_to_user=false +privacy/collected_data/gameplay_content/used_for_tracking=false +privacy/collected_data/gameplay_content/collection_purposes=0 +privacy/collected_data/customer_support/collected=false +privacy/collected_data/customer_support/linked_to_user=false +privacy/collected_data/customer_support/used_for_tracking=false +privacy/collected_data/customer_support/collection_purposes=0 +privacy/collected_data/other_user_content/collected=false +privacy/collected_data/other_user_content/linked_to_user=false +privacy/collected_data/other_user_content/used_for_tracking=false +privacy/collected_data/other_user_content/collection_purposes=0 +privacy/collected_data/browsing_history/collected=false +privacy/collected_data/browsing_history/linked_to_user=false +privacy/collected_data/browsing_history/used_for_tracking=false +privacy/collected_data/browsing_history/collection_purposes=0 +privacy/collected_data/search_hhistory/collected=false +privacy/collected_data/search_hhistory/linked_to_user=false +privacy/collected_data/search_hhistory/used_for_tracking=false +privacy/collected_data/search_hhistory/collection_purposes=0 +privacy/collected_data/user_id/collected=false +privacy/collected_data/user_id/linked_to_user=false +privacy/collected_data/user_id/used_for_tracking=false +privacy/collected_data/user_id/collection_purposes=0 +privacy/collected_data/device_id/collected=false +privacy/collected_data/device_id/linked_to_user=false +privacy/collected_data/device_id/used_for_tracking=false +privacy/collected_data/device_id/collection_purposes=0 +privacy/collected_data/purchase_history/collected=false +privacy/collected_data/purchase_history/linked_to_user=false +privacy/collected_data/purchase_history/used_for_tracking=false +privacy/collected_data/purchase_history/collection_purposes=0 +privacy/collected_data/product_interaction/collected=false +privacy/collected_data/product_interaction/linked_to_user=false +privacy/collected_data/product_interaction/used_for_tracking=false +privacy/collected_data/product_interaction/collection_purposes=0 +privacy/collected_data/advertising_data/collected=false +privacy/collected_data/advertising_data/linked_to_user=false +privacy/collected_data/advertising_data/used_for_tracking=false +privacy/collected_data/advertising_data/collection_purposes=0 +privacy/collected_data/other_usage_data/collected=false +privacy/collected_data/other_usage_data/linked_to_user=false +privacy/collected_data/other_usage_data/used_for_tracking=false +privacy/collected_data/other_usage_data/collection_purposes=0 +privacy/collected_data/crash_data/collected=false +privacy/collected_data/crash_data/linked_to_user=false +privacy/collected_data/crash_data/used_for_tracking=false +privacy/collected_data/crash_data/collection_purposes=0 +privacy/collected_data/performance_data/collected=false +privacy/collected_data/performance_data/linked_to_user=false +privacy/collected_data/performance_data/used_for_tracking=false +privacy/collected_data/performance_data/collection_purposes=0 +privacy/collected_data/other_diagnostic_data/collected=false +privacy/collected_data/other_diagnostic_data/linked_to_user=false +privacy/collected_data/other_diagnostic_data/used_for_tracking=false +privacy/collected_data/other_diagnostic_data/collection_purposes=0 +privacy/collected_data/environment_scanning/collected=false +privacy/collected_data/environment_scanning/linked_to_user=false +privacy/collected_data/environment_scanning/used_for_tracking=false +privacy/collected_data/environment_scanning/collection_purposes=0 +privacy/collected_data/hands/collected=false +privacy/collected_data/hands/linked_to_user=false +privacy/collected_data/hands/used_for_tracking=false +privacy/collected_data/hands/collection_purposes=0 +privacy/collected_data/head/collected=false +privacy/collected_data/head/linked_to_user=false +privacy/collected_data/head/used_for_tracking=false +privacy/collected_data/head/collection_purposes=0 +privacy/collected_data/other_data_types/collected=false +privacy/collected_data/other_data_types/linked_to_user=false +privacy/collected_data/other_data_types/used_for_tracking=false +privacy/collected_data/other_data_types/collection_purposes=0 +icons/iphone_120x120="" +icons/iphone_180x180="" +icons/ipad_76x76="" +icons/ipad_152x152="" +icons/ipad_167x167="" +icons/app_store_1024x1024="" +icons/spotlight_40x40="" +icons/spotlight_80x80="" +icons/settings_58x58="" +icons/settings_87x87="" +icons/notification_40x40="" +icons/notification_60x60="" +storyboard/image_scale_mode=0 +storyboard/custom_image@2x="" +storyboard/custom_image@3x="" +storyboard/use_custom_bg_color=false +storyboard/custom_bg_color=Color(0, 0, 0, 1) +architectures/armv7=false +application/name="Escoria Demo Game" +application/info="Made with Godot Engine" +application/identifier="com.github.godot-escoria" +application/copyright="" +storyboard/use_launch_screen_storyboard=false +landscape_launch_screens/iphone_2436x1125="" +landscape_launch_screens/iphone_2208x1242="" +landscape_launch_screens/ipad_1024x768="" +landscape_launch_screens/ipad_2048x1536="" +portrait_launch_screens/iphone_640x960="" +portrait_launch_screens/iphone_640x1136="" +portrait_launch_screens/iphone_750x1334="" +portrait_launch_screens/iphone_1125x2436="" +portrait_launch_screens/ipad_768x1024="" +portrait_launch_screens/ipad_1536x2048="" +portrait_launch_screens/iphone_1242x2208="" + +[preset.4] + +name="Android" +platform="Android" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="*.esc, *.cfg" +exclude_filter="" +export_path="bin/android/Escoria.apk" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=1 + +[preset.4.options] + +custom_template/debug="" +custom_template/release="" +gradle_build/use_gradle_build=false +gradle_build/gradle_build_directory="" +gradle_build/android_source_template="" +gradle_build/compress_native_libraries=false +gradle_build/export_format=0 +gradle_build/min_sdk="" +gradle_build/target_sdk="" +architectures/armeabi-v7a=true +architectures/arm64-v8a=true +architectures/x86=false +architectures/x86_64=false +version/code=1 +version/name="1.0" +package/unique_name="org.godotengine.$genname" +package/name="" +package/signed=true +package/app_category=2 +package/retain_data_on_uninstall=false +package/exclude_from_recents=false +package/show_in_android_tv=false +package/show_in_app_library=true +package/show_as_launcher_app=false +launcher_icons/main_192x192="" +launcher_icons/adaptive_foreground_432x432="" +launcher_icons/adaptive_background_432x432="" +graphics/opengl_debug=false +xr_features/xr_mode=0 +screen/immersive_mode=true +screen/support_small=true +screen/support_normal=true +screen/support_large=true +screen/support_xlarge=true +user_data_backup/allow=false +command_line/extra_args="" +apk_expansion/enable=false +apk_expansion/SALT="" +apk_expansion/public_key="" +permissions/custom_permissions=PackedStringArray() +permissions/access_checkin_properties=false +permissions/access_coarse_location=false +permissions/access_fine_location=false +permissions/access_location_extra_commands=false +permissions/access_mock_location=false +permissions/access_network_state=false +permissions/access_surface_flinger=false +permissions/access_wifi_state=false +permissions/account_manager=false +permissions/add_voicemail=false +permissions/authenticate_accounts=false +permissions/battery_stats=false +permissions/bind_accessibility_service=false +permissions/bind_appwidget=false +permissions/bind_device_admin=false +permissions/bind_input_method=false +permissions/bind_nfc_service=false +permissions/bind_notification_listener_service=false +permissions/bind_print_service=false +permissions/bind_remoteviews=false +permissions/bind_text_service=false +permissions/bind_vpn_service=false +permissions/bind_wallpaper=false +permissions/bluetooth=false +permissions/bluetooth_admin=false +permissions/bluetooth_privileged=false +permissions/brick=false +permissions/broadcast_package_removed=false +permissions/broadcast_sms=false +permissions/broadcast_sticky=false +permissions/broadcast_wap_push=false +permissions/call_phone=false +permissions/call_privileged=false +permissions/camera=false +permissions/capture_audio_output=false +permissions/capture_secure_video_output=false +permissions/capture_video_output=false +permissions/change_component_enabled_state=false +permissions/change_configuration=false +permissions/change_network_state=false +permissions/change_wifi_multicast_state=false +permissions/change_wifi_state=false +permissions/clear_app_cache=false +permissions/clear_app_user_data=false +permissions/control_location_updates=false +permissions/delete_cache_files=false +permissions/delete_packages=false +permissions/device_power=false +permissions/diagnostic=false +permissions/disable_keyguard=false +permissions/dump=false +permissions/expand_status_bar=false +permissions/factory_test=false +permissions/flashlight=false +permissions/force_back=false +permissions/get_accounts=false +permissions/get_package_size=false +permissions/get_tasks=false +permissions/get_top_activity_info=false +permissions/global_search=false +permissions/hardware_test=false +permissions/inject_events=false +permissions/install_location_provider=false +permissions/install_packages=false +permissions/install_shortcut=false +permissions/internal_system_window=false +permissions/internet=false +permissions/kill_background_processes=false +permissions/location_hardware=false +permissions/manage_accounts=false +permissions/manage_app_tokens=false +permissions/manage_documents=false +permissions/manage_external_storage=false +permissions/master_clear=false +permissions/media_content_control=false +permissions/modify_audio_settings=false +permissions/modify_phone_state=false +permissions/mount_format_filesystems=false +permissions/mount_unmount_filesystems=false +permissions/nfc=false +permissions/persistent_activity=false +permissions/post_notifications=false +permissions/process_outgoing_calls=false +permissions/read_calendar=false +permissions/read_call_log=false +permissions/read_contacts=false +permissions/read_external_storage=true +permissions/read_frame_buffer=false +permissions/read_history_bookmarks=false +permissions/read_input_state=false +permissions/read_logs=false +permissions/read_phone_state=false +permissions/read_profile=false +permissions/read_sms=false +permissions/read_social_stream=false +permissions/read_sync_settings=false +permissions/read_sync_stats=false +permissions/read_user_dictionary=false +permissions/reboot=false +permissions/receive_boot_completed=false +permissions/receive_mms=false +permissions/receive_sms=false +permissions/receive_wap_push=false +permissions/record_audio=false +permissions/reorder_tasks=false +permissions/restart_packages=false +permissions/send_respond_via_message=false +permissions/send_sms=false +permissions/set_activity_watcher=false +permissions/set_alarm=false +permissions/set_always_finish=false +permissions/set_animation_scale=false +permissions/set_debug_app=false +permissions/set_orientation=false +permissions/set_pointer_speed=false +permissions/set_preferred_applications=false +permissions/set_process_limit=false +permissions/set_time=false +permissions/set_time_zone=false +permissions/set_wallpaper=false +permissions/set_wallpaper_hints=false +permissions/signal_persistent_processes=false +permissions/status_bar=false +permissions/subscribed_feeds_read=false +permissions/subscribed_feeds_write=false +permissions/system_alert_window=false +permissions/transmit_ir=false +permissions/uninstall_shortcut=false +permissions/update_device_stats=false +permissions/use_credentials=false +permissions/use_sip=false +permissions/vibrate=false +permissions/wake_lock=false +permissions/write_apn_settings=false +permissions/write_calendar=false +permissions/write_call_log=false +permissions/write_contacts=false +permissions/write_external_storage=true +permissions/write_gservices=false +permissions/write_history_bookmarks=false +permissions/write_profile=false +permissions/write_secure_settings=false +permissions/write_settings=false +permissions/write_sms=false +permissions/write_social_stream=false +permissions/write_sync_settings=false +permissions/write_user_dictionary=false +custom_build/use_custom_build=false +custom_build/export_format=0 +custom_build/min_sdk="" +custom_build/target_sdk="" +one_click_deploy/clear_previous_install=true +package/classify_as_game=true +xr_features/hand_tracking=0 +xr_features/hand_tracking_frequency=0 +xr_features/passthrough=0 diff --git a/game.esc b/game.esc new file mode 100644 index 0000000..545223f --- /dev/null +++ b/game.esc @@ -0,0 +1,5 @@ +:init + show_menu("main") + +:newgame + change_scene("res://game/rooms/test/test.tscn") diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/100.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/100.png new file mode 100644 index 0000000..02377d3 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/100.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/100.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/100.png.import new file mode 100644 index 0000000..86bb708 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/100.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bse0v1l5dnv17" +path="res://.godot/imported/100.png-95a3f47f0c22984958ff7b12c991d914.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/100.png" +dest_files=["res://.godot/imported/100.png-95a3f47f0c22984958ff7b12c991d914.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000..deb5d31 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png.import new file mode 100644 index 0000000..ef55004 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2gx3c14w42no" +path="res://.godot/imported/1024.png-97795f4a588040f65d0b17c50217288d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/1024.png" +dest_files=["res://.godot/imported/1024.png-97795f4a588040f65d0b17c50217288d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/114.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000..1ea1dec Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/114.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/114.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/114.png.import new file mode 100644 index 0000000..df85765 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/114.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c38q2jfne8hy6" +path="res://.godot/imported/114.png-0c5f6ec7b0738fe4d1edef201470f28c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/114.png" +dest_files=["res://.godot/imported/114.png-0c5f6ec7b0738fe4d1edef201470f28c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/120.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000..9b05fa7 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/120.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/120.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/120.png.import new file mode 100644 index 0000000..798bca6 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/120.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bkbggrea2m0kv" +path="res://.godot/imported/120.png-018ed7d7ffc86f8c8cb502feff2224fa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/120.png" +dest_files=["res://.godot/imported/120.png-018ed7d7ffc86f8c8cb502feff2224fa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/128.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/128.png new file mode 100644 index 0000000..16e3de2 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/128.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/128.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/128.png.import new file mode 100644 index 0000000..5b5ea5e --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/128.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cejxfoqru5642" +path="res://.godot/imported/128.png-668cca6e7cf6fda4fd7e629d97747752.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/128.png" +dest_files=["res://.godot/imported/128.png-668cca6e7cf6fda4fd7e629d97747752.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/144.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/144.png new file mode 100644 index 0000000..b72e9e7 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/144.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/144.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/144.png.import new file mode 100644 index 0000000..9e08515 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/144.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://y1ow55naq6e3" +path="res://.godot/imported/144.png-40ae4f20a5b1cdb6f7cad500d82e77f2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/144.png" +dest_files=["res://.godot/imported/144.png-40ae4f20a5b1cdb6f7cad500d82e77f2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/152.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000..0d7f602 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/152.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/152.png.import new file mode 100644 index 0000000..819c3b5 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/152.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cdfnmrwvk6d8m" +path="res://.godot/imported/152.png-575e22eb4e99a87bbbc6f5f7656769a1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/152.png" +dest_files=["res://.godot/imported/152.png-575e22eb4e99a87bbbc6f5f7656769a1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/16.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/16.png new file mode 100644 index 0000000..8193d63 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/16.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/16.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/16.png.import new file mode 100644 index 0000000..781ae4c --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/16.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bxlgavg1nu1uf" +path="res://.godot/imported/16.png-980e4ee549cfcda4427fa4a2e5f22257.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/16.png" +dest_files=["res://.godot/imported/16.png-980e4ee549cfcda4427fa4a2e5f22257.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/167.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000..9c9a53a Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/167.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/167.png.import new file mode 100644 index 0000000..5a5bd13 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/167.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://jbqroh7ydef4" +path="res://.godot/imported/167.png-a793aaeb0e72bc11e57105f1d3685eba.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/167.png" +dest_files=["res://.godot/imported/167.png-a793aaeb0e72bc11e57105f1d3685eba.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/172.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/172.png new file mode 100644 index 0000000..67bc803 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/172.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/172.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/172.png.import new file mode 100644 index 0000000..ce68411 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/172.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cfeqjwdcmtutm" +path="res://.godot/imported/172.png-6ffe589c00a2db6fffc4e40ee6d043b3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/172.png" +dest_files=["res://.godot/imported/172.png-6ffe589c00a2db6fffc4e40ee6d043b3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/180.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000..c13edf7 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/180.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/180.png.import new file mode 100644 index 0000000..cf8d3c5 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/180.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://l3xuv27x0lql" +path="res://.godot/imported/180.png-ffe68adfa071452c067962552800aa57.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/180.png" +dest_files=["res://.godot/imported/180.png-ffe68adfa071452c067962552800aa57.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/196.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/196.png new file mode 100644 index 0000000..231bd60 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/196.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/196.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/196.png.import new file mode 100644 index 0000000..dcfd469 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/196.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2n6chujc8unr" +path="res://.godot/imported/196.png-7a328cce8dfbb0cd7f94e6793e8fd4c1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/196.png" +dest_files=["res://.godot/imported/196.png-7a328cce8dfbb0cd7f94e6793e8fd4c1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/20.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000..e015c6f Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/20.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/20.png.import new file mode 100644 index 0000000..51acb1e --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/20.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dumfsoao6qk2r" +path="res://.godot/imported/20.png-c26984d4e24dd6691db56e80c96e0f93.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/20.png" +dest_files=["res://.godot/imported/20.png-c26984d4e24dd6691db56e80c96e0f93.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/216.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/216.png new file mode 100644 index 0000000..f2bb2b2 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/216.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/216.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/216.png.import new file mode 100644 index 0000000..d8c7d1a --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/216.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c5q8pvwgbgmeg" +path="res://.godot/imported/216.png-b3aa14c2798788985b6cabd07b1db270.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/216.png" +dest_files=["res://.godot/imported/216.png-b3aa14c2798788985b6cabd07b1db270.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/256.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/256.png new file mode 100644 index 0000000..14dcaa6 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/256.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/256.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/256.png.import new file mode 100644 index 0000000..d872743 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/256.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cnf14vqk0fmxw" +path="res://.godot/imported/256.png-3089e86bcf099f4c93f7b6d298c6e219.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/256.png" +dest_files=["res://.godot/imported/256.png-3089e86bcf099f4c93f7b6d298c6e219.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/29.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000..0d9e592 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/29.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/29.png.import new file mode 100644 index 0000000..268f408 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/29.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cr786plhiikxn" +path="res://.godot/imported/29.png-efe599753f48d75843ac0b27d323fad9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/29.png" +dest_files=["res://.godot/imported/29.png-efe599753f48d75843ac0b27d323fad9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/32.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/32.png new file mode 100644 index 0000000..18a5790 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/32.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/32.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/32.png.import new file mode 100644 index 0000000..b0a193e --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/32.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bamlmvmfqrpgw" +path="res://.godot/imported/32.png-4323bde313c2dcdc3e479f05b8aaee35.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/32.png" +dest_files=["res://.godot/imported/32.png-4323bde313c2dcdc3e479f05b8aaee35.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/40.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000..b0cd7e8 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/40.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/40.png.import new file mode 100644 index 0000000..9fb1361 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/40.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bmarj5almwaqs" +path="res://.godot/imported/40.png-94cbbcc61fe411e40d394af3aa37c754.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/40.png" +dest_files=["res://.godot/imported/40.png-94cbbcc61fe411e40d394af3aa37c754.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/48.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/48.png new file mode 100644 index 0000000..4642374 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/48.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/48.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/48.png.import new file mode 100644 index 0000000..80b6f56 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/48.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://lw7wvrvafqlm" +path="res://.godot/imported/48.png-1d9ffe7fe5225d8a6b52164116c0877a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/48.png" +dest_files=["res://.godot/imported/48.png-1d9ffe7fe5225d8a6b52164116c0877a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/50.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/50.png new file mode 100644 index 0000000..4234131 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/50.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/50.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/50.png.import new file mode 100644 index 0000000..3760315 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/50.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://djkyxqpocw8rd" +path="res://.godot/imported/50.png-ec7fd18b431eb6e1170f8f530ce92499.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/50.png" +dest_files=["res://.godot/imported/50.png-ec7fd18b431eb6e1170f8f530ce92499.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/512.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/512.png new file mode 100644 index 0000000..890342d Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/512.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/512.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/512.png.import new file mode 100644 index 0000000..2f91e6d --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/512.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://byuqhp7vkhxmn" +path="res://.godot/imported/512.png-9ea181c4d2ffd5ac5864ecbe4dd5f901.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/512.png" +dest_files=["res://.godot/imported/512.png-9ea181c4d2ffd5ac5864ecbe4dd5f901.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/55.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/55.png new file mode 100644 index 0000000..0c00cac Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/55.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/55.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/55.png.import new file mode 100644 index 0000000..96c4418 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/55.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://coiwqrc4pidfs" +path="res://.godot/imported/55.png-4a0ea0e73ed9570401888cc3b508819b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/55.png" +dest_files=["res://.godot/imported/55.png-4a0ea0e73ed9570401888cc3b508819b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/57.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/57.png new file mode 100644 index 0000000..7a086d6 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/57.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/57.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/57.png.import new file mode 100644 index 0000000..2dc8741 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/57.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cdlxqb12e6qo4" +path="res://.godot/imported/57.png-be3efd38e51de516186250bc55fb258c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/57.png" +dest_files=["res://.godot/imported/57.png-be3efd38e51de516186250bc55fb258c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/58.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000..b40ee99 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/58.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/58.png.import new file mode 100644 index 0000000..72f6bbe --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/58.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c1tbkg58qf601" +path="res://.godot/imported/58.png-b4ccc6de18bf0fbbb00eaf8163dfbef8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/58.png" +dest_files=["res://.godot/imported/58.png-b4ccc6de18bf0fbbb00eaf8163dfbef8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/60.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000..551aec0 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/60.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/60.png.import new file mode 100644 index 0000000..47a965f --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/60.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c2driwpq8hw2n" +path="res://.godot/imported/60.png-4fc6354cd4d1a0dcd3bea8f179c4f3d2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/60.png" +dest_files=["res://.godot/imported/60.png-4fc6354cd4d1a0dcd3bea8f179c4f3d2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/64.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/64.png new file mode 100644 index 0000000..25eafbc Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/64.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/64.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/64.png.import new file mode 100644 index 0000000..57793f3 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/64.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://beaxmmh4265jb" +path="res://.godot/imported/64.png-951ce154c838c8f00ef8798558f963f4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/64.png" +dest_files=["res://.godot/imported/64.png-951ce154c838c8f00ef8798558f963f4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/72.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/72.png new file mode 100644 index 0000000..f9b8aec Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/72.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/72.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/72.png.import new file mode 100644 index 0000000..3537bb1 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/72.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d2krcpmfbdnui" +path="res://.godot/imported/72.png-4a37fa49f34b62d99a81c03035a552d9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/72.png" +dest_files=["res://.godot/imported/72.png-4a37fa49f34b62d99a81c03035a552d9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/76.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000..2b8fafe Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/76.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/76.png.import new file mode 100644 index 0000000..b9a11ae --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/76.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ba88ujtbi04iu" +path="res://.godot/imported/76.png-15cc4bd12ab843d789947d663ec2e254.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/76.png" +dest_files=["res://.godot/imported/76.png-15cc4bd12ab843d789947d663ec2e254.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/80.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000..72c2331 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/80.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/80.png.import new file mode 100644 index 0000000..296bb40 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/80.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cklllvdjnxrkm" +path="res://.godot/imported/80.png-1c9871d90f6ac0da7d8c198ef6700864.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/80.png" +dest_files=["res://.godot/imported/80.png-1c9871d90f6ac0da7d8c198ef6700864.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/87.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000..c517903 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/87.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/87.png.import new file mode 100644 index 0000000..a10b9e8 --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/87.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1pi0u14lgqsj" +path="res://.godot/imported/87.png-ca8818d3ee0c01165d308fee70e7dae7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/87.png" +dest_files=["res://.godot/imported/87.png-ca8818d3ee0c01165d308fee70e7dae7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/88.png b/game/appicons/Assets.xcassets/AppIcon.appiconset/88.png new file mode 100644 index 0000000..fcabe18 Binary files /dev/null and b/game/appicons/Assets.xcassets/AppIcon.appiconset/88.png differ diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/88.png.import b/game/appicons/Assets.xcassets/AppIcon.appiconset/88.png.import new file mode 100644 index 0000000..7a2b87d --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/88.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://der3a4j53ls24" +path="res://.godot/imported/88.png-18d2cefc29234a31eac25706a9863d6a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/Assets.xcassets/AppIcon.appiconset/88.png" +dest_files=["res://.godot/imported/88.png-18d2cefc29234a31eac25706a9863d6a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/Assets.xcassets/AppIcon.appiconset/Contents.json b/game/appicons/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..e138c0b --- /dev/null +++ b/game/appicons/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1 @@ +{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"idiom":"watch","filename":"172.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"86x86","expected-size":"172","role":"quickLook"},{"idiom":"watch","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"40x40","expected-size":"80","role":"appLauncher"},{"idiom":"watch","filename":"88.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"40mm","scale":"2x","size":"44x44","expected-size":"88","role":"appLauncher"},{"idiom":"watch","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"50x50","expected-size":"100","role":"appLauncher"},{"idiom":"watch","filename":"196.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"98x98","expected-size":"196","role":"quickLook"},{"idiom":"watch","filename":"216.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"44mm","scale":"2x","size":"108x108","expected-size":"216","role":"quickLook"},{"idiom":"watch","filename":"48.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"38mm","scale":"2x","size":"24x24","expected-size":"48","role":"notificationCenter"},{"idiom":"watch","filename":"55.png","folder":"Assets.xcassets/AppIcon.appiconset/","subtype":"42mm","scale":"2x","size":"27.5x27.5","expected-size":"55","role":"notificationCenter"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"3x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch","role":"companionSettings","scale":"2x"},{"size":"1024x1024","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"watch-marketing","scale":"1x"},{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]} \ No newline at end of file diff --git a/game/appicons/android/mipmap-hdpi/ic_launcher.png b/game/appicons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..2f9b708 Binary files /dev/null and b/game/appicons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/game/appicons/android/mipmap-hdpi/ic_launcher.png.import b/game/appicons/android/mipmap-hdpi/ic_launcher.png.import new file mode 100644 index 0000000..1e1b2bf --- /dev/null +++ b/game/appicons/android/mipmap-hdpi/ic_launcher.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dwd028d8lqw3d" +path="res://.godot/imported/ic_launcher.png-fa227237fdfabe913758ef7c4f81b04e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/android/mipmap-hdpi/ic_launcher.png" +dest_files=["res://.godot/imported/ic_launcher.png-fa227237fdfabe913758ef7c4f81b04e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/android/mipmap-mdpi/ic_launcher.png b/game/appicons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..a93aef2 Binary files /dev/null and b/game/appicons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/game/appicons/android/mipmap-mdpi/ic_launcher.png.import b/game/appicons/android/mipmap-mdpi/ic_launcher.png.import new file mode 100644 index 0000000..6925f2c --- /dev/null +++ b/game/appicons/android/mipmap-mdpi/ic_launcher.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://onyvh5hayeep" +path="res://.godot/imported/ic_launcher.png-8f5b520987637a2e2fb6a5bb3fa0042e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/android/mipmap-mdpi/ic_launcher.png" +dest_files=["res://.godot/imported/ic_launcher.png-8f5b520987637a2e2fb6a5bb3fa0042e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/android/mipmap-xhdpi/ic_launcher.png b/game/appicons/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..b4b944b Binary files /dev/null and b/game/appicons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/game/appicons/android/mipmap-xhdpi/ic_launcher.png.import b/game/appicons/android/mipmap-xhdpi/ic_launcher.png.import new file mode 100644 index 0000000..d1e28e9 --- /dev/null +++ b/game/appicons/android/mipmap-xhdpi/ic_launcher.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bo45gtv640yk8" +path="res://.godot/imported/ic_launcher.png-d495f3efee47de98a3e42a26b4079e7d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/android/mipmap-xhdpi/ic_launcher.png" +dest_files=["res://.godot/imported/ic_launcher.png-d495f3efee47de98a3e42a26b4079e7d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/android/mipmap-xxhdpi/ic_launcher.png b/game/appicons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..03dfa6c Binary files /dev/null and b/game/appicons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/game/appicons/android/mipmap-xxhdpi/ic_launcher.png.import b/game/appicons/android/mipmap-xxhdpi/ic_launcher.png.import new file mode 100644 index 0000000..e7b0f42 --- /dev/null +++ b/game/appicons/android/mipmap-xxhdpi/ic_launcher.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dcj2lopx1stil" +path="res://.godot/imported/ic_launcher.png-34ad4a74c532ea366beff33e6af53bd8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/android/mipmap-xxhdpi/ic_launcher.png" +dest_files=["res://.godot/imported/ic_launcher.png-34ad4a74c532ea366beff33e6af53bd8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/android/mipmap-xxxhdpi/ic_launcher.png b/game/appicons/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..8ce1031 Binary files /dev/null and b/game/appicons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/game/appicons/android/mipmap-xxxhdpi/ic_launcher.png.import b/game/appicons/android/mipmap-xxxhdpi/ic_launcher.png.import new file mode 100644 index 0000000..5185dce --- /dev/null +++ b/game/appicons/android/mipmap-xxxhdpi/ic_launcher.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dhvrcbuisl0te" +path="res://.godot/imported/ic_launcher.png-5ee746e8546099b051bb67de5fe13c8c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/android/mipmap-xxxhdpi/ic_launcher.png" +dest_files=["res://.godot/imported/ic_launcher.png-5ee746e8546099b051bb67de5fe13c8c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/appstore.png b/game/appicons/appstore.png new file mode 100644 index 0000000..deb5d31 Binary files /dev/null and b/game/appicons/appstore.png differ diff --git a/game/appicons/appstore.png.import b/game/appicons/appstore.png.import new file mode 100644 index 0000000..d22b50b --- /dev/null +++ b/game/appicons/appstore.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bri4j3uiubywu" +path="res://.godot/imported/appstore.png-7a71226538dd266a524a46114b08d8ef.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/appstore.png" +dest_files=["res://.godot/imported/appstore.png-7a71226538dd266a524a46114b08d8ef.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/appicons/playstore.png b/game/appicons/playstore.png new file mode 100644 index 0000000..cd3bd11 Binary files /dev/null and b/game/appicons/playstore.png differ diff --git a/game/appicons/playstore.png.import b/game/appicons/playstore.png.import new file mode 100644 index 0000000..4085f65 --- /dev/null +++ b/game/appicons/playstore.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cvguocbkp7qs6" +path="res://.godot/imported/playstore.png-e69fee81bd6515a26427a24ee3e56e47.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/appicons/playstore.png" +dest_files=["res://.godot/imported/playstore.png-e69fee81bd6515a26427a24ee3e56e47.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/characters/weener/png/weener.png b/game/characters/weener/png/weener.png new file mode 100644 index 0000000..f9a3441 Binary files /dev/null and b/game/characters/weener/png/weener.png differ diff --git a/game/characters/weener/png/weener.png.import b/game/characters/weener/png/weener.png.import new file mode 100644 index 0000000..937ea68 --- /dev/null +++ b/game/characters/weener/png/weener.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://gjpbik4cr1ax" +path="res://.godot/imported/weener.png-ef42afcda1688fa425005a4b5cd4de38.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/characters/weener/png/weener.png" +dest_files=["res://.godot/imported/weener.png-ef42afcda1688fa425005a4b5cd4de38.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/characters/weener/png/weener.png~ b/game/characters/weener/png/weener.png~ new file mode 100644 index 0000000..f9a3441 Binary files /dev/null and b/game/characters/weener/png/weener.png~ differ diff --git a/game/characters/weener/weener.tscn b/game/characters/weener/weener.tscn new file mode 100644 index 0000000..f23c0b6 --- /dev/null +++ b/game/characters/weener/weener.tscn @@ -0,0 +1,325 @@ +[gd_scene format=3 uid="uid://bxxhvoforh8g8"] + +[ext_resource type="Script" uid="uid://btrr8e33hu0i4" path="res://addons/escoria-core/game/core-scripts/esc_player.gd" id="1_sgupy"] +[ext_resource type="Texture2D" uid="uid://gjpbik4cr1ax" path="res://game/characters/weener/png/weener.png" id="2_36mur"] +[ext_resource type="Script" uid="uid://bxc3dll30x8tl" path="res://addons/escoria-core/game/core-scripts/resources/esc_directionangle.gd" id="2_vicw0"] +[ext_resource type="Script" uid="uid://clixil1u5exq7" path="res://addons/escoria-core/game/core-scripts/resources/esc_animationname.gd" id="3_301ij"] +[ext_resource type="Script" uid="uid://sksvt0n8gle2" path="res://addons/escoria-core/game/core-scripts/resources/esc_animationresource.gd" id="4_1pyro"] +[ext_resource type="Script" uid="uid://85bledusisk1" path="res://addons/escoria-core/game/core-scripts/esc_dialog_location.gd" id="6_301ij"] + +[sub_resource type="Resource" id="Resource_gxqku"] +resource_name = "Up" +script = ExtResource("2_vicw0") +angle_start = 315 +angle_size = 90 +metadata/_custom_type_script = "uid://bxc3dll30x8tl" + +[sub_resource type="Resource" id="Resource_6hgy2"] +resource_name = "Right" +script = ExtResource("2_vicw0") +angle_start = 45 +angle_size = 90 +metadata/_custom_type_script = "uid://bxc3dll30x8tl" + +[sub_resource type="Resource" id="Resource_i6xu8"] +resource_name = "Down" +script = ExtResource("2_vicw0") +angle_start = 135 +angle_size = 90 +metadata/_custom_type_script = "uid://bxc3dll30x8tl" + +[sub_resource type="Resource" id="Resource_r2jcy"] +resource_name = "Left" +script = ExtResource("2_vicw0") +angle_start = 225 +angle_size = 90 +metadata/_custom_type_script = "uid://bxc3dll30x8tl" + +[sub_resource type="Resource" id="Resource_jhbdn"] +script = ExtResource("3_301ij") +animation = "walkback" +metadata/_custom_type_script = "uid://clixil1u5exq7" + +[sub_resource type="Resource" id="Resource_h0hb7"] +script = ExtResource("3_301ij") +animation = "walkright" +metadata/_custom_type_script = "uid://clixil1u5exq7" + +[sub_resource type="Resource" id="Resource_njxbm"] +script = ExtResource("3_301ij") +animation = "walkfront" +metadata/_custom_type_script = "uid://clixil1u5exq7" + +[sub_resource type="Resource" id="Resource_q1mnd"] +script = ExtResource("3_301ij") +animation = "walkleft" +metadata/_custom_type_script = "uid://clixil1u5exq7" + +[sub_resource type="Resource" id="Resource_sosf6"] +script = ExtResource("3_301ij") +animation = "idleback" +metadata/_custom_type_script = "uid://clixil1u5exq7" + +[sub_resource type="Resource" id="Resource_1imgt"] +script = ExtResource("3_301ij") +animation = "idleright" +metadata/_custom_type_script = "uid://clixil1u5exq7" + +[sub_resource type="Resource" id="Resource_ba6ex"] +script = ExtResource("3_301ij") +animation = "idlefront" +metadata/_custom_type_script = "uid://clixil1u5exq7" + +[sub_resource type="Resource" id="Resource_klnla"] +script = ExtResource("3_301ij") +animation = "idleleft" +metadata/_custom_type_script = "uid://clixil1u5exq7" + +[sub_resource type="Resource" id="Resource_n3rnq"] +script = ExtResource("4_1pyro") +dir_angles = Array[ExtResource("2_vicw0")]([SubResource("Resource_gxqku"), SubResource("Resource_6hgy2"), SubResource("Resource_i6xu8"), SubResource("Resource_r2jcy")]) +directions = Array[ExtResource("3_301ij")]([SubResource("Resource_jhbdn"), SubResource("Resource_h0hb7"), SubResource("Resource_njxbm"), SubResource("Resource_q1mnd")]) +idles = Array[ExtResource("3_301ij")]([SubResource("Resource_sosf6"), SubResource("Resource_1imgt"), SubResource("Resource_ba6ex"), SubResource("Resource_klnla")]) +speaks = Array[ExtResource("3_301ij")]([SubResource("Resource_sosf6"), SubResource("Resource_1imgt"), SubResource("Resource_ba6ex"), SubResource("Resource_klnla")]) +metadata/_custom_type_script = "uid://sksvt0n8gle2" + +[sub_resource type="AtlasTexture" id="AtlasTexture_vicw0"] +atlas = ExtResource("2_36mur") +region = Rect2(72, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_301ij"] +atlas = ExtResource("2_36mur") +region = Rect2(0, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1pyro"] +atlas = ExtResource("2_36mur") +region = Rect2(96, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_gxqku"] +atlas = ExtResource("2_36mur") +region = Rect2(48, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_6hgy2"] +atlas = ExtResource("2_36mur") +region = Rect2(72, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_i6xu8"] +atlas = ExtResource("2_36mur") +region = Rect2(336, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_r2jcy"] +atlas = ExtResource("2_36mur") +region = Rect2(360, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jhbdn"] +atlas = ExtResource("2_36mur") +region = Rect2(384, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_h0hb7"] +atlas = ExtResource("2_36mur") +region = Rect2(0, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_njxbm"] +atlas = ExtResource("2_36mur") +region = Rect2(144, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_q1mnd"] +atlas = ExtResource("2_36mur") +region = Rect2(168, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_sosf6"] +atlas = ExtResource("2_36mur") +region = Rect2(192, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1imgt"] +atlas = ExtResource("2_36mur") +region = Rect2(408, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ba6ex"] +atlas = ExtResource("2_36mur") +region = Rect2(432, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_klnla"] +atlas = ExtResource("2_36mur") +region = Rect2(456, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_n3rnq"] +atlas = ExtResource("2_36mur") +region = Rect2(480, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_v0kwc"] +atlas = ExtResource("2_36mur") +region = Rect2(504, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_cshop"] +atlas = ExtResource("2_36mur") +region = Rect2(96, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_52qbr"] +atlas = ExtResource("2_36mur") +region = Rect2(216, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_86w33"] +atlas = ExtResource("2_36mur") +region = Rect2(240, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_b1vkn"] +atlas = ExtResource("2_36mur") +region = Rect2(264, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_wijc4"] +atlas = ExtResource("2_36mur") +region = Rect2(288, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_f5jre"] +atlas = ExtResource("2_36mur") +region = Rect2(312, 0, 24, 70) + +[sub_resource type="AtlasTexture" id="AtlasTexture_l2x7x"] +atlas = ExtResource("2_36mur") +region = Rect2(48, 0, 24, 70) + +[sub_resource type="SpriteFrames" id="SpriteFrames_v8qy8"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_vicw0") +}], +"loop": true, +"name": &"idleback", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_301ij") +}], +"loop": true, +"name": &"idlefront", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_1pyro") +}], +"loop": true, +"name": &"idleleft", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_gxqku") +}], +"loop": true, +"name": &"idleright", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_6hgy2") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_i6xu8") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_r2jcy") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jhbdn") +}], +"loop": true, +"name": &"walkback", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_h0hb7") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_njxbm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_q1mnd") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_sosf6") +}], +"loop": true, +"name": &"walkfront", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_1imgt") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ba6ex") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_klnla") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_n3rnq") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_v0kwc") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_cshop") +}], +"loop": true, +"name": &"walkleft", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_52qbr") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_86w33") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_b1vkn") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_wijc4") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_f5jre") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_l2x7x") +}], +"loop": true, +"name": &"walkright", +"speed": 5.0 +}] + +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_vicw0"] +radius = 96.0 +height = 490.0 + +[node name="Weener" type="Area2D" unique_id=191684192] +process_mode = 1 +script = ExtResource("1_sgupy") +global_id = "weener" +is_movable = true +combine_when_selected_action_is_in = PackedStringArray() +animations = SubResource("Resource_n3rnq") +animations = SubResource("Resource_n3rnq") +metadata/_custom_type_script = "uid://btrr8e33hu0i4" + +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=439283691] +position = Vector2(0, -228) +scale = Vector2(6.487256, 6.487256) +sprite_frames = SubResource("SpriteFrames_v8qy8") +animation = &"idlefront" +frame_progress = 0.54350686 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1357362817] +position = Vector2(0, -221) +shape = SubResource("CapsuleShape2D_vicw0") + +[node name="dialog_position" type="Marker2D" parent="." unique_id=1753098437] +position = Vector2(0, -534) +script = ExtResource("6_301ij") +metadata/_custom_type_script = "uid://85bledusisk1" diff --git a/game/fonts/caslonantique.tres b/game/fonts/caslonantique.tres new file mode 100644 index 0000000..f60b46c --- /dev/null +++ b/game/fonts/caslonantique.tres @@ -0,0 +1,7 @@ +[gd_resource type="FontFile" load_steps=2 format=2] + +[ext_resource path="res://game/fonts/caslonantique.ttf" type="FontFile" id=1] + +[resource] +size = 21 +font_data = ExtResource( 1 ) diff --git a/game/fonts/caslonantique.ttf b/game/fonts/caslonantique.ttf new file mode 100644 index 0000000..eec6f63 Binary files /dev/null and b/game/fonts/caslonantique.ttf differ diff --git a/game/fonts/caslonantique.ttf.import b/game/fonts/caslonantique.ttf.import new file mode 100644 index 0000000..2e69037 --- /dev/null +++ b/game/fonts/caslonantique.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://b8h2pgeglxm7o" +path="res://.godot/imported/caslonantique.ttf-1c5a6881b445a6cb3a0186b2b2cb0e1a.fontdata" + +[deps] + +source_file="res://game/fonts/caslonantique.ttf" +dest_files=["res://.godot/imported/caslonantique.ttf-1c5a6881b445a6cb3a0186b2b2cb0e1a.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +modulate_color_glyphs=false +hinting=1 +subpixel_positioning=4 +keep_rounding_remainders=true +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/game/items/escitems/button.tscn b/game/items/escitems/button.tscn new file mode 100644 index 0000000..ae0e27f --- /dev/null +++ b/game/items/escitems/button.tscn @@ -0,0 +1,96 @@ +[gd_scene load_steps=11 format=3 uid="uid://1kxo8k6wdlaq"] + +[ext_resource type="Script" uid="uid://dshelmw7y44qg" path="res://addons/escoria-core/game/core-scripts/esc_item.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://cag2tv2frq5vj" path="res://game/rooms/room03/smoke.png" id="2"] + +[sub_resource type="Gradient" id="1"] +offsets = PackedFloat32Array(0, 0.830189) +colors = PackedColorArray(1, 1, 1, 1, 0.353516, 0.353516, 0.353516, 1) + +[sub_resource type="GradientTexture2D" id="8"] +gradient = SubResource("1") + +[sub_resource type="Curve" id="3"] +_data = [Vector2(0, 0.0886364), 0.0, 0.0, 0, 0, Vector2(0.612766, 1), 0.0, 0.0, 0, 0, Vector2(0.770213, 0.95), -1.55372, -1.55372, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 4 + +[sub_resource type="CurveTexture" id="9"] +curve = SubResource("3") + +[sub_resource type="ParticleProcessMaterial" id="5"] +lifetime_randomness = 0.2 +emission_shape = 1 +emission_sphere_radius = 1.0 +gravity = Vector3(0, -9.81, 0) +scale_min = 0.0 +scale_max = 0.0 +scale_curve = SubResource("9") +color_ramp = SubResource("8") + +[sub_resource type="Animation" id="6"] +resource_name = "button_broken" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("GPUParticles2D:emitting") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} + +[sub_resource type="Animation" id="7"] +resource_name = "button_repaired" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("GPUParticles2D:emitting") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_frgsd"] +_data = { +&"button_broken": SubResource("6"), +&"button_repaired": SubResource("7") +} + +[node name="button" type="Area2D"] +process_mode = 1 +script = ExtResource("1") +tooltip_name = "Button" +default_action = "use" +combine_when_selected_action_is_in = PackedStringArray() + +[node name="lines" type="Line2D" parent="."] +position = Vector2(0, -266.591) +points = PackedVector2Array(322.305, 390.985, 322.305, 439.068, 368.698, 440.037, 368.974, 392.399, 319.028, 391.549) + +[node name="Polygon2D" type="Polygon2D" parent="lines"] +visible = false +polygon = PackedVector2Array(343.993, 396.767, 323.298, 415.689, 344.585, 438.158, 365.872, 417.463) + +[node name="GPUParticles2D" type="GPUParticles2D" parent="."] +position = Vector2(344.768, 142.144) +amount = 16 +process_material = SubResource("5") +texture = ExtResource("2") +lifetime = 4.0 +preprocess = 1.99 +speed_scale = 1.39 + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_frgsd") +} + +[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="."] +polygon = PackedVector2Array(319.633, 122.126, 320.427, 177.685, 371.224, 176.098, 372.811, 121.332) diff --git a/game/items/inventory/pen.esc b/game/items/inventory/pen.esc new file mode 100644 index 0000000..0600e03 --- /dev/null +++ b/game/items/inventory/pen.esc @@ -0,0 +1,3 @@ +:pickup + inventory_add("pen") + set_active("pen", false) diff --git a/game/items/inventory/pen.png b/game/items/inventory/pen.png new file mode 100644 index 0000000..0eb86b9 Binary files /dev/null and b/game/items/inventory/pen.png differ diff --git a/game/items/inventory/pen.png.import b/game/items/inventory/pen.png.import new file mode 100644 index 0000000..8f7190d --- /dev/null +++ b/game/items/inventory/pen.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2vhviqn3xpsj" +path="res://.godot/imported/pen.png-1935812df4996307422b31a279d1fb31.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/items/inventory/pen.png" +dest_files=["res://.godot/imported/pen.png-1935812df4996307422b31a279d1fb31.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/items/inventory/pen.tscn b/game/items/inventory/pen.tscn new file mode 100644 index 0000000..cc786f8 --- /dev/null +++ b/game/items/inventory/pen.tscn @@ -0,0 +1,23 @@ +[gd_scene format=3 uid="uid://cgs614ewjs78u"] + +[ext_resource type="Script" uid="uid://dshelmw7y44qg" path="res://addons/escoria-core/game/core-scripts/esc_item.gd" id="1_eo2a0"] +[ext_resource type="Texture2D" uid="uid://b2vhviqn3xpsj" path="res://game/items/inventory/pen.png" id="2_5v6x0"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_xpfx3"] + +[node name="Pen" type="Area2D" unique_id=1638794728] +process_mode = 1 +script = ExtResource("1_eo2a0") +global_id = "pen" +esc_script = "res://game/items/inventory/pen.esc" +combine_when_selected_action_is_in = PackedStringArray() +inventory_texture = ExtResource("2_5v6x0") +inventory_texture_hovered = ExtResource("2_5v6x0") +metadata/_custom_type_script = "uid://dshelmw7y44qg" + +[node name="Sprite2D" type="Sprite2D" parent="." unique_id=26149042] +texture = ExtResource("2_5v6x0") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=1729405739] +scale = Vector2(2.962122, 2.962122) +shape = SubResource("RectangleShape2D_xpfx3") diff --git a/game/items/textures/bottle.png b/game/items/textures/bottle.png new file mode 100644 index 0000000..87babdd Binary files /dev/null and b/game/items/textures/bottle.png differ diff --git a/game/items/textures/bottle.png.import b/game/items/textures/bottle.png.import new file mode 100644 index 0000000..fcb5fbc --- /dev/null +++ b/game/items/textures/bottle.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddomp71uwr167" +path="res://.godot/imported/bottle.png-83a76946d26b818becdd62da99e8ebe6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/items/textures/bottle.png" +dest_files=["res://.godot/imported/bottle.png-83a76946d26b818becdd62da99e8ebe6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/intro/esc/intro.esc b/game/rooms/intro/esc/intro.esc new file mode 100644 index 0000000..05d14da --- /dev/null +++ b/game/rooms/intro/esc/intro.esc @@ -0,0 +1,13 @@ +:setup + anim_block("intro_animation_player", "RESET") + +:ready | NO_UI + # Until we have a proper way to skip animations/cutscenes, we want to prevent + # something silly like being able to pause during the intro. + accept_input("NONE") + + anim_block("intro_animation_player", "intro") + + accept_input("ALL") + + show_menu("main") diff --git a/game/rooms/intro/escoria-logo-comet-anim.png b/game/rooms/intro/escoria-logo-comet-anim.png new file mode 100644 index 0000000..b9b1b67 Binary files /dev/null and b/game/rooms/intro/escoria-logo-comet-anim.png differ diff --git a/game/rooms/intro/escoria-logo-comet-anim.png.import b/game/rooms/intro/escoria-logo-comet-anim.png.import new file mode 100644 index 0000000..1b27a0d --- /dev/null +++ b/game/rooms/intro/escoria-logo-comet-anim.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bcqf41n5ixawf" +path="res://.godot/imported/escoria-logo-comet-anim.png-61a9daadbd9a5013893a34a16352eaf5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/rooms/intro/escoria-logo-comet-anim.png" +dest_files=["res://.godot/imported/escoria-logo-comet-anim.png-61a9daadbd9a5013893a34a16352eaf5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/intro/escoria-logo-comet.png b/game/rooms/intro/escoria-logo-comet.png new file mode 100644 index 0000000..296c1ae Binary files /dev/null and b/game/rooms/intro/escoria-logo-comet.png differ diff --git a/game/rooms/intro/escoria-logo-comet.png.import b/game/rooms/intro/escoria-logo-comet.png.import new file mode 100644 index 0000000..24213d2 --- /dev/null +++ b/game/rooms/intro/escoria-logo-comet.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://biaeu2magjnnb" +path="res://.godot/imported/escoria-logo-comet.png-4feca95e0a71adb9a4583c4e2d6b9f39.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/rooms/intro/escoria-logo-comet.png" +dest_files=["res://.godot/imported/escoria-logo-comet.png-4feca95e0a71adb9a4583c4e2d6b9f39.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/intro/escoria_background.png b/game/rooms/intro/escoria_background.png new file mode 100644 index 0000000..8d4fc2b Binary files /dev/null and b/game/rooms/intro/escoria_background.png differ diff --git a/game/rooms/intro/escoria_background.png.import b/game/rooms/intro/escoria_background.png.import new file mode 100644 index 0000000..617050d --- /dev/null +++ b/game/rooms/intro/escoria_background.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dr7u6tmgltb5d" +path="res://.godot/imported/escoria_background.png-8bcff96f08e380943bb09ed3e8a001a4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/rooms/intro/escoria_background.png" +dest_files=["res://.godot/imported/escoria_background.png-8bcff96f08e380943bb09ed3e8a001a4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/intro/escoria_background_1080p.png b/game/rooms/intro/escoria_background_1080p.png new file mode 100644 index 0000000..3d94505 Binary files /dev/null and b/game/rooms/intro/escoria_background_1080p.png differ diff --git a/game/rooms/intro/escoria_background_1080p.png.import b/game/rooms/intro/escoria_background_1080p.png.import new file mode 100644 index 0000000..19a85f7 --- /dev/null +++ b/game/rooms/intro/escoria_background_1080p.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bcw3ujtn2jd6a" +path="res://.godot/imported/escoria_background_1080p.png-fc81012d9e0bc6c86bb55c1924e92011.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/rooms/intro/escoria_background_1080p.png" +dest_files=["res://.godot/imported/escoria_background_1080p.png-fc81012d9e0bc6c86bb55c1924e92011.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/intro/intro_background_space.png b/game/rooms/intro/intro_background_space.png new file mode 100644 index 0000000..0a8ff18 Binary files /dev/null and b/game/rooms/intro/intro_background_space.png differ diff --git a/game/rooms/intro/intro_background_space.png.import b/game/rooms/intro/intro_background_space.png.import new file mode 100644 index 0000000..31c88ee --- /dev/null +++ b/game/rooms/intro/intro_background_space.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://hwm26syqec82" +path="res://.godot/imported/intro_background_space.png-03815c403298b393ba1bea2b29eafeac.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/rooms/intro/intro_background_space.png" +dest_files=["res://.godot/imported/intro_background_space.png-03815c403298b393ba1bea2b29eafeac.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/intro/intro_background_space_1080p.png b/game/rooms/intro/intro_background_space_1080p.png new file mode 100644 index 0000000..6da33fe Binary files /dev/null and b/game/rooms/intro/intro_background_space_1080p.png differ diff --git a/game/rooms/intro/intro_background_space_1080p.png.import b/game/rooms/intro/intro_background_space_1080p.png.import new file mode 100644 index 0000000..dbb95fb --- /dev/null +++ b/game/rooms/intro/intro_background_space_1080p.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bi4mey684c14m" +path="res://.godot/imported/intro_background_space_1080p.png-59ae70e602893abda84a69b82c8be9a4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/rooms/intro/intro_background_space_1080p.png" +dest_files=["res://.godot/imported/intro_background_space_1080p.png-59ae70e602893abda84a69b82c8be9a4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/intro/intro_room.tscn b/game/rooms/intro/intro_room.tscn new file mode 100644 index 0000000..ee702d2 --- /dev/null +++ b/game/rooms/intro/intro_room.tscn @@ -0,0 +1,306 @@ +[gd_scene load_steps=16 format=3 uid="uid://bunr8esv8b1w7"] + +[ext_resource type="Texture2D" uid="uid://b60usq11m50gt" path="res://game/rooms/intro/escoria-logo-comet-anim.png" id="1"] +[ext_resource type="Texture2D" uid="uid://buwluvpj002oo" path="res://game/rooms/intro/intro_background_space.png" id="2"] +[ext_resource type="Texture2D" uid="uid://cmjd8bala4gmj" path="res://game/rooms/intro/escoria-logo-comet.png" id="3"] +[ext_resource type="Script" uid="uid://c7fcpp72w3dnj" path="res://addons/escoria-core/game/core-scripts/esc_room.gd" id="5"] +[ext_resource type="Script" uid="uid://dshelmw7y44qg" path="res://addons/escoria-core/game/core-scripts/esc_item.gd" id="7"] + +[sub_resource type="AtlasTexture" id="1"] +atlas = ExtResource("1") +region = Rect2(0, 0, 340, 400) + +[sub_resource type="AtlasTexture" id="2"] +atlas = ExtResource("1") +region = Rect2(340, 0, 340, 400) + +[sub_resource type="AtlasTexture" id="3"] +atlas = ExtResource("1") +region = Rect2(680, 0, 340, 400) + +[sub_resource type="AtlasTexture" id="4"] +atlas = ExtResource("1") +region = Rect2(1020, 0, 340, 400) + +[sub_resource type="AtlasTexture" id="5"] +atlas = ExtResource("1") +region = Rect2(1360, 0, 340, 400) + +[sub_resource type="AtlasTexture" id="6"] +atlas = ExtResource("1") +region = Rect2(1700, 0, 340, 400) + +[sub_resource type="SpriteFrames" id="7"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("1") +}, { +"duration": 1.0, +"texture": SubResource("2") +}, { +"duration": 1.0, +"texture": SubResource("3") +}, { +"duration": 1.0, +"texture": SubResource("4") +}, { +"duration": 1.0, +"texture": SubResource("5") +}, { +"duration": 1.0, +"texture": SubResource("6") +}], +"loop": true, +"name": &"default", +"speed": 10.0 +}] + +[sub_resource type="Animation" id="8"] +length = 0.1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("asteroid:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [false] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("initial_background:visible") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [true] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("animated_asteroid:visible") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("asteroid:position") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(620, 450)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("Polygon2D:visible") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [true] +} + +[sub_resource type="Animation" id="9"] +length = 6.0 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("animated_asteroid:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 3), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Vector2(350, -20), Vector2(630, 471)] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("animated_asteroid:scale") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 3), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Vector2(0.1, 0.1), Vector2(1, 1)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("animated_asteroid:visible") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 3), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [true, false] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("asteroid:visible") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 3), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [false, true] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("asteroid:modulate") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0, 3.5, 4.5), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("Polygon2D:color") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/6/type = "value" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("Polygon2D:modulate") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(0, 3, 3.5, 5), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0)] +} +tracks/7/type = "value" +tracks/7/imported = false +tracks/7/enabled = true +tracks/7/path = NodePath("initial_background:visible") +tracks/7/interp = 1 +tracks/7/loop_wrap = true +tracks/7/keys = { +"times": PackedFloat32Array(0, 3.5), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [true, false] +} +tracks/8/type = "value" +tracks/8/imported = false +tracks/8/enabled = true +tracks/8/path = NodePath("asteroid:position") +tracks/8/interp = 1 +tracks/8/loop_wrap = true +tracks/8/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(620, 450)] +} +tracks/9/type = "value" +tracks/9/imported = false +tracks/9/enabled = true +tracks/9/path = NodePath("Polygon2D:visible") +tracks/9/interp = 1 +tracks/9/loop_wrap = true +tracks/9/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_teoqk"] +_data = { +&"RESET": SubResource("8"), +&"intro": SubResource("9") +} + +[node name="ESCRoom" type="Node2D"] +script = ExtResource("5") +global_id = "intro_cutscene" +esc_script = "res://game/rooms/intro/esc/intro.esc" +camera_limits = [Rect2(0, 0, 1920, 1080)] + +[node name="ReferenceRect" type="ReferenceRect" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="black_background" type="ColorRect" parent="ReferenceRect"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 1) + +[node name="initial_background" type="Sprite2D" parent="."] +position = Vector2(640, 450) +texture = ExtResource("2") + +[node name="animated_asteroid" type="AnimatedSprite2D" parent="."] +visible = false +position = Vector2(608.222, 432.811) +scale = Vector2(0.93, 0.93) +sprite_frames = SubResource("7") +autoplay = "default" +frame = 5 + +[node name="Polygon2D" type="Polygon2D" parent="."] +modulate = Color(1, 1, 1, 0) +position = Vector2(0, 6) +polygon = PackedVector2Array(0, 0, 1280, 0, 1280, 900, 0, 900) + +[node name="asteroid" type="Sprite2D" parent="."] +visible = false +position = Vector2(620, 450) +texture = ExtResource("3") + +[node name="ESCItem" type="Area2D" parent="."] +process_mode = 1 +script = ExtResource("7") +global_id = "intro_animation_player" +combine_when_selected_action_is_in = PackedStringArray() +animations = null + +[node name="AnimationPlayer" type="AnimationPlayer" parent="ESCItem"] +root_node = NodePath("../..") +libraries = { +"": SubResource("AnimationLibrary_teoqk") +} diff --git a/game/rooms/no_escoria/no_escoria.tscn b/game/rooms/no_escoria/no_escoria.tscn new file mode 100644 index 0000000..a5c9353 --- /dev/null +++ b/game/rooms/no_escoria/no_escoria.tscn @@ -0,0 +1,13 @@ +[gd_scene format=3 uid="uid://bv66iymp6v3mv"] + +[ext_resource type="Script" uid="uid://bhlf2vcb8gme7" path="res://game/rooms/no_escoria/player.gd" id="1_pqe6t"] +[ext_resource type="Texture2D" uid="uid://bex7crp3v5020" path="res://icon.png" id="1_xxogh"] + +[node name="NoEscoria" type="Node2D" unique_id=1279639823] + +[node name="player" type="Area2D" parent="." unique_id=1039845384] +position = Vector2(549, 289) +script = ExtResource("1_pqe6t") + +[node name="Sprite2D" type="Sprite2D" parent="player" unique_id=309330691] +texture = ExtResource("1_xxogh") diff --git a/game/rooms/no_escoria/player.gd b/game/rooms/no_escoria/player.gd new file mode 100644 index 0000000..9e05c56 --- /dev/null +++ b/game/rooms/no_escoria/player.gd @@ -0,0 +1,32 @@ +extends Area2D + +@export var speed = 400 # How fast the player will move (pixels/sec). +var screen_size # Size of the game window. + +@onready var _change_scene: ChangeSceneCommand = ChangeSceneCommand.new() +const next_scene_path: String = "res://game/rooms/room01/room01.tscn" + +func _ready(): + screen_size = get_viewport_rect().size + + +func _process(delta): + var velocity = Vector2.ZERO # The player's movement vector. + if Input.is_action_pressed("ui_right"): + velocity.x += 1 + if Input.is_action_pressed("ui_left"): + velocity.x -= 1 + if Input.is_action_pressed("ui_down"): + velocity.y += 1 + if Input.is_action_pressed("ui_up"): + velocity.y -= 1 + + if velocity.length() > 0: + velocity = velocity.normalized() * speed + + position += velocity * delta + position = position.clamp(Vector2.ZERO, screen_size) + + if position.x > 800: + _change_scene.run([next_scene_path, false]) + queue_free() diff --git a/game/rooms/no_escoria/player.gd.uid b/game/rooms/no_escoria/player.gd.uid new file mode 100644 index 0000000..04adcdf --- /dev/null +++ b/game/rooms/no_escoria/player.gd.uid @@ -0,0 +1 @@ +uid://bhlf2vcb8gme7 diff --git a/game/rooms/test/phone.esc b/game/rooms/test/phone.esc new file mode 100644 index 0000000..b13a65e --- /dev/null +++ b/game/rooms/test/phone.esc @@ -0,0 +1,8 @@ +:look + if $pen in inventory: + say($weener, "Why use a phone when I have this pen? I can write.") + else: + say($weener, "I don't trust phones. You don't know when they'll ring and scare you.") + +:use pen + say($weener, "I will fill in the numbers.") diff --git a/game/rooms/test/room_test.esc b/game/rooms/test/room_test.esc new file mode 100644 index 0000000..b75c9b2 --- /dev/null +++ b/game/rooms/test/room_test.esc @@ -0,0 +1,4 @@ +:setup + if ESC_LAST_SCENE == "kitchen": + teleport($weener, $test_exit) + set_angle($weener, 180) diff --git a/game/rooms/test/test.tscn b/game/rooms/test/test.tscn new file mode 100644 index 0000000..b4e62b5 --- /dev/null +++ b/game/rooms/test/test.tscn @@ -0,0 +1,113 @@ +[gd_scene format=3 uid="uid://cbtqk144uipsv"] + +[ext_resource type="Script" uid="uid://c7fcpp72w3dnj" path="res://addons/escoria-core/game/core-scripts/esc_room.gd" id="1_jhg43"] +[ext_resource type="PackedScene" uid="uid://bxxhvoforh8g8" path="res://game/characters/weener/weener.tscn" id="2_fmbdf"] +[ext_resource type="Texture2D" uid="uid://bnjgnax8nlttv" path="res://game/rooms/test/test_bg.png" id="3_5kokq"] +[ext_resource type="Script" uid="uid://fnwnfflaoeqp" path="res://addons/escoria-core/game/core-scripts/esc_background.gd" id="4_x54b2"] +[ext_resource type="Script" uid="uid://5pbcieq7gpdo" path="res://addons/escoria-core/game/core-scripts/esc_terrain.gd" id="5_x54b2"] +[ext_resource type="Script" uid="uid://beh3ds4eapldu" path="res://addons/escoria-core/game/core-scripts/esc_location.gd" id="6_4gqel"] +[ext_resource type="Script" uid="uid://dshelmw7y44qg" path="res://addons/escoria-core/game/core-scripts/esc_item.gd" id="7_agkbc"] +[ext_resource type="Script" uid="uid://chu4bvqgwxuts" path="res://addons/escoria-core/game/core-scripts/esc_interaction_location.gd" id="8_54mxh"] +[ext_resource type="Texture2D" uid="uid://ddomp71uwr167" path="res://game/items/textures/bottle.png" id="10_wafjj"] +[ext_resource type="PackedScene" uid="uid://cgs614ewjs78u" path="res://game/items/inventory/pen.tscn" id="11_co1tc"] +[ext_resource type="Script" uid="uid://btt4cprco53ec" path="res://addons/escoria-core/game/core-scripts/esc_exit.gd" id="11_wafjj"] + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_4gqel"] +vertices = PackedVector2Array(628, 674.8125, 672.3281, 669.1797, 694.1797, 842, 628, 724.78906, 1171.6484, 842, 1800.4766, 889.4375, 1911, 984.5781, 1911, 1070, 11, 1070, 11, 928.21094, 1237.8828, 683.8203, 1526.5469, 757.4375, 346.09375, 796.0469, 211.74219, 787.8828, 301.64844, 753.59375) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 5, 6, 7, 8, 9, 2), PackedInt32Array(4, 10, 11, 5), PackedInt32Array(12, 3, 2, 9), PackedInt32Array(13, 14, 12, 9)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(1, 923, 207, 779, 304, 742, 349, 785, 618, 717, 618, 666, 681, 658, 703, 832, 1165, 832, 1232, 672, 1530, 748, 1806, 881, 1921, 980, 1921, 1080, 1, 1080)]) + +[node name="Test" type="Node2D" unique_id=1166794] +script = ExtResource("1_jhg43") +global_id = "test" +esc_script = "res://game/rooms/test/room_test.esc" +player_scene = ExtResource("2_fmbdf") +metadata/_custom_type_script = "uid://c7fcpp72w3dnj" + +[node name="escbackground" type="TextureRect" parent="." unique_id=533934161] +offset_right = 1920.0 +offset_bottom = 1080.0 +mouse_filter = 2 +texture = ExtResource("3_5kokq") +script = ExtResource("4_x54b2") +metadata/_custom_type_script = "uid://fnwnfflaoeqp" + +[node name="ESCTerrain" type="Node2D" parent="." unique_id=1609749800] +script = ExtResource("5_x54b2") +metadata/_custom_type_script = "uid://5pbcieq7gpdo" + +[node name="NavigationRegion2D" type="NavigationRegion2D" parent="ESCTerrain" unique_id=482689538] +navigation_polygon = SubResource("NavigationPolygon_4gqel") + +[node name="Start" type="Marker2D" parent="." unique_id=1291454412] +position = Vector2(957, 951) +script = ExtResource("6_4gqel") +global_id = "start" +is_start_location = true +metadata/_custom_type_script = "uid://beh3ds4eapldu" + +[node name="Phone" type="Area2D" parent="." unique_id=754116905] +process_mode = 1 +position = Vector2(0, -2) +script = ExtResource("7_agkbc") +global_id = "phone" +esc_script = "res://game/rooms/test/phone.esc" +tooltip_name = "Phone" +combine_when_selected_action_is_in = PackedStringArray() +metadata/_custom_type_script = "uid://dshelmw7y44qg" + +[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Phone" unique_id=750375557] +polygon = PackedVector2Array(537, 363, 551, 362, 555, 346, 549, 335, 503, 327, 476, 331, 444, 340, 426, 349, 421, 362, 426, 376, 445, 381, 440, 394, 440, 420, 476, 453, 511, 445, 550, 432, 556, 420, 552, 392) + +[node name="ESCInteractionLocation" type="Marker2D" parent="Phone" unique_id=1048861594] +position = Vector2(565, 767) +script = ExtResource("8_54mxh") +metadata/_custom_type_script = "uid://chu4bvqgwxuts" + +[node name="Bottle" type="Area2D" parent="." unique_id=1730354307] +process_mode = 1 +z_index = 1048 +position = Vector2(1549, 997) +script = ExtResource("7_agkbc") +global_id = "bottle" +combine_when_selected_action_is_in = PackedStringArray() +is_interactive = false +inventory_texture = ExtResource("10_wafjj") +inventory_texture_hovered = ExtResource("10_wafjj") +metadata/_custom_type_script = "uid://dshelmw7y44qg" + +[node name="Sprite2D" type="Sprite2D" parent="Bottle" unique_id=1797859338] +position = Vector2(0, -171) +scale = Vector2(10.689665, 7.0120864) +texture = ExtResource("10_wafjj") + +[node name="Pen" parent="." unique_id=1638794728 instance=ExtResource("11_co1tc")] +position = Vector2(1824, 614) +scale = Vector2(2, 2) +tooltip_name = "Pen" +combine_when_selected_action_is_in = PackedStringArray("use") +default_action_inventory = "use" +use_from_inventory_only = true + +[node name="ESCInteractionLocation" type="Marker2D" parent="Pen" unique_id=46124229] +position = Vector2(-44.5, 141.5) +script = ExtResource("8_54mxh") +metadata/_custom_type_script = "uid://chu4bvqgwxuts" + +[node name="ESCExit" type="Area2D" parent="." unique_id=1821249842] +position = Vector2(92, 675) +script = ExtResource("11_wafjj") +target_scene = "uid://dw2s1aomk3k0e" +global_id = "exit_to_kitchen" +tooltip_name = "To kitchen" +metadata/_custom_type_script = "uid://btt4cprco53ec" + +[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="ESCExit" unique_id=1151309996] +position = Vector2(59, 222) +polygon = PackedVector2Array(33, -544, -151, -472, -151, 48, -106, 6, -10, -63, 51, -90) + +[node name="ESCInteractionLocation" type="Marker2D" parent="ESCExit" unique_id=242339506] +position = Vector2(73, 222) +script = ExtResource("8_54mxh") +global_id = "test_exit" +metadata/_custom_type_script = "uid://chu4bvqgwxuts" diff --git a/game/rooms/test/test_bg.png b/game/rooms/test/test_bg.png new file mode 100644 index 0000000..02682c3 Binary files /dev/null and b/game/rooms/test/test_bg.png differ diff --git a/game/rooms/test/test_bg.png.import b/game/rooms/test/test_bg.png.import new file mode 100644 index 0000000..dc0b7b6 --- /dev/null +++ b/game/rooms/test/test_bg.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bnjgnax8nlttv" +path="res://.godot/imported/test_bg.png-129e26406ef58c404687bb02b892741d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/rooms/test/test_bg.png" +dest_files=["res://.godot/imported/test_bg.png-129e26406ef58c404687bb02b892741d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/test_kitchen/kitchen.png b/game/rooms/test_kitchen/kitchen.png new file mode 100644 index 0000000..71d6fa9 Binary files /dev/null and b/game/rooms/test_kitchen/kitchen.png differ diff --git a/game/rooms/test_kitchen/kitchen.png.import b/game/rooms/test_kitchen/kitchen.png.import new file mode 100644 index 0000000..4a9c5d2 --- /dev/null +++ b/game/rooms/test_kitchen/kitchen.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cdv11spgcle4e" +path="res://.godot/imported/kitchen.png-f2ce326655b7e4a2376b8d8f4183d5de.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://game/rooms/test_kitchen/kitchen.png" +dest_files=["res://.godot/imported/kitchen.png-f2ce326655b7e4a2376b8d8f4183d5de.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/game/rooms/test_kitchen/room_kitchen.esc b/game/rooms/test_kitchen/room_kitchen.esc new file mode 100644 index 0000000..c50f2fd --- /dev/null +++ b/game/rooms/test_kitchen/room_kitchen.esc @@ -0,0 +1,4 @@ +:setup + if ESC_LAST_SCENE == "test": + teleport($weener, $kitchen_exit) + set_angle($weener, 270) diff --git a/game/rooms/test_kitchen/test_kitchen.tscn b/game/rooms/test_kitchen/test_kitchen.tscn new file mode 100644 index 0000000..688c363 --- /dev/null +++ b/game/rooms/test_kitchen/test_kitchen.tscn @@ -0,0 +1,53 @@ +[gd_scene format=3 uid="uid://dw2s1aomk3k0e"] + +[ext_resource type="Script" uid="uid://c7fcpp72w3dnj" path="res://addons/escoria-core/game/core-scripts/esc_room.gd" id="1_j38dn"] +[ext_resource type="PackedScene" uid="uid://bxxhvoforh8g8" path="res://game/characters/weener/weener.tscn" id="2_w42vq"] +[ext_resource type="Texture2D" uid="uid://cdv11spgcle4e" path="res://game/rooms/test_kitchen/kitchen.png" id="3_u6s3a"] +[ext_resource type="Script" uid="uid://fnwnfflaoeqp" path="res://addons/escoria-core/game/core-scripts/esc_background.gd" id="4_t26dx"] +[ext_resource type="Script" uid="uid://5pbcieq7gpdo" path="res://addons/escoria-core/game/core-scripts/esc_terrain.gd" id="5_xa5sy"] +[ext_resource type="Script" uid="uid://btt4cprco53ec" path="res://addons/escoria-core/game/core-scripts/esc_exit.gd" id="6_u6s3a"] +[ext_resource type="Script" uid="uid://chu4bvqgwxuts" path="res://addons/escoria-core/game/core-scripts/esc_interaction_location.gd" id="7_t26dx"] + +[sub_resource type="NavigationPolygon" id="NavigationPolygon_kamrl"] +vertices = PackedVector2Array(1910, 739.21094, 1910, 924.2969, 1732.2266, 896.4375, 1764.6719, 710.34375, 854.9922, 1028.0234, 819.60156, 981.1328, 809.28906, 895.1875, 919.78125, 897.10156, 1560.5391, 1031.9766, 1072.9063, 626.2969, 1131.9063, 571.97656, 1752.8125, 570.02344) +polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 5, 6, 7, 8), PackedInt32Array(8, 7, 9, 10, 11, 3, 2)]) +outlines = Array[PackedVector2Array]([PackedVector2Array(1920, 731, 1774, 702, 1762, 560, 1128, 562, 1065, 620, 914, 887, 798, 885, 810, 985, 850, 1038, 1564, 1042, 1735, 907, 1920, 936)]) + +[node name="TestKitchen" type="Node2D" unique_id=205166811] +script = ExtResource("1_j38dn") +global_id = "kitchen" +esc_script = "res://game/rooms/test_kitchen/room_kitchen.esc" +player_scene = ExtResource("2_w42vq") +metadata/_custom_type_script = "uid://c7fcpp72w3dnj" + +[node name="escbackground" type="TextureRect" parent="." unique_id=2070587804] +offset_right = 40.0 +offset_bottom = 40.0 +mouse_filter = 2 +texture = ExtResource("3_u6s3a") +script = ExtResource("4_t26dx") +metadata/_custom_type_script = "uid://fnwnfflaoeqp" + +[node name="ESCTerrain" type="Node2D" parent="." unique_id=568229096] +script = ExtResource("5_xa5sy") +metadata/_custom_type_script = "uid://5pbcieq7gpdo" + +[node name="NavigationRegion2D" type="NavigationRegion2D" parent="ESCTerrain" unique_id=1889897109] +navigation_polygon = SubResource("NavigationPolygon_kamrl") + +[node name="ESCExit" type="Area2D" parent="." unique_id=1918860245] +position = Vector2(1837, 822) +script = ExtResource("6_u6s3a") +target_scene = "uid://cbtqk144uipsv" +global_id = "exit_to_test" +tooltip_name = "To test" +metadata/_custom_type_script = "uid://btt4cprco53ec" + +[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="ESCExit" unique_id=585897778] +polygon = PackedVector2Array(-71, -112, 83, -81, 83, 117, -107, 88) + +[node name="ESCInteractionLocation" type="Marker2D" parent="ESCExit" unique_id=137473435] +position = Vector2(-92, -12) +script = ExtResource("7_t26dx") +global_id = "kitchen_exit" +metadata/_custom_type_script = "uid://chu4bvqgwxuts" diff --git a/game/shaders/outline.gdshader b/game/shaders/outline.gdshader new file mode 100644 index 0000000..a925059 --- /dev/null +++ b/game/shaders/outline.gdshader @@ -0,0 +1,64 @@ +// Source : https://godotshaders.com/shader/2d-outline-inline/ + +shader_type canvas_item; + +uniform vec4 color : source_color = vec4(1.0); +uniform float width : hint_range(0, 10) = 1.0; +uniform int pattern : hint_range(0, 2) = 0; // diamond, circle, square +uniform bool inside = false; +uniform bool add_margins = true; // only useful when inside is false + +void vertex() { + if (add_margins) { + VERTEX += (UV * 2.0 - 1.0) * width; + } +} + +bool hasContraryNeighbour(vec2 uv, vec2 texture_pixel_size, sampler2D texture) { + for (float i = -ceil(width); i <= ceil(width); i++) { + float x = abs(i) > width ? width * sign(i) : i; + float offset; + + if (pattern == 0) { + offset = width - abs(x); + } else if (pattern == 1) { + offset = floor(sqrt(pow(width + 0.5, 2) - x * x)); + } else if (pattern == 2) { + offset = width; + } + + for (float j = -ceil(offset); j <= ceil(offset); j++) { + float y = abs(j) > offset ? offset * sign(j) : j; + vec2 xy = uv + texture_pixel_size * vec2(x, y); + + if ((xy != clamp(xy, vec2(0.0), vec2(1.0)) || texture(texture, xy).a <= 0.0) == inside) { + return true; + } + } + } + + return false; +} + +void fragment() { + vec2 uv = UV; + + if (add_margins) { + vec2 texture_pixel_size = vec2(1.0) / (vec2(1.0) / TEXTURE_PIXEL_SIZE + vec2(width * 2.0)); + + uv = (uv - texture_pixel_size * width) * TEXTURE_PIXEL_SIZE / texture_pixel_size; + + if (uv != clamp(uv, vec2(0.0), vec2(1.0))) { + COLOR.a = 0.0; + } else { + COLOR = texture(TEXTURE, uv); + } + } else { + COLOR = texture(TEXTURE, uv); + } + + if ((COLOR.a > 0.0) == inside && hasContraryNeighbour(uv, TEXTURE_PIXEL_SIZE, TEXTURE)) { + COLOR.rgb = inside ? mix(COLOR.rgb, color.rgb, color.a) : color.rgb; + COLOR.a += (1.0 - COLOR.a) * color.a; + } +} \ No newline at end of file diff --git a/game/shaders/outline.gdshader.uid b/game/shaders/outline.gdshader.uid new file mode 100644 index 0000000..b3f146f --- /dev/null +++ b/game/shaders/outline.gdshader.uid @@ -0,0 +1 @@ +uid://crapjow6genqs diff --git a/game/shaders/shadermaterial_outline_green5px.tres b/game/shaders/shadermaterial_outline_green5px.tres new file mode 100644 index 0000000..fab515d --- /dev/null +++ b/game/shaders/shadermaterial_outline_green5px.tres @@ -0,0 +1,11 @@ +[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://bredlcniiy5bp"] + +[ext_resource type="Shader" path="res://game/shaders/outline.gdshader" id="1_2bktm"] + +[resource] +shader = ExtResource("1_2bktm") +shader_parameter/color = Color(0, 1, 0, 1) +shader_parameter/width = 5.0 +shader_parameter/pattern = 0 +shader_parameter/inside = false +shader_parameter/add_margins = true diff --git a/game/translations/game.csv.import b/game/translations/game.csv.import new file mode 100644 index 0000000..c9d5bc5 --- /dev/null +++ b/game/translations/game.csv.import @@ -0,0 +1,19 @@ +[remap] + +importer="csv_translation" +type="Translation" +uid="uid://cbqpt1hb4b1o2" + +[deps] + +files=["res://game/translations/game.en.translation", "res://game/translations/game.fr.translation", "res://game/translations/game.de.translation", "res://game/translations/game.es.translation"] + +source_file="res://game/translations/game.csv" +dest_files=["res://game/translations/game.en.translation", "res://game/translations/game.fr.translation", "res://game/translations/game.de.translation", "res://game/translations/game.es.translation"] + +[params] + +compress=1 +delimiter=0 +unescape_keys=false +unescape_translations=true diff --git a/game/translations/main_menu.csv.import b/game/translations/main_menu.csv.import new file mode 100644 index 0000000..7c1d77e --- /dev/null +++ b/game/translations/main_menu.csv.import @@ -0,0 +1,19 @@ +[remap] + +importer="csv_translation" +type="Translation" +uid="uid://dhvwha7pjvms6" + +[deps] + +files=["res://game/translations/main_menu.en.translation", "res://game/translations/main_menu.fr.translation", "res://game/translations/main_menu.de.translation", "res://game/translations/main_menu.es.translation"] + +source_file="res://game/translations/main_menu.csv" +dest_files=["res://game/translations/main_menu.en.translation", "res://game/translations/main_menu.fr.translation", "res://game/translations/main_menu.de.translation", "res://game/translations/main_menu.es.translation"] + +[params] + +compress=1 +delimiter=0 +unescape_keys=false +unescape_translations=true diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..c1d88c3 Binary files /dev/null and b/icon.png differ diff --git a/icon.png.import b/icon.png.import new file mode 100644 index 0000000..0f1e6fc --- /dev/null +++ b/icon.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bex7crp3v5020" +path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/icon.svg b/icon.svg deleted file mode 100644 index 9d8b7fa..0000000 --- a/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import deleted file mode 100644 index 03e39d3..0000000 --- a/icon.svg.import +++ /dev/null @@ -1,43 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b112kbpvaevfa" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/mus/intro.wav b/mus/intro.wav deleted file mode 100644 index e1bfb69..0000000 Binary files a/mus/intro.wav and /dev/null differ diff --git a/mus/intro.wav.import b/mus/intro.wav.import deleted file mode 100644 index 376dbc5..0000000 --- a/mus/intro.wav.import +++ /dev/null @@ -1,24 +0,0 @@ -[remap] - -importer="wav" -type="AudioStreamWAV" -uid="uid://coes42bdgqlbh" -path="res://.godot/imported/intro.wav-6a3a8645ea7c599bb4799e8b7aa4ac0f.sample" - -[deps] - -source_file="res://mus/intro.wav" -dest_files=["res://.godot/imported/intro.wav-6a3a8645ea7c599bb4799e8b7aa4ac0f.sample"] - -[params] - -force/8_bit=false -force/mono=false -force/max_rate=false -force/max_rate_hz=44100 -edit/trim=false -edit/normalize=false -edit/loop_mode=0 -edit/loop_begin=0 -edit/loop_end=-1 -compress/mode=2 diff --git a/project.godot b/project.godot index 22de0a0..0845e8a 100644 --- a/project.godot +++ b/project.godot @@ -15,41 +15,111 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true [application] config/name="Moloch" -run/main_scene="res://scenes/ui/title.tscn" -config/features=PackedStringArray("4.6", "Forward Plus") -config/icon="res://icon.svg" +run/main_scene="res://addons/escoria-core/game/main_scene.tscn" +config/features=PackedStringArray("4.6") +boot_splash/stretch_mode=0 +boot_splash/use_filter=false +boot_splash/image="res://addons/escoria-core/design/escoria-logo-small.png" +config/icon="res://icon.png" -[autoload] +[audio] -Menu="*uid://dqhxjaxlgy4yf" -Inventory="*uid://dsnx5a3q0ffv0" +buses/default_bus_layout="res://addons/escoria-core/buses/default_bus_layout.tres" -[debug] +[autoload] -gdscript/warnings/shadowed_variable=0 +escoria="*res://addons/escoria-core/game/esc_autoload.gd" [display] window/size/viewport_width=1920 window/size/viewport_height=1080 -window/size/window_width_override=1080 -window/size/window_height_override=720 window/stretch/mode="canvas_items" +[editor] + +search_in_file_extensions=PackedStringArray("gd", "shader", "esc") + +[editor_plugins] + +enabled=PackedStringArray("res://addons/escoria-core/plugin.cfg", "res://addons/escoria-dialog-simple/plugin.cfg", "res://addons/escoria-ui-simplemouse/plugin.cfg", "res://addons/escoria-wizard/plugin.cfg") + +[escoria] + +main/game_version="0.1.0" +main/game_start_script="res://game.esc" +main/force_quit=true +main/command_directories=["res://addons/escoria-core/game/core-scripts/esc/commands"] +main/text_lang="en_US" +main/voice_lang="en_US" +main/savegames_path="res://saves/" +main/settings_path="user://" +main/game_migration_path="" +debug/terminate_on_warnings=false +debug/terminate_on_errors=true +debug/development_lang="en" +debug/log_level="DEBUG" +debug/log_file_path="user://" +debug/crash_message="We're sorry, but the game crashed. Please send us the following files: + +%s" +debug/enable_room_selector=true +debug/room_selector_room_dir="res://game/rooms" +ui/game_scene="res://addons/escoria-ui-simplemouse/game.tscn" +ui/inventory_items_path="res://game/items/inventory" +ui/default_transition="instant" +ui/transition_paths=["res://addons/escoria-core/game/scenes/transitions/shaders/"] +ui/inventory_item_size=Vector2(72, 72) +ui/dialog_managers=["res://addons/escoria-dialog-simple/esc_dialog_simple.gd"] +sound/master_volume=1 +sound/music_volume=1 +sound/sfx_volume=1 +sound/speech_volume=1 +sound/speech_folder="res://game/speech" +sound/speech_extension="ogg" +sound/ambient_volume=1 +platform/skip_cache=false +platform/skip_cache.mobile=true +sound/speech_enabled=1 +ui/tooltip_follows_mouse=false +ui/dialogs_chooser="res://addons/escoria-core/ui_library/dialogs/text_dialog_chooser.tscn" +ui/default_dialog_scene="res://addons/escoria-core/ui_library/dialogs/floating_dialog_player.tscn" +main/action_default_script="res://action_defaults.esc" +dialog_simple/text_speed_per_character=0.1 +dialog_simple/fast_text_speed_per_character=0.25 +ui/default_dialog_type="floating" +dialog_simple/avatars_path="res://game/dialog_avatars" +dialog_simple/text_time_per_letter_ms=100 +dialog_simple/text_time_per_fast_letter_ms=25 +dialog_simple/clear_text_by_click_only=false +dialog_simple/reading_speed_in_wpm=200 +dialog_simple/left_click_action="Speed up" +dialog_simple/stop_talking_animation_on="End of audio" +debug/enable_hover_stack_viewer=true +main/escoria_version="4.0-alpha" +debug/perform_script_analysis_at_runtime=true +wizard/path_to_rooms="res://rooms" + +[file_customization] + +folder_colors={ +"res://game/rooms/": "teal" +} + [input] -ui_menu={ +esc_show_debug_prompt={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -] -} -interact={ -"deadzone": 0.2, -"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) -] -} -set_target={ -"deadzone": 0.2, -"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194333,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } + +[network] + +limits/debugger/max_chars_per_second=99999999 + +[rendering] + +textures/canvas_textures/default_texture_filter=0 +textures/vram_compression/import_etc2_astc=true +environment/defaults/default_environment="res://default_env.tres" diff --git a/room/kitchen/kitchen.png b/room/kitchen/kitchen.png deleted file mode 100644 index 71d6fa9..0000000 Binary files a/room/kitchen/kitchen.png and /dev/null differ diff --git a/room/kitchen/kitchen.png.import b/room/kitchen/kitchen.png.import deleted file mode 100644 index cf4bfd1..0000000 --- a/room/kitchen/kitchen.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://domalafsxaubx" -path="res://.godot/imported/kitchen.png-4701b051c0d81b7628727c36145d09f6.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://room/kitchen/kitchen.png" -dest_files=["res://.godot/imported/kitchen.png-4701b051c0d81b7628727c36145d09f6.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/room/kitchen/kitchenbottomlayer.png b/room/kitchen/kitchenbottomlayer.png deleted file mode 100644 index 99b7d57..0000000 Binary files a/room/kitchen/kitchenbottomlayer.png and /dev/null differ diff --git a/room/kitchen/kitchenbottomlayer.png.import b/room/kitchen/kitchenbottomlayer.png.import deleted file mode 100644 index c72c4dd..0000000 --- a/room/kitchen/kitchenbottomlayer.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://ep6wdkye3roh" -path="res://.godot/imported/kitchenbottomlayer.png-aa79e5cdef6798122640ff12fcae8592.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://room/kitchen/kitchenbottomlayer.png" -dest_files=["res://.godot/imported/kitchenbottomlayer.png-aa79e5cdef6798122640ff12fcae8592.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/room/kitchen/kitchentoplayer.png b/room/kitchen/kitchentoplayer.png deleted file mode 100644 index 89b4da8..0000000 Binary files a/room/kitchen/kitchentoplayer.png and /dev/null differ diff --git a/room/kitchen/kitchentoplayer.png.import b/room/kitchen/kitchentoplayer.png.import deleted file mode 100644 index f559717..0000000 --- a/room/kitchen/kitchentoplayer.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dbajn6hm3stsh" -path="res://.godot/imported/kitchentoplayer.png-8901261216b514ba15a5f58eda6a3183.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://room/kitchen/kitchentoplayer.png" -dest_files=["res://.godot/imported/kitchentoplayer.png-8901261216b514ba15a5f58eda6a3183.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/room/start/room.jpg b/room/start/room.jpg deleted file mode 100644 index 4539b77..0000000 Binary files a/room/start/room.jpg and /dev/null differ diff --git a/room/start/room.jpg.import b/room/start/room.jpg.import deleted file mode 100644 index b4688a5..0000000 --- a/room/start/room.jpg.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cfn3ctcvyfjn0" -path="res://.godot/imported/room.jpg-9d1d4a884a7fee2b733d5359249a53db.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://room/start/room.jpg" -dest_files=["res://.godot/imported/room.jpg-9d1d4a884a7fee2b733d5359249a53db.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/room/start/testprop.png b/room/start/testprop.png deleted file mode 100644 index 032ca72..0000000 Binary files a/room/start/testprop.png and /dev/null differ diff --git a/room/start/testprop.png.import b/room/start/testprop.png.import deleted file mode 100644 index 1cf0966..0000000 --- a/room/start/testprop.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://det86lqkb5y4p" -path="res://.godot/imported/testprop.png-402e13894885a95ace74c4c142c49634.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://room/start/testprop.png" -dest_files=["res://.godot/imported/testprop.png-402e13894885a95ace74c4c142c49634.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/scenes/char/weener.gd b/scenes/char/weener.gd deleted file mode 100644 index b970b20..0000000 --- a/scenes/char/weener.gd +++ /dev/null @@ -1,110 +0,0 @@ -class_name Weener extends CharacterBody2D - -@onready var sprite := $Sprite -@onready var nav2d := $NavigationAgent2D - -const ref_speed := 500.0 -@onready var speed := ref_speed -@onready var target := position - -@onready var interactions := [] -var door: Door = null -#@onready var inventory := [] - -static var spawnpoint: int = -1 -static var sprite_flip: bool - -var input_listen := true - -func _physics_process(delta: float) -> void: - set_target() - if input_listen: - navigate(delta) - - -func _ready() -> void: - nav2d.navigation_finished.connect(func() -> void: - if door != null: - travel(door) - ) - sprite.flip_h = sprite_flip - - -func _exit_tree() -> void: - sprite_flip = sprite.flip_h - - -func set_target() -> void: - if Input.is_action_just_pressed("set_target"): - if !interactions.is_empty() and interactions[0].mouse_in: - interact(interactions[0]) - else: - nav2d.target_position = get_global_mouse_position() - if position.distance_to(nav2d.target_position) > 750: - speed = ref_speed * 1.8 - else: - speed = ref_speed - - -func navigate(delta: float) -> void: - if nav2d.is_navigation_finished(): - sprite.play("stand") - return - var next_path_position: Vector2 = nav2d.get_next_path_position() - velocity = global_position.direction_to(next_path_position) * speed - sprite.flip_h = velocity.x > 0 - position += velocity * delta - if speed > ref_speed: - sprite.play("run") - else: - sprite.play("walk") - - -func _on_area_entered(area): - interactions.insert(0, area) - - -func _on_area_exited(area): - interactions.erase(area) - - -func _on_walk_detect_area_entered(area: Area2D) -> void: - door = area as Door - - -func _on_walk_detect_area_exited(_area: Area2D) -> void: - door = null - - -func interact(item: Item) -> void: - match item.item_type: - Item.ItemTypes.NONE: - return - Item.ItemTypes.TALK: - print(" ".join(item.text)) - Item.ItemTypes.PICKUP: - input_listen = false - sprite.play("grab") - sprite.animation_finished.connect(func() -> void: input_listen = true) - Inventory.inventory.set(item.item_id, false) - item.queue_free() - print(Inventory.inventory) - Item.ItemTypes.USE: - #item.execute(item.uses) - for use in item.uses: - match use: - Item.UseTypes.TAKE_ITEM: - if item.useable_item in Inventory.inventory: - Inventory.inventory.set(item.useable_item, true) - print(Inventory.inventory) - item.used = true - Item.UseTypes.CLEAR: - if item.used: - item.queue_free() - _: - break - - -func travel(d: Door) -> void: - spawnpoint = d.go_to_spawnpoint - get_tree().change_scene_to_file(d.target_scene) diff --git a/scenes/char/weener.gd.uid b/scenes/char/weener.gd.uid deleted file mode 100644 index 170ef28..0000000 --- a/scenes/char/weener.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bmudq0wqybjnw diff --git a/scenes/char/weener.tscn b/scenes/char/weener.tscn deleted file mode 100644 index 8ed1a80..0000000 --- a/scenes/char/weener.tscn +++ /dev/null @@ -1,109 +0,0 @@ -[gd_scene format=3 uid="uid://bt4utdbe577mj"] - -[ext_resource type="Script" uid="uid://bmudq0wqybjnw" path="res://scenes/char/weener.gd" id="1_5hljh"] -[ext_resource type="Texture2D" uid="uid://bje5rius281oi" path="res://spr/weener/crouch.png" id="1_6smxx"] -[ext_resource type="Texture2D" uid="uid://dk3ulxm2i0qm6" path="res://spr/weener/stand.png" id="1_b3dvs"] -[ext_resource type="Texture2D" uid="uid://cr80vs56knlqh" path="res://spr/weener/grab.png" id="1_ka3oh"] -[ext_resource type="Texture2D" uid="uid://bbcs52esee2pt" path="res://spr/weener/walk.png" id="2_tfg2r"] -[ext_resource type="Texture2D" uid="uid://b1bn5hf6uxfiw" path="res://spr/weener/run1.png" id="5_rsd08"] -[ext_resource type="Texture2D" uid="uid://0pued7jac5i2" path="res://spr/weener/run2.png" id="6_xgxdw"] - -[sub_resource type="SpriteFrames" id="SpriteFrames_ka3oh"] -animations = [{ -"frames": [{ -"duration": 1.0, -"texture": ExtResource("1_6smxx") -}], -"loop": true, -"name": &"crouch", -"speed": 5.0 -}, { -"frames": [{ -"duration": 1.0, -"texture": ExtResource("1_ka3oh") -}], -"loop": false, -"name": &"grab", -"speed": 2.0 -}, { -"frames": [{ -"duration": 1.0, -"texture": ExtResource("5_rsd08") -}, { -"duration": 1.0, -"texture": ExtResource("1_b3dvs") -}, { -"duration": 1.0, -"texture": ExtResource("6_xgxdw") -}, { -"duration": 1.0, -"texture": ExtResource("1_b3dvs") -}], -"loop": true, -"name": &"run", -"speed": 5.0 -}, { -"frames": [{ -"duration": 1.0, -"texture": ExtResource("1_b3dvs") -}], -"loop": false, -"name": &"stand", -"speed": 5.0 -}, { -"frames": [{ -"duration": 1.0, -"texture": ExtResource("2_tfg2r") -}, { -"duration": 1.0, -"texture": ExtResource("1_b3dvs") -}], -"loop": true, -"name": &"walk", -"speed": 3.0 -}] - -[sub_resource type="RectangleShape2D" id="RectangleShape2D_rsd08"] -size = Vector2(65.5, 150.25) - -[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_rsd08"] -radius = 116.0 -height = 444.0 - -[node name="Weener" type="CharacterBody2D" unique_id=1834042649] -script = ExtResource("1_5hljh") - -[node name="Sprite" type="AnimatedSprite2D" parent="." unique_id=918466208] -position = Vector2(0, -190) -sprite_frames = SubResource("SpriteFrames_ka3oh") -animation = &"stand" -autoplay = "stand" - -[node name="WalkCollision" type="CollisionShape2D" parent="." unique_id=1548923938] -rotation = 1.5707964 -shape = SubResource("RectangleShape2D_rsd08") - -[node name="NavigationAgent2D" type="NavigationAgent2D" parent="." unique_id=2038637548] - -[node name="InteractDetect" type="Area2D" parent="." unique_id=1466530066] -collision_mask = 2 - -[node name="InteractCollision" type="CollisionShape2D" parent="InteractDetect" unique_id=526603123] -position = Vector2(0, -205) -shape = SubResource("CapsuleShape2D_rsd08") - -[node name="WalkDetect" type="Area2D" parent="." unique_id=1208097571] - -[node name="WalkCollision" type="CollisionShape2D" parent="WalkDetect" unique_id=1174332734] -rotation = 1.5707964 -shape = SubResource("RectangleShape2D_rsd08") - -[node name="Inventory" type="Label" parent="." unique_id=1503183978] -offset_top = -471.0 -offset_right = 40.0 -offset_bottom = -448.0 - -[connection signal="area_entered" from="InteractDetect" to="." method="_on_area_entered"] -[connection signal="area_exited" from="InteractDetect" to="." method="_on_area_exited"] -[connection signal="area_entered" from="WalkDetect" to="." method="_on_walk_detect_area_entered"] -[connection signal="area_exited" from="WalkDetect" to="." method="_on_walk_detect_area_exited"] diff --git a/scenes/item/door.gd b/scenes/item/door.gd deleted file mode 100644 index 75b8d84..0000000 --- a/scenes/item/door.gd +++ /dev/null @@ -1,14 +0,0 @@ -class_name Door extends Area2D - -@export_file var target_scene -@export var go_to_spawnpoint: int -@export var flip_sprite: bool = false - -var mouse_in := false - -#func _on_mouse_entered() -> void: - #mouse_in = true -# -# -#func _on_mouse_exited() -> void: - #mouse_in = false diff --git a/scenes/item/door.gd.uid b/scenes/item/door.gd.uid deleted file mode 100644 index e0ea3be..0000000 --- a/scenes/item/door.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://br4thmxp67m51 diff --git a/scenes/item/door.tscn b/scenes/item/door.tscn deleted file mode 100644 index 901f8e1..0000000 --- a/scenes/item/door.tscn +++ /dev/null @@ -1,14 +0,0 @@ -[gd_scene format=3 uid="uid://ldwt8qv482qa"] - -[ext_resource type="Script" uid="uid://br4thmxp67m51" path="res://scenes/item/door.gd" id="1_tbo8r"] - -[node name="Door" type="Area2D" unique_id=359892745] -collision_mask = 0 -script = ExtResource("1_tbo8r") - -[node name="Sprite" type="Sprite2D" parent="." unique_id=89747108] - -[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="." unique_id=348112285] - -[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] -[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] diff --git a/scenes/item/inventory.gd b/scenes/item/inventory.gd deleted file mode 100644 index 84bf1e2..0000000 --- a/scenes/item/inventory.gd +++ /dev/null @@ -1,12 +0,0 @@ -extends Node - -var inventory = {} - -# structure -# item, dictionary of: -# prop 1 -# prop 2 etc... -# inventory, dictionary of dictionaries with value used -# inventory items have a used property -# on scene load, if item exists in inventory, do not spawn -# once item is expended, used is true, then the item also does not spawn diff --git a/scenes/item/inventory.gd.uid b/scenes/item/inventory.gd.uid deleted file mode 100644 index 8d523c7..0000000 --- a/scenes/item/inventory.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dsnx5a3q0ffv0 diff --git a/scenes/item/inventory.tscn b/scenes/item/inventory.tscn deleted file mode 100644 index 1d8f705..0000000 --- a/scenes/item/inventory.tscn +++ /dev/null @@ -1,3 +0,0 @@ -[gd_scene format=3 uid="uid://ccrfeohraf7yl"] - -[node name="Inventory" type="Node2D" unique_id=739717319] diff --git a/scenes/item/inventory_item.gd b/scenes/item/inventory_item.gd deleted file mode 100644 index 10d18f8..0000000 --- a/scenes/item/inventory_item.gd +++ /dev/null @@ -1 +0,0 @@ -class_name InventoryItem extends Node diff --git a/scenes/item/inventory_item.gd.uid b/scenes/item/inventory_item.gd.uid deleted file mode 100644 index 8cf1b07..0000000 --- a/scenes/item/inventory_item.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dlyiamkvjhmkt diff --git a/scenes/item/item.gd b/scenes/item/item.gd deleted file mode 100644 index dc37a40..0000000 --- a/scenes/item/item.gd +++ /dev/null @@ -1,42 +0,0 @@ -class_name Item extends Area2D - -enum ItemTypes { NONE, TALK, PICKUP, SCRIPT, USE, ANIM } - -enum UseTypes { TAKE_ITEM, FLAG, ANIM, CLEAR } - -# play a little animation, check for received item and: -# take item -# trigger flag -> global flag for a door unlock, etc -# change sprite -# instantiate an item (flag?) -# clear item - -@export var item_type := ItemTypes.NONE -@export var item_id := "" - -@export var text: Array[String] -@export var item_script: Script - -@export var interact_anim := "" - -@export var uses: Array[UseTypes] -@export var useable_item := "" -@export var use_anim := "" -static var used := false - -var mouse_in := false - -func _ready() -> void: - if (item_type == ItemTypes.PICKUP and item_id in Inventory.inventory) or (item_type == ItemTypes.USE and used): - queue_free() - - -func _on_mouse_entered() -> void: - mouse_in = true - - -func _on_mouse_exited() -> void: - mouse_in = false - - -#func execute(uses) -> void: diff --git a/scenes/item/item.gd.uid b/scenes/item/item.gd.uid deleted file mode 100644 index 8200bc7..0000000 --- a/scenes/item/item.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bw4bw3o6okiy4 diff --git a/scenes/item/item.tscn b/scenes/item/item.tscn deleted file mode 100644 index c2d21d8..0000000 --- a/scenes/item/item.tscn +++ /dev/null @@ -1,19 +0,0 @@ -[gd_scene format=3 uid="uid://xn4bxjj1vuim"] - -[ext_resource type="Script" uid="uid://bw4bw3o6okiy4" path="res://scenes/item/item.gd" id="1_vcqat"] - -[sub_resource type="CircleShape2D" id="CircleShape2D_vijbe"] -radius = 76.105194 - -[node name="Item" type="Area2D" unique_id=359892745] -collision_layer = 2 -collision_mask = 0 -script = ExtResource("1_vcqat") - -[node name="Sprite" type="Sprite2D" parent="." unique_id=89747108] - -[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=574625641] -shape = SubResource("CircleShape2D_vijbe") - -[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] -[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] diff --git a/scenes/room/kitchen.tscn b/scenes/room/kitchen.tscn deleted file mode 100644 index 74fabf4..0000000 --- a/scenes/room/kitchen.tscn +++ /dev/null @@ -1,32 +0,0 @@ -[gd_scene format=3 uid="uid://c40vhh8wtss4b"] - -[ext_resource type="PackedScene" uid="uid://b1s6v121kb2mi" path="res://scenes/room/room.tscn" id="1_4qnmt"] -[ext_resource type="Texture2D" uid="uid://domalafsxaubx" path="res://room/kitchen/kitchen.png" id="2_6oufn"] -[ext_resource type="PackedScene" uid="uid://ldwt8qv482qa" path="res://scenes/item/door.tscn" id="3_u4rop"] - -[sub_resource type="NavigationPolygon" id="NavigationPolygon_6oufn"] -vertices = PackedVector2Array(856.2578, 1032.0703, 817.2578, 913.3281, 916.02344, 900.28906, 1515.4922, 1038.9609, 1701.0469, 888.5078, 1123.5625, 563.03906, 1748.4766, 567.9219, 1755.3672, 703.6172, 1910, 916.5625, 1910, 748.5) -polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 3, 2, 5, 6, 7), PackedInt32Array(8, 4, 7, 9)]) -outlines = Array[PackedVector2Array]([PackedVector2Array(1704, 899, 1519, 1049, 849, 1042, 804, 905, 910, 891, 1118, 553, 1758, 558, 1765, 696, 1920, 741, 1920, 928)]) - -[node name="Kitchen" unique_id=2088608692 instance=ExtResource("1_4qnmt")] - -[node name="Room" parent="." index="0" unique_id=1997957082] -texture = ExtResource("2_6oufn") - -[node name="Weener" parent="." index="1" unique_id=1834042649] -position = Vector2(1671, 837) - -[node name="NavigationRegion2D" parent="." index="2" unique_id=1498960834] -navigation_polygon = SubResource("NavigationPolygon_6oufn") - -[node name="Spawnpoint" parent="." index="3" unique_id=1413507405] -position = Vector2(1671, 822) - -[node name="Door" parent="." index="4" unique_id=359892745 instance=ExtResource("3_u4rop")] -target_scene = "uid://cec247tmm7ait" - -[node name="CollisionPolygon2D" parent="Door" index="1" unique_id=348112285] -polygon = PackedVector2Array(1803, 702, 1799, 927, 1920, 928, 1920, 705) - -[editable path="Door"] diff --git a/scenes/room/room.gd b/scenes/room/room.gd deleted file mode 100644 index fe8cf70..0000000 --- a/scenes/room/room.gd +++ /dev/null @@ -1,7 +0,0 @@ -extends Node2D - -@export var entrances: Array[Marker2D] = [] - -func _ready() -> void: - if $Weener.spawnpoint != -1: - $Weener.position = entrances[$Weener.spawnpoint].global_position diff --git a/scenes/room/room.gd.uid b/scenes/room/room.gd.uid deleted file mode 100644 index 05233ba..0000000 --- a/scenes/room/room.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://drthtcpmeydm6 diff --git a/scenes/room/room.tscn b/scenes/room/room.tscn deleted file mode 100644 index 145ee48..0000000 --- a/scenes/room/room.tscn +++ /dev/null @@ -1,19 +0,0 @@ -[gd_scene format=3 uid="uid://b1s6v121kb2mi"] - -[ext_resource type="Script" uid="uid://drthtcpmeydm6" path="res://scenes/room/room.gd" id="1_kxljb"] -[ext_resource type="PackedScene" uid="uid://bt4utdbe577mj" path="res://scenes/char/weener.tscn" id="2_vaubn"] -[ext_resource type="PackedScene" uid="uid://chwbsexrxbi4a" path="res://scenes/room/spawnpoint.tscn" id="3_y1kpu"] - -[node name="Room" type="Node2D" unique_id=2088608692 node_paths=PackedStringArray("entrances")] -script = ExtResource("1_kxljb") -entrances = [NodePath("Spawnpoint")] - -[node name="Room" type="Sprite2D" parent="." unique_id=1997957082] -centered = false - -[node name="Weener" parent="." unique_id=1834042649 instance=ExtResource("2_vaubn")] -position = Vector2(1127, 956) - -[node name="NavigationRegion2D" type="NavigationRegion2D" parent="." unique_id=1498960834] - -[node name="Spawnpoint" parent="." unique_id=1413507405 instance=ExtResource("3_y1kpu")] diff --git a/scenes/room/spawnpoint.gd b/scenes/room/spawnpoint.gd deleted file mode 100644 index fd74c79..0000000 --- a/scenes/room/spawnpoint.gd +++ /dev/null @@ -1 +0,0 @@ -extends Marker2D diff --git a/scenes/room/spawnpoint.gd.uid b/scenes/room/spawnpoint.gd.uid deleted file mode 100644 index c1ecb4c..0000000 --- a/scenes/room/spawnpoint.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://j2ctii1213or diff --git a/scenes/room/spawnpoint.tscn b/scenes/room/spawnpoint.tscn deleted file mode 100644 index 9c217ab..0000000 --- a/scenes/room/spawnpoint.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene format=3 uid="uid://chwbsexrxbi4a"] - -[ext_resource type="Script" uid="uid://j2ctii1213or" path="res://scenes/room/spawnpoint.gd" id="1_xdtlq"] - -[node name="Spawnpoint" type="Marker2D" unique_id=1413507405] -script = ExtResource("1_xdtlq") diff --git a/scenes/room/start.tscn b/scenes/room/start.tscn deleted file mode 100644 index df92143..0000000 --- a/scenes/room/start.tscn +++ /dev/null @@ -1,78 +0,0 @@ -[gd_scene format=3 uid="uid://cec247tmm7ait"] - -[ext_resource type="PackedScene" uid="uid://b1s6v121kb2mi" path="res://scenes/room/room.tscn" id="1_c20yv"] -[ext_resource type="Texture2D" uid="uid://cfn3ctcvyfjn0" path="res://room/start/room.jpg" id="1_ft2pm"] -[ext_resource type="PackedScene" uid="uid://ldwt8qv482qa" path="res://scenes/item/door.tscn" id="3_tspce"] -[ext_resource type="PackedScene" uid="uid://xn4bxjj1vuim" path="res://scenes/item/item.tscn" id="3_uxhtn"] -[ext_resource type="Texture2D" uid="uid://det86lqkb5y4p" path="res://room/start/testprop.png" id="4_q15md"] -[ext_resource type="PackedScene" uid="uid://chwbsexrxbi4a" path="res://scenes/room/spawnpoint.tscn" id="5_c20yv"] - -[sub_resource type="NavigationPolygon" id="NavigationPolygon_tspce"] -vertices = PackedVector2Array(9.0234375, 884.4922, 196.39844, 766.34375, 211.64844, 814.03125, 1180.5234, 843, 1738.7969, 863.5625, 1910, 981.2578, 1910, 1070, 9.953125, 1070, 699.8828, 843, 619.5703, 725.96094, 674.9297, 724.25, 344.52344, 807.15625, 1243.9688, 699.21875, 1524.6563, 749.5469, 302.3672, 769.89844) -polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2), PackedInt32Array(3, 4, 5, 6, 7, 0, 8), PackedInt32Array(9, 10, 8, 11), PackedInt32Array(3, 12, 13, 4), PackedInt32Array(2, 14, 11), PackedInt32Array(11, 8, 0, 2)]) -outlines = Array[PackedVector2Array]([PackedVector2Array(202, 751, 217.65723, 799.9918, 304, 758, 347, 796, 618, 716, 683, 714, 708, 833, 1174, 833, 1238, 688, 1528, 740, 1744, 855, 1920, 976, 1920, 1080, 0, 1080, -1, 879)]) - -[node name="Room" unique_id=2088608692 node_paths=PackedStringArray("entrances") instance=ExtResource("1_c20yv")] -entrances = [NodePath("Spawnpoint"), NodePath("From Corridor")] - -[node name="Room" parent="." index="0" unique_id=1997957082] -texture = ExtResource("1_ft2pm") - -[node name="NavigationRegion2D" parent="." index="2" unique_id=1498960834] -visible = false -navigation_polygon = SubResource("NavigationPolygon_tspce") - -[node name="Kitchen" parent="." index="3" unique_id=359892745 instance=ExtResource("3_tspce")] -position = Vector2(90, 864) -target_scene = "uid://c40vhh8wtss4b" - -[node name="CollisionPolygon2D" parent="Kitchen" index="1" unique_id=348112285] -polygon = PackedVector2Array(-90, 91, -1, 107, 101, 80, 139, -4, 119, -62, 96, -523, -90, -445) - -[node name="Item" parent="." index="4" unique_id=1732180617 instance=ExtResource("3_uxhtn")] -position = Vector2(1626, 608) -item_type = 2 -item_id = "test1" - -[node name="Sprite" parent="Item" index="0" unique_id=89747108] -texture = ExtResource("4_q15md") - -[node name="Item3" parent="." index="5" unique_id=1012362187 instance=ExtResource("3_uxhtn")] -position = Vector2(1806, 649) -item_type = 2 -item_id = "test3" - -[node name="Sprite" parent="Item3" index="0" unique_id=89747108] -texture = ExtResource("4_q15md") - -[node name="Item2" parent="." index="6" unique_id=740081124 instance=ExtResource("3_uxhtn")] -position = Vector2(500, 417) -item_type = 1 -item_id = "test2" -text = Array[String](["abababab"]) - -[node name="Sprite" parent="Item2" index="0" unique_id=89747108] -texture = ExtResource("4_q15md") - -[node name="Item4" parent="." index="7" unique_id=811205574 instance=ExtResource("3_uxhtn")] -position = Vector2(789, 956) -item_type = 4 -item_id = "test4" -uses = Array[int]([0, 3]) -useable_item = "test1" - -[node name="Sprite" parent="Item4" index="0" unique_id=89747108] -texture = ExtResource("4_q15md") - -[node name="From Corridor" parent="." index="8" unique_id=1847337443 instance=ExtResource("5_c20yv")] -position = Vector2(1355, 763) - -[node name="Spawnpoint" parent="." index="9" unique_id=1413507405] -position = Vector2(209, 890) - -[editable path="Kitchen"] -[editable path="Item"] -[editable path="Item3"] -[editable path="Item2"] -[editable path="Item4"] -[editable path="Spawnpoint"] diff --git a/scenes/ui/menu.gd b/scenes/ui/menu.gd deleted file mode 100644 index a60336b..0000000 --- a/scenes/ui/menu.gd +++ /dev/null @@ -1,42 +0,0 @@ -extends CanvasLayer - -func pause() -> void: - Menu.visible = true - get_tree().paused = true - - -func resume() -> void: - Menu.visible = false - get_tree().paused = false - - -func _ready() -> void: - resume() - - -func _input(event: InputEvent) -> void: - if event.is_action_pressed("ui_menu") and get_tree().current_scene.scene_file_path != "res://scenes/ui/title.tscn": - if get_tree().paused: - resume() - else: - pause() - -func _on_start_pressed() -> void: - get_tree().change_scene_to_file("res://scenes/room/start.tscn") - - -func _on_main_menu_pressed() -> void: - resume() - get_tree().change_scene_to_file("res://scenes/ui/title.tscn") - - -func _on_quit_pressed() -> void: - get_tree().quit() - - -func _on_options_pressed() -> void: - if !has_node("Options"): - var options = load("res://scenes/ui/options.tscn").instantiate() - add_child(options) - options.menu_node = self - process_mode = Node.PROCESS_MODE_DISABLED diff --git a/scenes/ui/menu.gd.uid b/scenes/ui/menu.gd.uid deleted file mode 100644 index 45d6d4f..0000000 --- a/scenes/ui/menu.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://befvsvog6282s diff --git a/scenes/ui/menu.tscn b/scenes/ui/menu.tscn deleted file mode 100644 index cc25d7b..0000000 --- a/scenes/ui/menu.tscn +++ /dev/null @@ -1,76 +0,0 @@ -[gd_scene format=3 uid="uid://dqhxjaxlgy4yf"] - -[ext_resource type="Script" uid="uid://befvsvog6282s" path="res://scenes/ui/menu.gd" id="1_wedc3"] -[ext_resource type="PackedScene" uid="uid://dj2dk1g7jnk23" path="res://scenes/ui/title_button.tscn" id="2_c2n5p"] - -[node name="Menu" type="CanvasLayer" unique_id=1912348469] -process_mode = 3 -script = ExtResource("1_wedc3") - -[node name="ColorRect" type="ColorRect" parent="." unique_id=1816673840] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(0, 0, 0, 0.60784316) - -[node name="Menu" type="Control" parent="." unique_id=115089705] -layout_mode = 3 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 4 -size_flags_vertical = 4 - -[node name="PanelContainer" type="PanelContainer" parent="Menu" unique_id=1414562164] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -260.5 -offset_top = -318.25 -offset_right = 260.5 -offset_bottom = 318.25 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="Menu/PanelContainer" unique_id=604076564] -layout_mode = 2 - -[node name="Label" type="Label" parent="Menu/PanelContainer/VBoxContainer" unique_id=1102397096] -layout_mode = 2 -theme_override_font_sizes/font_size = 100 -text = "MENU!!!!!!!!" -horizontal_alignment = 1 -uppercase = true - -[node name="Save" parent="Menu/PanelContainer/VBoxContainer" unique_id=1597537067 instance=ExtResource("2_c2n5p")] -layout_mode = 2 -text = "Save" - -[node name="Load" parent="Menu/PanelContainer/VBoxContainer" unique_id=1726486889 instance=ExtResource("2_c2n5p")] -layout_mode = 2 -text = "Load" - -[node name="Options" parent="Menu/PanelContainer/VBoxContainer" unique_id=674602521 instance=ExtResource("2_c2n5p")] -layout_mode = 2 -text = "Options" - -[node name="Main Menu" parent="Menu/PanelContainer/VBoxContainer" unique_id=1904714546 instance=ExtResource("2_c2n5p")] -layout_mode = 2 -text = "Main Menu" - -[node name="Quit" parent="Menu/PanelContainer/VBoxContainer" unique_id=1892162088 instance=ExtResource("2_c2n5p")] -layout_mode = 2 -text = "Quit" - -[connection signal="pressed" from="Menu/PanelContainer/VBoxContainer/Options" to="." method="_on_options_pressed"] -[connection signal="pressed" from="Menu/PanelContainer/VBoxContainer/Main Menu" to="." method="_on_main_menu_pressed"] -[connection signal="pressed" from="Menu/PanelContainer/VBoxContainer/Quit" to="." method="_on_quit_pressed"] diff --git a/scenes/ui/options.gd b/scenes/ui/options.gd deleted file mode 100644 index c172e04..0000000 --- a/scenes/ui/options.gd +++ /dev/null @@ -1,7 +0,0 @@ -extends CanvasLayer - -var menu_node: CanvasLayer - -func _on_close_pressed() -> void: - menu_node.process_mode = Node.PROCESS_MODE_ALWAYS - queue_free() diff --git a/scenes/ui/options.gd.uid b/scenes/ui/options.gd.uid deleted file mode 100644 index f0c89ba..0000000 --- a/scenes/ui/options.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ck4twnv274ivh diff --git a/scenes/ui/options.tscn b/scenes/ui/options.tscn deleted file mode 100644 index f2c0278..0000000 --- a/scenes/ui/options.tscn +++ /dev/null @@ -1,54 +0,0 @@ -[gd_scene format=3 uid="uid://i8ml66ykc8eh"] - -[ext_resource type="PackedScene" uid="uid://dj2dk1g7jnk23" path="res://scenes/ui/title_button.tscn" id="1_ad3a0"] -[ext_resource type="Script" uid="uid://ck4twnv274ivh" path="res://scenes/ui/options.gd" id="1_lr2b1"] - -[node name="Options" type="CanvasLayer" unique_id=1868787303] -process_mode = 3 -script = ExtResource("1_lr2b1") - -[node name="Control" type="Control" parent="." unique_id=1010669591] -layout_mode = 3 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -20.0 -offset_top = -20.0 -offset_right = 20.0 -offset_bottom = 20.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="Control" unique_id=551513181] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -50.0 -offset_top = -92.5 -offset_right = 50.0 -offset_bottom = 92.5 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="Label" type="Label" parent="Control/VBoxContainer" unique_id=1111325079] -layout_mode = 2 -theme_override_font_sizes/font_size = 50 -text = "OPTIONS" - -[node name="Button" parent="Control/VBoxContainer" unique_id=1597537067 instance=ExtResource("1_ad3a0")] -layout_mode = 2 - -[node name="Button2" parent="Control/VBoxContainer" unique_id=932377545 instance=ExtResource("1_ad3a0")] -layout_mode = 2 - -[node name="Close" parent="Control/VBoxContainer" unique_id=1306792286 instance=ExtResource("1_ad3a0")] -layout_mode = 2 -text = "Close -" - -[connection signal="pressed" from="Control/VBoxContainer/Close" to="." method="_on_close_pressed"] diff --git a/scenes/ui/title.tscn b/scenes/ui/title.tscn deleted file mode 100644 index 91d47e9..0000000 --- a/scenes/ui/title.tscn +++ /dev/null @@ -1,72 +0,0 @@ -[gd_scene format=3 uid="uid://c2cmda8vfq0lb"] - -[ext_resource type="Script" uid="uid://befvsvog6282s" path="res://scenes/ui/menu.gd" id="1_g80e2"] -[ext_resource type="PackedScene" uid="uid://dj2dk1g7jnk23" path="res://scenes/ui/title_button.tscn" id="1_t6us6"] - -[node name="Title" type="CanvasLayer" unique_id=1428349657] -script = ExtResource("1_g80e2") - -[node name="Control" type="Control" parent="." unique_id=930849088] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="Title" type="MarginContainer" parent="Control" unique_id=22126455] -layout_mode = 1 -anchors_preset = 5 -anchor_left = 0.5 -anchor_right = 0.5 -offset_left = -359.5 -offset_right = 359.5 -offset_bottom = 273.0 -grow_horizontal = 2 -theme_override_constants/margin_top = 50 - -[node name="Label" type="Label" parent="Control/Title" unique_id=1066740590] -layout_mode = 2 -size_flags_horizontal = 0 -theme_override_font_sizes/font_size = 200 -text = "MULCH" -uppercase = true - -[node name="MarginContainer" type="MarginContainer" parent="Control" unique_id=1839328694] -layout_mode = 1 -anchors_preset = 12 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = -219.0 -grow_horizontal = 2 -grow_vertical = 0 -theme_override_constants/margin_left = 10 -theme_override_constants/margin_top = 10 -theme_override_constants/margin_right = 10 -theme_override_constants/margin_bottom = 10 - -[node name="Hbox" type="HBoxContainer" parent="Control/MarginContainer" unique_id=728577623] -layout_mode = 2 -theme_override_constants/separation = 25 -alignment = 1 - -[node name="Start" parent="Control/MarginContainer/Hbox" unique_id=1597537067 instance=ExtResource("1_t6us6")] -layout_mode = 2 -text = "Start" - -[node name="Load" parent="Control/MarginContainer/Hbox" unique_id=1936574972 instance=ExtResource("1_t6us6")] -layout_mode = 2 -text = "Load" - -[node name="Options" parent="Control/MarginContainer/Hbox" unique_id=1664031607 instance=ExtResource("1_t6us6")] -layout_mode = 2 -text = "Options" - -[node name="Quit" parent="Control/MarginContainer/Hbox" unique_id=443491275 instance=ExtResource("1_t6us6")] -layout_mode = 2 -text = "Quit" - -[connection signal="pressed" from="Control/MarginContainer/Hbox/Start" to="." method="_on_start_pressed"] -[connection signal="pressed" from="Control/MarginContainer/Hbox/Options" to="." method="_on_options_pressed"] -[connection signal="pressed" from="Control/MarginContainer/Hbox/Quit" to="." method="_on_quit_pressed"] diff --git a/scenes/ui/title_button.tscn b/scenes/ui/title_button.tscn deleted file mode 100644 index 8c876d3..0000000 --- a/scenes/ui/title_button.tscn +++ /dev/null @@ -1,8 +0,0 @@ -[gd_scene format=3 uid="uid://dj2dk1g7jnk23"] - -[node name="Button" type="Button" unique_id=1597537067] -custom_minimum_size = Vector2(100, 50) -offset_right = 8.0 -offset_bottom = 8.0 -size_flags_horizontal = 3 -size_flags_vertical = 3 diff --git a/sfx/telephone.wav b/sfx/telephone.wav deleted file mode 100644 index f00d760..0000000 Binary files a/sfx/telephone.wav and /dev/null differ diff --git a/sfx/telephone.wav.import b/sfx/telephone.wav.import deleted file mode 100644 index 38d957b..0000000 --- a/sfx/telephone.wav.import +++ /dev/null @@ -1,24 +0,0 @@ -[remap] - -importer="wav" -type="AudioStreamWAV" -uid="uid://diiml75ifnw78" -path="res://.godot/imported/telephone.wav-4ce071c7ee2f2f520dfe1169fa80f1d7.sample" - -[deps] - -source_file="res://sfx/telephone.wav" -dest_files=["res://.godot/imported/telephone.wav-4ce071c7ee2f2f520dfe1169fa80f1d7.sample"] - -[params] - -force/8_bit=false -force/mono=false -force/max_rate=false -force/max_rate_hz=44100 -edit/trim=false -edit/normalize=false -edit/loop_mode=0 -edit/loop_begin=0 -edit/loop_end=-1 -compress/mode=2 diff --git a/spr/weener/crouch.png b/spr/weener/crouch.png deleted file mode 100644 index ab41d1c..0000000 Binary files a/spr/weener/crouch.png and /dev/null differ diff --git a/spr/weener/crouch.png.import b/spr/weener/crouch.png.import deleted file mode 100644 index 1f0ef4c..0000000 --- a/spr/weener/crouch.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bje5rius281oi" -path="res://.godot/imported/crouch.png-75053774087b61c96c0a12fed86c645b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://spr/weener/crouch.png" -dest_files=["res://.godot/imported/crouch.png-75053774087b61c96c0a12fed86c645b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/spr/weener/grab.png b/spr/weener/grab.png deleted file mode 100644 index 86857e0..0000000 Binary files a/spr/weener/grab.png and /dev/null differ diff --git a/spr/weener/grab.png.import b/spr/weener/grab.png.import deleted file mode 100644 index e9511c0..0000000 --- a/spr/weener/grab.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cr80vs56knlqh" -path="res://.godot/imported/grab.png-9ce28178086dc660cf6a51212594526c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://spr/weener/grab.png" -dest_files=["res://.godot/imported/grab.png-9ce28178086dc660cf6a51212594526c.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/spr/weener/run1.png b/spr/weener/run1.png deleted file mode 100644 index 9dc3077..0000000 Binary files a/spr/weener/run1.png and /dev/null differ diff --git a/spr/weener/run1.png.import b/spr/weener/run1.png.import deleted file mode 100644 index d7ff0f4..0000000 --- a/spr/weener/run1.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b1bn5hf6uxfiw" -path="res://.godot/imported/run1.png-d26046d8fad7d0daa75ecaf596d7ce63.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://spr/weener/run1.png" -dest_files=["res://.godot/imported/run1.png-d26046d8fad7d0daa75ecaf596d7ce63.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/spr/weener/run2.png b/spr/weener/run2.png deleted file mode 100644 index 8e5685a..0000000 Binary files a/spr/weener/run2.png and /dev/null differ diff --git a/spr/weener/run2.png.import b/spr/weener/run2.png.import deleted file mode 100644 index 7d317ad..0000000 --- a/spr/weener/run2.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://0pued7jac5i2" -path="res://.godot/imported/run2.png-bfa78c0635757a07e11f926b3d2494fd.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://spr/weener/run2.png" -dest_files=["res://.godot/imported/run2.png-bfa78c0635757a07e11f926b3d2494fd.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/spr/weener/stand.png b/spr/weener/stand.png deleted file mode 100644 index 15d2a6f..0000000 Binary files a/spr/weener/stand.png and /dev/null differ diff --git a/spr/weener/stand.png.import b/spr/weener/stand.png.import deleted file mode 100644 index 80fbbdc..0000000 --- a/spr/weener/stand.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dk3ulxm2i0qm6" -path="res://.godot/imported/stand.png-5ee97d0083a86108f4b04a767a42c835.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://spr/weener/stand.png" -dest_files=["res://.godot/imported/stand.png-5ee97d0083a86108f4b04a767a42c835.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/spr/weener/walk.png b/spr/weener/walk.png deleted file mode 100644 index 0b0c896..0000000 Binary files a/spr/weener/walk.png and /dev/null differ diff --git a/spr/weener/walk.png.import b/spr/weener/walk.png.import deleted file mode 100644 index c8ed909..0000000 --- a/spr/weener/walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bbcs52esee2pt" -path="res://.godot/imported/walk.png-d79775f481243dd53f2ddd05fbab8284.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://spr/weener/walk.png" -dest_files=["res://.godot/imported/walk.png-d79775f481243dd53f2ddd05fbab8284.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/tools/__pycache__/reformat_docstrings.cpython-39.pyc b/tools/__pycache__/reformat_docstrings.cpython-39.pyc new file mode 100644 index 0000000..00b8880 Binary files /dev/null and b/tools/__pycache__/reformat_docstrings.cpython-39.pyc differ diff --git a/tools/docstring_formatter.py b/tools/docstring_formatter.py new file mode 100644 index 0000000..1aeaf88 --- /dev/null +++ b/tools/docstring_formatter.py @@ -0,0 +1,908 @@ +#!/usr/bin/env python3 +""" +Utility to normalize GDScript docstrings in Escoria to the format described in AGENTS.md. + +This script focuses on reformatting existing docstrings without discarding information. +It reconstructs parameter tables, enforces the `[br]` line break tokens, wraps type names +in backticks, and preserves warnings / notes. The primary target is public-facing methods +and command classes under `addons/escoria-core`. +""" + +from __future__ import annotations + +import argparse +import re +from dataclasses import dataclass, field +from pathlib import Path +from typing import Dict, Iterable, List, Optional, Sequence, Tuple + + +DOC_PREFIX = "##" +BR_TOKEN = "[br]" +PARAM_TABLE_HEADER = [ + "| Name | Type | Description | Required? |", + "|:-----|:-----|:------------|:----------|", +] + + +def strip_prefix(text: str, prefix: str) -> str: + if text.startswith(prefix): + return text[len(prefix) :] + return text + + +def strip_doc_line(line: str, indent: str) -> str: + if not line.startswith(indent): + # If indentation is inconsistent, fall back to lstrip to avoid losing content. + line = line.lstrip() + else: + line = line[len(indent) :] + content = strip_prefix(line, DOC_PREFIX).lstrip() + while content.startswith("#"): + content = content[1:].lstrip() + return content + + +def strip_br_suffix(text: str) -> Tuple[str, bool]: + """ + Remove a trailing `[br]` token from the given string. + Returns a tuple of (new_text, removed_br). + """ + updated = text.rstrip() + if updated.endswith(BR_TOKEN): + updated = updated[: -len(BR_TOKEN)].rstrip() + return updated, True + return text, False + + +def ensure_br(text: str) -> str: + stripped, had_br = strip_br_suffix(text) + if had_br: + return stripped + f"{BR_TOKEN}" + return stripped + + +def ensure_type_backticks(type_text: str) -> str: + clean = type_text.strip() + if not clean: + return "`Variant`" + if clean.startswith("`") and clean.endswith("`"): + return clean + return f"`{clean}`" + + +def collapse_spaces(text: str) -> str: + return re.sub(r"\s+", " ", text.strip()) + + +@dataclass +class ParameterDoc: + name: str + type_name: str + description: str + required: str + + def ensure_defaults(self) -> None: + if not self.type_name: + self.type_name = "Variant" + if not self.description: + self.description = "Description not provided." + if self.required not in {"yes", "no"}: + self.required = "yes" + + +@dataclass +class ReturnDoc: + type_name: str + description: str + + def ensure_defaults(self) -> None: + if not self.type_name: + self.type_name = "Variant" + if not self.description: + self.description = "Description not provided." + + +@dataclass +class DocstringData: + description_lines: List[str] = field(default_factory=list) + parameters: Dict[str, ParameterDoc] = field(default_factory=dict) + returns: Optional[ReturnDoc] = None + notes_lines: List[str] = field(default_factory=list) + extra_sections: Dict[str, List[str]] = field(default_factory=dict) + trailer_lines: List[str] = field(default_factory=list) + + +def parse_section_lines(raw_lines: Sequence[str]) -> Dict[str, List[str]]: + sections: Dict[str, List[str]] = {"description": []} + current = "description" + + for line in raw_lines: + raw = line.strip() + normalized = raw + if normalized.endswith(BR_TOKEN): + normalized = normalized[: -len(BR_TOKEN)].rstrip() + if normalized: + normalized_lower = normalized.lower() + matched = False + for key, tokens in SECTION_TOKENS.items(): + for token in tokens: + token_lower = token.lower() + rest = "" + if normalized_lower == token_lower: + matched = True + elif normalized_lower.startswith(token_lower + " "): + rest = normalized[len(token):].lstrip(" :-") + matched = True + elif normalized_lower.startswith(token_lower + ":"): + rest = normalized[len(token):].lstrip(" :-") + matched = True + elif normalized_lower.startswith(token_lower + "-"): + rest = normalized[len(token):].lstrip(" :-") + matched = True + if matched: + current = key + sections.setdefault(current, []) + if rest: + sections[current].append(rest) + break + if matched: + break + if matched: + continue + sections.setdefault(current, []).append(line) + + return sections + + +def clean_doc_lines(lines: Iterable[str]) -> List[str]: + cleaned: List[str] = [] + for line in lines: + text = line.strip() + if not text: + cleaned.append("") + continue + # Remove `[br]` tokens because we'll re-apply them later. + text, _ = strip_br_suffix(text) + cleaned.append(text) + # Trim leading / trailing blank entries. + while cleaned and not cleaned[0]: + cleaned.pop(0) + while cleaned and not cleaned[-1]: + cleaned.pop() + return cleaned + + +def parse_parameter_table(lines: Sequence[str]) -> Dict[str, ParameterDoc]: + params: Dict[str, ParameterDoc] = {} + for line in lines: + stripped = line.strip() + if not stripped.startswith("|"): + continue + cells = [cell.strip() for cell in stripped.split("|")[1:-1]] + if len(cells) != 4: + continue + if cells[0].lower() == "name" and cells[1].lower().startswith("type"): + continue + if all(cell and all(ch in ":-" for ch in cell) for cell in cells): + continue + name = cells[0] + type_name = cells[1] + description = cells[2] + required = cells[3].lower() + params[name] = ParameterDoc( + name=name, + type_name=type_name.strip("`"), + description=description, + required=required, + ) + return params + + +def parse_parameter_bullets(lines: Sequence[str]) -> Dict[str, ParameterDoc]: + params: Dict[str, ParameterDoc] = {} + current: Optional[ParameterDoc] = None + + for line in lines: + stripped = line.strip() + if not stripped: + continue + text, _ = strip_br_suffix(stripped) + text = text.strip() + if not text: + continue + lowered = text.lower() + if lowered.startswith("this method does not accept parameters") or lowered.startswith("this command does not accept parameters"): + return {} + + if text.startswith("-"): + content = text[1:].strip() + if ":" in content: + name_part, desc_part = content.split(":", 1) + name = re.sub(r"[`\*]", "", name_part).strip() + description = desc_part.strip() + else: + name = re.sub(r"[`\*]", "", content).strip() + description = "" + current = ParameterDoc( + name=name, + type_name="", + description=description, + required="yes", + ) + params[name] = current + continue + + if current: + if current.description: + current.description += " " + text + else: + current.description = text + + return params + + +def parse_parameters_section(lines: Sequence[str]) -> Dict[str, ParameterDoc]: + if any(line.strip().startswith("|") for line in lines): + return parse_parameter_table(lines) + return parse_parameter_bullets(lines) + + +RETURN_PATTERN = re.compile( + r"^`?(?P[A-Za-z0-9_\.]+)`?\s*(?:—|-)\s*(?P.+)$" +) +RETURN_MARKER_RE = re.compile(r"\*+returns?\*+", re.IGNORECASE) + + +def parse_return_section(lines: Sequence[str]) -> Optional[ReturnDoc]: + cleaned: List[str] = [] + for line in lines: + stripped = line.strip() + if not stripped: + continue + text, _ = strip_br_suffix(stripped) + text = text.strip() + if not text: + continue + text = RETURN_MARKER_RE.sub("", text).strip() + cleaned.append(collapse_spaces(text)) + if not cleaned: + return None + + first_line = cleaned[0] + match = RETURN_PATTERN.match(first_line) + if match: + return ReturnDoc(match.group("type"), match.group("desc")) + + if first_line.lower().startswith("returns"): + desc = first_line.split(":", 1)[1].strip() if ":" in first_line else first_line + return ReturnDoc("", desc) + + # Fall back to treating the line as description only. + return ReturnDoc("", cleaned[0]) + + +WARNING_PREFIXES = ("**Warning**", "**Note**", "**Notes**", "**Warning:**", "**Note:**") +SECTION_TOKENS = { + "parameters": ["#### parameters", "**parameters**", "*parameters*", "parameters", "parameters:"], + "returns": ["#### returns", "**returns**", "*returns*", "returns:", "return:", "return value", "return value:"], + "notes": ["#### notes", "**notes**", "*notes*", "notes", "notes:"], +} + + +def extract_description_and_notes(lines: Sequence[str]) -> Tuple[List[str], List[str]]: + description: List[str] = [] + notes: List[str] = [] + + for line in clean_doc_lines(lines): + if any(line.startswith(prefix) for prefix in WARNING_PREFIXES): + notes.append(line) + elif line.startswith("@"): + notes.append(line) + else: + description.append(line) + + return description, notes + + +def extract_note_lines(lines: Sequence[str]) -> Tuple[List[str], List[str]]: + notes: List[str] = [] + filtered: List[str] = [] + note_active = False + for line in lines: + stripped = line.strip() + if not stripped: + filtered.append(line) + note_active = False + continue + text, _ = strip_br_suffix(stripped) + text = text.strip() + if not text: + filtered.append(line) + note_active = False + continue + if note_active: + notes[-1] += f" {text}" + continue + if any(text.startswith(prefix) for prefix in WARNING_PREFIXES): + notes.append(text) + note_active = True + continue + if text.startswith("@"): + notes.append(text) + note_active = False + continue + filtered.append(line) + return notes, filtered + + +def parse_docstring(raw_lines: Sequence[str]) -> DocstringData: + sections = parse_section_lines(raw_lines) + description_lines, note_lines = extract_description_and_notes(sections.get("description", [])) + data = DocstringData(description_lines=description_lines, notes_lines=note_lines) + + if "parameters" in sections: + param_notes, param_lines = extract_note_lines(sections["parameters"]) + data.notes_lines.extend(param_notes) + data.parameters = parse_parameters_section(param_lines) + if "returns" in sections: + data.returns = parse_return_section(sections["returns"]) + if "notes" in sections: + note_desc, note_warn = extract_description_and_notes(sections["notes"]) + data.notes_lines.extend(note_desc) + data.notes_lines.extend(note_warn) + + extra_keys = { + key + for key in sections.keys() + if key not in {"description", "parameters", "returns", "notes"} + } + for key in extra_keys: + data.extra_sections[key] = clean_doc_lines(sections[key]) + + return data + + +FUNC_DEF_RE = re.compile(r"func\s+([A-Za-z0-9_]+)") + + +def get_function_signature(lines: Sequence[str], start_index: int) -> Tuple[Optional[str], Optional[str]]: + """ + Returns (function_name, signature_text) starting at `start_index`. + The caller should ensure that lines[start_index] is part of a `func` definition. + """ + if start_index >= len(lines): + return None, None + + idx = start_index + while idx < len(lines) and not lines[idx].strip(): + idx += 1 + + if idx >= len(lines): + return None, None + + first_line = lines[idx] + match = FUNC_DEF_RE.search(first_line) + if not match: + return None, None + name = match.group(1) + + signature_parts = [first_line.rstrip("\n")] + open_parens = first_line.count("(") - first_line.count(")") + while idx + 1 < len(lines) and (open_parens > 0 or not first_line.rstrip().endswith(":")): + idx += 1 + next_line = lines[idx].rstrip("\n") + signature_parts.append(next_line) + open_parens += next_line.count("(") - next_line.count(")") + first_line = next_line + if open_parens <= 0 and next_line.rstrip().endswith(":"): + break + + signature_text = " ".join(part.strip() for part in signature_parts) + return name, signature_text + + +TYPE_HINT_RE = re.compile(r":\s*([^=\s]+)") + + +def parse_func_parameters(signature_text: str) -> List[ParameterDoc]: + if "(" not in signature_text or ")" not in signature_text: + return [] + inner = signature_text[signature_text.index("(") + 1 : signature_text.rfind(")")] + # Remove trailing comments. + inner = inner.split("#", 1)[0] + + params: List[ParameterDoc] = [] + current = "" + depth = 0 + for char in inner: + if char == "(": + depth += 1 + current += char + elif char == ")": + depth = max(depth - 1, 0) + current += char + elif char == "," and depth == 0: + token = current.strip() + if token: + params.append(_build_param_from_token(token)) + current = "" + else: + current += char + + token = current.strip() + if token: + params.append(_build_param_from_token(token)) + + return params + + +DEFAULT_VALUE_RE = re.compile(r"=\s*(.+)$") + + +def _build_param_from_token(token: str) -> ParameterDoc: + name = token + type_name = "" + required = "yes" + description = "" + + default_match = DEFAULT_VALUE_RE.search(token) + if default_match: + required = "no" + token = token[: default_match.start()].strip() + + if ":" in token: + name_part, type_part = token.split(":", 1) + name = name_part.strip() + type_name = collapse_spaces(type_part) + else: + name = token.strip() + + if not type_name: + type_name = "Variant" + + return ParameterDoc( + name=name, + type_name=type_name, + description=description, + required=required, + ) + + +RETURN_TYPE_RE = re.compile(r"->\s*([^:\s]+)") + + +def parse_return_from_signature(signature_text: str) -> Optional[str]: + match = RETURN_TYPE_RE.search(signature_text) + if match: + return collapse_spaces(match.group(1)) + return None + + +COMMAND_EXTENDS_RE = re.compile(r"extends\s+ESCBaseCommand") + + +def detect_command_class(context_lines: Sequence[str], start_index: int) -> bool: + idx = start_index + while idx < len(context_lines) and not context_lines[idx].strip(): + idx += 1 + if idx >= len(context_lines): + return False + return COMMAND_EXTENDS_RE.search(context_lines[idx]) is not None + + +COMMAND_SIGNATURE_RE = re.compile(r"`([^`]+)`") + + +def parse_command_signature(line: str) -> Tuple[str, str, List[ParameterDoc]]: + """ + Parse a command signature line like + `anim(object: String[, reverse: Boolean])` + and return (signature_text, command_name, parameters). + """ + match = COMMAND_SIGNATURE_RE.search(line) + if not match: + return "", "", [] + signature = match.group(1) + if "(" not in signature: + return signature.strip(), signature.strip(), [] + name = signature[: signature.index("(")].strip() + params_text = signature[signature.index("(") + 1 : signature.rfind(")")] + + params: List[ParameterDoc] = [] + current = "" + depth = 0 + for char in params_text: + if char == "[": + if current.strip(): + params.append(_build_command_param(current.strip(), optional=(depth > 0))) + current = "" + depth += 1 + elif char == "]": + if current.strip(): + params.append(_build_command_param(current.strip(), optional=True)) + current = "" + depth = max(depth - 1, 0) + elif char == "," and depth == 0: + if current.strip(): + params.append(_build_command_param(current.strip(), optional=False)) + current = "" + else: + current += char + + if current.strip(): + params.append(_build_command_param(current.strip(), optional=(depth > 0))) + + # Filter out empty entries introduced by commas. + params = [param for param in params if param.name] + return signature.strip(), name, params + + +def _build_command_param(token: str, optional: bool) -> ParameterDoc: + token = token.strip() + required = "no" if optional else "yes" + if token.startswith(","): + token = token[1:].strip() + + if ":" in token: + name_part, type_part = token.split(":", 1) + name = name_part.strip() + type_name = collapse_spaces(type_part) + else: + name = collapse_spaces(token) + type_name = "Variant" + + return ParameterDoc( + name=name, + type_name=type_name, + description="", + required=required, + ) + + +def merge_parameter_details( + signature_params: List[ParameterDoc], + existing_params: Dict[str, ParameterDoc], +) -> List[ParameterDoc]: + merged: List[ParameterDoc] = [] + for param in signature_params: + existing = existing_params.get(param.name) + if existing: + description = existing.description or param.description + type_name = param.type_name or existing.type_name + required = param.required or existing.required + merged.append( + ParameterDoc( + name=param.name, + type_name=type_name, + description=description, + required=required, + ) + ) + else: + merged.append(param) + + # Include any existing parameters we could not match (to avoid data loss). + for name, param in existing_params.items(): + if name not in {p.name for p in merged}: + merged.append(param) + + for param in merged: + param.ensure_defaults() + + return merged + + +def merge_return_details( + existing: Optional[ReturnDoc], + fallback_type: Optional[str], +) -> ReturnDoc: + if existing: + existing.ensure_defaults() + if fallback_type and (not existing.type_name or existing.type_name == "Variant"): + existing.type_name = fallback_type + if existing.type_name == "void" and existing.description == "Description not provided.": + existing.description = "No value returned." + return existing + + type_name = fallback_type or "Variant" + default_description = "No value returned." if type_name == "void" else "Description not provided." + doc = ReturnDoc( + type_name=type_name, + description=default_description, + ) + doc.ensure_defaults() + return doc + + +def format_description_lines(lines: Sequence[str], indent: str) -> List[str]: + if not lines: + lines = ["Description not provided."] + formatted: List[str] = [] + for line in lines: + formatted.append(f"{indent}{DOC_PREFIX} {line}{BR_TOKEN}") + return formatted + + +def format_blank_line(indent: str) -> List[str]: + return [f"{indent}{DOC_PREFIX} {BR_TOKEN}"] + + +def format_parameters_section(parameters: List[ParameterDoc], indent: str) -> List[str]: + output: List[str] = [] + output.append(f"{indent}{DOC_PREFIX} #### Parameters{BR_TOKEN}") + output.extend(format_blank_line(indent)) + if parameters: + output.extend(f"{indent}{DOC_PREFIX} {line}{BR_TOKEN}" for line in PARAM_TABLE_HEADER) + for param in parameters: + output.append( + f"{indent}{DOC_PREFIX} |{param.name}|{ensure_type_backticks(param.type_name)}|{param.description}|{param.required}|{BR_TOKEN}" + ) + else: + output.append(f"{indent}{DOC_PREFIX} This method does not accept parameters.{BR_TOKEN}") + return output + + +def format_returns_section(return_doc: ReturnDoc, indent: str) -> List[str]: + output: List[str] = [] + output.append(f"{indent}{DOC_PREFIX} #### Returns{BR_TOKEN}") + output.extend(format_blank_line(indent)) + description = return_doc.description.strip() + if description and not description.endswith("."): + description += "." + output.append( + f"{indent}{DOC_PREFIX} {ensure_type_backticks(return_doc.type_name)} — {description}" + ) + return output + + +def format_notes_section(notes: Sequence[str], indent: str) -> List[str]: + if not notes: + return [] + output: List[str] = [] + output.append(f"{indent}{DOC_PREFIX} #### Notes{BR_TOKEN}") + output.extend(format_blank_line(indent)) + for note in notes: + output.append(f"{indent}{DOC_PREFIX} {note}{BR_TOKEN}") + return output + + +def format_extra_sections(extra: Dict[str, List[str]], indent: str) -> List[str]: + lines: List[str] = [] + for name, contents in extra.items(): + title = name.title() + lines.append(f"{indent}{DOC_PREFIX} #### {title}{BR_TOKEN}") + lines.extend(format_blank_line(indent)) + if contents: + for entry in contents: + lines.append(f"{indent}{DOC_PREFIX} {entry}{BR_TOKEN}") + else: + lines.append(f"{indent}{DOC_PREFIX} {BR_TOKEN}") + return lines + + +def rebuild_function_docstring( + raw_lines: Sequence[str], + indent: str, + surrounding_lines: Sequence[str], + context_index: int, +) -> List[str]: + data = parse_docstring(raw_lines) + func_name, signature_text = get_function_signature(surrounding_lines, context_index) + signature_params: List[ParameterDoc] = [] + return_type_hint: Optional[str] = None + + if signature_text: + signature_params = parse_func_parameters(signature_text) + return_type_hint = parse_return_from_signature(signature_text) + + parameters = merge_parameter_details(signature_params, data.parameters) + return_doc = merge_return_details(data.returns, return_type_hint) + + rebuilt: List[str] = [] + rebuilt.extend(format_description_lines(data.description_lines, indent)) + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_parameters_section(parameters, indent)) + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_returns_section(return_doc, indent)) + notes = data.notes_lines + extra = data.extra_sections + if notes: + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_notes_section(notes, indent)) + if extra: + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_extra_sections(extra, indent)) + return rebuilt + + +def rebuild_command_docstring( + raw_lines: Sequence[str], + indent: str, +) -> List[str]: + data = parse_docstring(raw_lines) + + cleaned_lines = clean_doc_lines(raw_lines) + first_nonempty = next((line for line in cleaned_lines if line), "") + signature_text, command_name, signature_params = parse_command_signature(first_nonempty) + if not signature_text: + return rebuild_generic_docstring(raw_lines, indent) + parameters = merge_parameter_details(signature_params, data.parameters) + + rebuilt: List[str] = [] + + # Recreate the brief description with no trailing [br] per requirement. + if signature_text: + rebuilt.append(f"{indent}{DOC_PREFIX} `{signature_text}`") + else: + # Preserve the first line even if no signature detected. + original_first = raw_lines[0].strip() + rebuilt.append(f"{indent}{DOC_PREFIX} {strip_prefix(original_first, DOC_PREFIX).strip()}") + + rebuilt.append(f"{indent}{DOC_PREFIX}") + # Append the remaining description lines (skip the first signature line). + signature_line = f"`{signature_text}`" if signature_text else "" + description_lines = [line for line in data.description_lines if line != signature_line] + if not description_lines: + # If removing the signature removed everything, fall back to original description lines. + description_lines = data.description_lines + description_lines = [line for line in description_lines if line] + rebuilt.extend(format_description_lines(description_lines, indent)) + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_parameters_section(parameters, indent)) + if data.notes_lines or data.extra_sections or data.returns: + rebuilt.extend(format_blank_line(indent)) + if data.notes_lines: + rebuilt.extend(format_notes_section(data.notes_lines, indent)) + if data.returns: + rebuilt.extend(format_returns_section(data.returns, indent)) + if data.extra_sections: + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_extra_sections(data.extra_sections, indent)) + if data.trailer_lines: + rebuilt.extend(data.trailer_lines) + return rebuilt + + +def rebuild_generic_docstring(raw_lines: Sequence[str], indent: str) -> List[str]: + data = parse_docstring(raw_lines) + rebuilt: List[str] = [] + rebuilt.extend(format_description_lines(data.description_lines, indent)) + if data.parameters: + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_parameters_section(list(data.parameters.values()), indent)) + if data.returns: + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_returns_section(data.returns, indent)) + if data.notes_lines: + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_notes_section(data.notes_lines, indent)) + if data.extra_sections: + rebuilt.extend(format_blank_line(indent)) + rebuilt.extend(format_extra_sections(data.extra_sections, indent)) + return rebuilt + + +def find_next_code_line(lines: Sequence[str], start_index: int) -> int: + idx = start_index + while idx < len(lines): + stripped = lines[idx].strip() + if not stripped: + idx += 1 + continue + if stripped.startswith("#"): + idx += 1 + continue + break + return idx + + +def collect_docstring_block( + lines: Sequence[str], start_index: int +) -> Tuple[int, List[str], List[str], str]: + indent = re.match(r"\s*", lines[start_index]).group(0) + original_block: List[str] = [] + content_block: List[str] = [] + idx = start_index + while idx < len(lines): + line = lines[idx] + stripped = line.lstrip() + if not stripped.startswith(DOC_PREFIX): + break + original_block.append(line.rstrip("\n")) + content_block.append(strip_doc_line(line.rstrip("\n"), indent)) + idx += 1 + return idx, original_block, content_block, indent + + +def rebuild_docstring( + raw_block: List[str], + indent: str, + full_lines: Sequence[str], + context_index: int, +) -> List[str]: + next_code_idx = find_next_code_line(full_lines, context_index) + if next_code_idx >= len(full_lines): + return [f"{indent}{DOC_PREFIX} {line}" for line in raw_block] + + next_line = full_lines[next_code_idx].strip() + if next_line.startswith("func "): + return rebuild_function_docstring(raw_block, indent, full_lines, next_code_idx) + if next_line.startswith("class_name") or next_line.startswith("extends"): + if detect_command_class(full_lines, next_code_idx): + return rebuild_command_docstring(raw_block, indent) + return rebuild_generic_docstring(raw_block, indent) + + return rebuild_generic_docstring(raw_block, indent) + + +def process_file(path: Path) -> bool: + original_lines = path.read_text(encoding="utf-8").splitlines() + new_lines: List[str] = [] + idx = 0 + changed = False + + while idx < len(original_lines): + line = original_lines[idx] + stripped = line.lstrip() + if stripped.startswith(DOC_PREFIX): + block_end, original_block, content_block, indent = collect_docstring_block( + original_lines, idx + ) + rebuilt = rebuild_docstring(content_block, indent, original_lines, block_end) + new_lines.extend(rebuilt) + idx = block_end + if not changed: + if len(original_block) != len(rebuilt): + changed = True + else: + for raw_line, new_line in zip(original_block, rebuilt): + if raw_line != new_line: + changed = True + break + continue + + new_lines.append(line) + idx += 1 + + if changed: + path.write_text("\n".join(new_lines) + "\n", encoding="utf-8") + return changed + + +def iter_gd_files(root: Path) -> Iterable[Path]: + for path in root.rglob("*.gd"): + if path.is_file(): + yield path + + +def main() -> None: + parser = argparse.ArgumentParser(description="Normalize GDScript docstrings per AGENTS.md.") + parser.add_argument( + "paths", + nargs="*", + default=["addons/escoria-core"], + help="Directories or files to process (default: addons/escoria-core).", + ) + args = parser.parse_args() + + targets: List[Path] = [] + for value in args.paths: + path = Path(value) + if path.is_file(): + targets.append(path) + elif path.is_dir(): + targets.extend(iter_gd_files(path)) + + processed = 0 + changed = 0 + for path in sorted(set(targets)): + processed += 1 + if process_file(path): + changed += 1 + + print(f"Processed {processed} files; updated {changed}.") + + +if __name__ == "__main__": + main() diff --git a/tools/reformat_docstrings.py b/tools/reformat_docstrings.py new file mode 100644 index 0000000..31dafd6 --- /dev/null +++ b/tools/reformat_docstrings.py @@ -0,0 +1,1132 @@ +#!/usr/bin/env python3 +import re +from pathlib import Path +from typing import Dict, List, Optional, Tuple + + +ROOT = Path("addons/escoria-core") + +DEFAULT_PARAM_DESC = "No description provided." + +PARAM_OVERRIDES_BY_SIGNATURE = { + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd", + "func init(name: ESCToken, initializer: ESCGrammarExpr):", + "name", + ): "Token representing the variable's name.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd", + "func init(name: ESCToken, target: ESCGrammarExprs.Literal, flags: Dictionary, body: ESCGrammarStmts.Block, object_global_id: String):", + "name", + ): "Token representing the event name.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd", + "func init(name: ESCToken):", + "name", + ): "Token representing the variable's name.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_exprs.gd", + "func init(name: ESCToken, value: ESCGrammarExpr):", + "name", + ): "Token representing the variable's name to assign.", +} + +PARAM_OVERRIDES_BY_FUNCTION = { + ( + "addons/escoria-core/game/esc_project_settings_manager.gd", + "register_setting", + "name", + ): "Fully qualified Project Settings key to register.", + ( + "addons/escoria-core/game/esc_project_settings_manager.gd", + "remove_setting", + "name", + ): "Fully qualified Project Settings key to remove.", + ( + "addons/escoria-core/game/core-scripts/esc_animation_player.gd", + "_on_animation_finished", + "name", + ): "Name of the animation that triggered the callback.", + ( + "addons/escoria-core/game/scenes/dialogs/esc_dialog_player.gd", + "_update_dialog_manager", + "dialog_manager_type", + ): "Type name of the dialog manager implementation to instantiate.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_object_manager.gd", + "register_object", + "auto_unregister", + ): "(optional) Automatically unregister the object when its node exits the scene tree (default: `true`).", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "is_channel_free", + "name", + ): "Name of the channel to inspect.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "get_running_event", + "name", + ): "Name of the channel whose running event should be returned.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd", + "is_valid_key", + "name", + ): "Token describing the variable name to look up.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd", + "get_value", + "name", + ): "Token describing the variable name whose value is requested.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd", + "assign", + "name", + ): "Token describing the variable name to assign.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd", + "define", + "name", + ): "Variable name to register in this scope.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd", + "get_at", + "name", + ): "Variable name to resolve at the requested scope depth.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd", + "assign_at", + "name", + ): "Token describing the variable name to modify at the requested scope depth.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd", + "look_up_variable", + "name", + ): "Token representing the variable name to resolve.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd", + "add_event", + "name", + ): "Event identifier to add to the script.", + ( + "addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd", + "add_command", + "name", + ): "Command name to append to the script.", + ( + "addons/escoria-core/game/core-scripts/esc_animation_player.gd", + "play", + "name", + ): "Animation name to play.", + ( + "addons/escoria-core/game/core-scripts/esc_animation_player.gd", + "play_backwards", + "name", + ): "Animation name to play in reverse.", + ( + "addons/escoria-core/game/core-scripts/esc_animation_player.gd", + "has_animation", + "name", + ): "Animation name to test for availability.", + ( + "addons/escoria-core/game/core-scripts/esc_animation_player.gd", + "seek_end", + "name", + ): "Animation name to jump to the last frame of.", + ( + "addons/escoria-core/game/core-scripts/esc_animation_player.gd", + "get_length", + "name", + ): "Animation name whose duration should be returned.", + ( + "addons/escoria-core/game/scenes/transitions/esc_transition_player.gd", + "get_transition", + "name", + ): "Transition name whose material path should be resolved.", + ( + "addons/escoria-core/game/scenes/transitions/esc_transition_player.gd", + "has_transition", + "name", + ): "Transition name to check for availability.", + ( + "addons/escoria-core/plugin.gd", + "register_setting", + "name", + ): "Fully qualified Project Settings key to register.", +} + +PARAM_OVERRIDES_BY_COMMAND = { + ( + "addons/escoria-core/game/core-scripts/esc/commands/anim.gd", + "anim", + "name", + ): "Name of the animation to start on the object.", + ( + "addons/escoria-core/game/core-scripts/esc/commands/anim_block.gd", + "anim_block", + "name", + ): "Name of the animation to play before continuing.", + ( + "addons/escoria-core/game/core-scripts/esc/commands/dec_global.gd", + "dec_global", + "name", + ): "Name of the global variable to decrement.", + ( + "addons/escoria-core/game/core-scripts/esc/commands/inc_global.gd", + "inc_global", + "name", + ): "Name of the global variable to increment.", + ( + "addons/escoria-core/game/core-scripts/esc/commands/rand_global.gd", + "rand_global", + "name", + ): "Name of the global variable that will receive the random value.", + ( + "addons/escoria-core/game/core-scripts/esc/commands/set_global.gd", + "set_global", + "name", + ): "Name of the global variable to set.", +} + +PARAM_OVERRIDES_BY_SIGNAL = { + ( + "addons/escoria-core/game/core-scripts/esc_location.gd", + "editor_is_start_location_set", + "node_to_ignore", + ): "`ESCLocation` that should be ignored while validating start locations.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd", + "global_changed", + "global", + ): "Key of the global that changed.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd", + "global_changed", + "old_value", + ): "Value stored under the key before the change.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_globals_manager.gd", + "global_changed", + "new_value", + ): "Updated value stored for the key.", + ( + "addons/escoria-core/game/core-scripts/esc_animation_player.gd", + "animation_finished", + "name", + ): "Name of the animation that completed playback.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "event_started", + "event_name", + ): "Name of the event whose execution has started.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "background_event_started", + "channel_name", + ): "Name of the background channel where the event runs.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "background_event_started", + "event_name", + ): "Name of the event that began on the background channel.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "event_finished", + "return_code", + ): "Execution result returned by the event.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "event_finished", + "event_name", + ): "Name of the event that just finished.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "background_event_finished", + "return_code", + ): "Execution result returned by the background event.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "background_event_finished", + "event_name", + ): "Name of the background event that finished.", + ( + "addons/escoria-core/game/core-scripts/esc/esc_event_manager.gd", + "background_event_finished", + "channel_name", + ): "Background channel where the event finished.", + ( + "addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd", + "finished", + "event", + ): "`ESCStatement` representing the event whose execution completed.", + ( + "addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd", + "finished", + "statement", + ): "`ESCStatement` that was running when the signal fired.", + ( + "addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd", + "finished", + "return_code", + ): "Execution result code produced by the statement.", + ( + "addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd", + "interrupted", + "event", + ): "`ESCStatement` representing the event whose execution was interrupted.", + ( + "addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd", + "interrupted", + "statement", + ): "`ESCStatement` that was executing when the interruption occurred.", + ( + "addons/escoria-core/game/core-scripts/esc/types/esc_statement.gd", + "interrupted", + "return_code", + ): "Execution result code describing the interruption outcome.", + ( + "addons/escoria-core/tools/logging/esc_logger.gd", + "error_message_signal", + "message", + ): "Error or warning message emitted through the logger.", +} + + +def get_param_override( + context_type: str, + path: Path, + context_name: str, + param_name: str, + signature_text: Optional[str] = None, +) -> Optional[str]: + path_key = path.as_posix() + if context_type == "function": + if signature_text: + key = (path_key, signature_text, param_name) + if key in PARAM_OVERRIDES_BY_SIGNATURE: + return PARAM_OVERRIDES_BY_SIGNATURE[key] + key = (path_key, context_name, param_name) + if key in PARAM_OVERRIDES_BY_FUNCTION: + return PARAM_OVERRIDES_BY_FUNCTION[key] + elif context_type == "command": + key = (path_key, context_name, param_name) + if key in PARAM_OVERRIDES_BY_COMMAND: + return PARAM_OVERRIDES_BY_COMMAND[key] + elif context_type == "signal": + key = (path_key, context_name, param_name) + if key in PARAM_OVERRIDES_BY_SIGNAL: + return PARAM_OVERRIDES_BY_SIGNAL[key] + return None + + +def strip_doc_prefix(line: str, indent: str) -> str: + if not line.startswith(indent + "##"): + return "" + content = line[len(indent) + 2 :] + if content.startswith(" "): + content = content[1:] + return content.rstrip("\n") + + +def strip_trailing_br(text: str) -> str: + if not text: + return text + new_text = text + while new_text.endswith("[br]"): + new_text = new_text[: -4].rstrip() + return new_text.strip() + + +def parse_param_bullet(raw_line: str) -> Optional[Tuple[str, str]]: + stripped_original = raw_line.lstrip() + if stripped_original.startswith("##"): + content = stripped_original[2:] + else: + content = stripped_original + while content.startswith("#"): + content = content[1:] + content = content.lstrip() + if not content or not content.startswith(("-", "*")): + return None + line = strip_trailing_br(content) + if not line: + return None + line = line.lstrip("-* \t") + if not line: + return None + if ":" not in line: + return None + name_part, desc_part = line.split(":", 1) + name = name_part.strip().strip("*").strip("`") + desc = desc_part.strip() + if not name: + return None + return name, desc + + +def format_type_cell(type_name: Optional[str]) -> str: + type_str = (type_name or "").strip() + if not type_str: + type_str = "Variant" + # Remove surrounding backticks if present + if type_str.startswith("`") and type_str.endswith("`"): + type_str = type_str[1:-1] + parts = [part.strip() for part in type_str.split("|") if part.strip()] + if not parts: + parts = ["Variant"] + return " or ".join(f"`{part}`" for part in parts) + + +def is_table_structure_line(raw_line: str) -> bool: + text = strip_trailing_br(raw_line.strip()) + if not text.startswith("|"): + return False + inner = [part.strip() for part in text.strip().strip("|").split("|")] + if not inner: + return False + first = inner[0].lower() + return first in {"name", ":-----"} + + +def parse_table_row(raw_line: str) -> Optional[Dict[str, str]]: + text = strip_trailing_br(raw_line.strip()) + if not text.startswith("|"): + return None + inner = [part.strip() for part in text.strip().strip("|").split("|")] + if len(inner) != 4: + return None + if inner[0].lower() in {"name", ":-----"}: + return None + name_cell = inner[0].lstrip("\\").strip() + return { + "name": name_cell, + "type": inner[1].strip("`"), + "desc": inner[2], + "required": inner[3].lower(), + } + + +def extract_inline_return_type(text: str) -> Tuple[str, Optional[str]]: + stripped = text.strip() + match = re.search(r"\(`([^`]+)`\)\.?$", stripped) + if not match: + return text, None + type_name = match.group(1) + prefix = stripped[: match.start()].rstrip() + if prefix.endswith("."): + prefix = prefix[:-1].rstrip() + return prefix, type_name + + +def normalize_whitespace(text: str) -> str: + return re.sub(r"\s+", " ", text).strip() + + +def parse_function_signature(lines: List[str]) -> Tuple[str, str, List[Dict], str]: + signature_text = " ".join(line.strip() for line in lines) + signature_text = normalize_whitespace(signature_text) + match = re.match(r"(?:static\s+)?func\s+([A-Za-z0-9_]+)\s*\((.*)\)\s*(?:->\s*([^:]+))?:", signature_text) + func_name = "" + params: List[Dict] = [] + return_type = "void" + if match: + func_name = match.group(1) + raw_params = match.group(2).strip() + return_type = match.group(3).strip() if match.group(3) else "void" + if raw_params: + params = parse_parameter_list(raw_params) + return func_name, signature_text, params, return_type + + +def parse_parameter_list(params_fragment: str) -> List[Dict]: + params: List[Dict] = [] + current = "" + depth = 0 + for ch in params_fragment: + if ch in "([{": + depth += 1 + current += ch + continue + if ch in ")]}": + depth = max(depth - 1, 0) + current += ch + continue + if ch == "," and depth == 0: + token = current.strip() + if token: + params.append(parse_parameter_token(token)) + current = "" + continue + current += ch + token = current.strip() + if token: + params.append(parse_parameter_token(token)) + return params + + +def parse_parameter_token(token: str) -> Dict: + required = True + name_part = token + if "=" in token: + name_part, _ = token.split("=", 1) + required = False + param_type = "Variant" + name = name_part.strip() + if ":" in name_part: + name_bits = name_part.split(":", 1) + name = name_bits[0].strip() + type_candidate = name_bits[1].strip() + if type_candidate: + param_type = type_candidate + if "=" in param_type: + param_type = param_type.split("=", 1)[0].strip() + if "=" in name: + name = name.split("=", 1)[0].strip() + required = False + name = name.lstrip("\\").strip() + if not name: + name = "param" + return {"name": name, "type": param_type, "required": required} + + +def sanitize_description(text: str) -> str: + text = strip_trailing_br(text) + lowered = text.strip().lower() + if lowered in {"none", "none."}: + return "" + return text + + +def clean_desc_text(text: str) -> str: + if not text: + return text + cleaned = re.sub(r"(?:\s*None\.)+$", "", text).strip() + return cleaned + + +def append_extra_text(current: str, extra: str) -> str: + extra = strip_trailing_br(extra.strip()) + extra = extra.lstrip("-* \t") + if not extra: + return current + if not current: + return extra + return f"{current} {extra}" + + +def merge_extra_rows(rows: List[Tuple[str, str, str, str]], extras: List[Tuple[str, str]]) -> List[Tuple[str, str, str, str]]: + if not extras: + return rows + merged = list(rows) + for name, desc in extras: + name = name.strip() + desc = desc.strip() + if not name and not desc: + continue + text = " ".join(filter(None, [name, desc])) + if merged: + last_name, last_type, last_desc, last_required = merged[-1] + merged[-1] = ( + last_name, + last_type, + append_extra_text(last_desc, text), + last_required, + ) + else: + merged.append((name or "Extra", format_type_cell("Variant"), desc or DEFAULT_PARAM_DESC, "yes")) + return merged + + +def reformat_function_docstring( + block_lines: List[str], + indent: str, + following_lines: List[str], + path: Path, +) -> Optional[List[str]]: + content_lines = [strip_doc_prefix(line, indent) for line in block_lines] + # Collect signature lines + signature_lines: List[str] = [] + for line in following_lines: + signature_lines.append(line) + if line.strip().endswith(":"): + break + func_name, signature_text, params_info, return_type = parse_function_signature(signature_lines) + + desc_lines: List[str] = [] + params: List[Dict[str, str]] = [] + param_buffer: Optional[Dict[str, str]] = None + return_lines: List[str] = [] + + for raw_line in content_lines: + stripped = raw_line.strip() + if not stripped: + continue + if stripped.startswith("@ESC"): + break + lowered = stripped.lower() + if lowered in {"[br]", "##"}: + continue + if lowered.startswith("#### parameters") or lowered.startswith("**parameters"): + param_buffer = None + continue + if lowered.startswith("parameters"): + param_buffer = None + continue + if lowered.startswith("#### returns") or lowered.startswith("**returns"): + param_buffer = None + continue + if lowered.startswith("*returns*"): + text = stripped[len("*Returns*") :].strip(" :-") + text = strip_trailing_br(text) + if text: + return_lines.append(text) + param_buffer = None + continue + if lowered.startswith("returns "): + text = stripped[len("returns ") :].strip() + text = strip_trailing_br(text) + if text: + return_lines.append(text) + param_buffer = None + continue + if is_table_structure_line(raw_line): + continue + table_entry = parse_table_row(raw_line) + if table_entry: + entry = {"name": table_entry["name"], "desc": table_entry["desc"]} + params.append(entry) + param_buffer = entry + continue + bullet = parse_param_bullet(raw_line) + if bullet: + name, desc = bullet + entry = {"name": name, "desc": desc} + params.append(entry) + param_buffer = entry + continue + if param_buffer: + param_buffer["desc"] = append_extra_text(param_buffer.get("desc", ""), stripped) + continue + if return_lines: + return_lines[-1] = append_extra_text(return_lines[-1], stripped) + continue + desc_lines.append(sanitize_description(raw_line)) + + desc_text = " ".join(filter(None, [strip_trailing_br(line) for line in desc_lines])).strip() + desc_text = clean_desc_text(desc_text) + if not desc_text and return_lines: + desc_text = return_lines[0] + if desc_text and desc_text[0].islower(): + desc_text = desc_text[0].upper() + desc_text[1:] + if not desc_text: + desc_text = "No description provided." + + # Prepare parameter rows following signature order + param_map = {entry["name"]: entry.get("desc", "") for entry in params} + rows: List[Tuple[str, str, str, str]] = [] + for param in params_info: + name = param["name"] + type_name = format_type_cell(param["type"]) + required = "yes" if param["required"] else "no" + desc = param_map.pop(name, "").strip() + desc_key = desc.strip() + if not desc_key or desc_key.startswith(DEFAULT_PARAM_DESC): + override = get_param_override("function", path, func_name, name, signature_text) + if override: + desc = override.strip() + desc_key = desc + if not desc_key: + desc = DEFAULT_PARAM_DESC + else: + desc = desc_key + rows.append((name, type_name, desc, required)) + rows = merge_extra_rows(rows, list(param_map.items())) + + raw_return_desc = " ".join(return_lines).strip() + cleaned_return_desc, inline_type = extract_inline_return_type(raw_return_desc) + return_desc = cleaned_return_desc.strip() + effective_declared_type = return_type + if effective_declared_type == "void" and inline_type: + test_desc = re.sub(r"[\.\s]+$", "", cleaned_return_desc.strip().lower()) + if test_desc not in {"", "returns nothing", "nothing"}: + effective_declared_type = inline_type + inferred_return_type = infer_return_type(effective_declared_type, return_desc or raw_return_desc) + if not return_desc: + if inferred_return_type == "void": + return_desc = "Returns nothing." + else: + return_desc = f"Returns a `{inferred_return_type}` value." + else: + lowered = return_desc.lower() + if lowered.startswith("returns "): + return_desc = return_desc[8:].strip() + elif lowered.startswith("return "): + return_desc = return_desc[7:].strip() + if return_desc and not return_desc.endswith("."): + return_desc += "." + return_desc = f"Returns {normalize_return_sentence(return_desc)}" + if inferred_return_type and inferred_return_type != "void": + return_desc = f"{return_desc} (`{inferred_return_type}`)" + + new_block: List[str] = [] + new_block.append(f"{indent}## {desc_text}[br]") + new_block.append(f"{indent}## [br]") + new_block.append(f"{indent}## #### Parameters[br]") + new_block.append(f"{indent}## [br]") + if rows: + new_block.append(f"{indent}## | Name | Type | Description | Required? |[br]") + new_block.append(f"{indent}## |:-----|:-----|:------------|:----------|[br]") + for name, type_name, description, required in rows: + new_block.append(f"{indent}## |{name}|{type_name}|{description}|{required}|[br]") + new_block.append(f"{indent}## [br]") + else: + new_block.append(f"{indent}## None.") + new_block.append(f"{indent}## [br]") + new_block.append(f"{indent}## #### Returns[br]") + new_block.append(f"{indent}## [br]") + new_block.append(f"{indent}## {return_desc}") + return new_block + + +def parse_command_signature(first_line: str) -> Tuple[str, List[Dict]]: + command_name = "" + fragment = "" + signature_match = re.match(r"`\s*([A-Za-z0-9_]+)\s*\((.*)\)`", first_line.strip()) + if signature_match: + command_name = signature_match.group(1) + fragment = signature_match.group(2).strip() + else: + match = re.search(r"`[^`]*\((.*)\)`", first_line) + if match: + fragment = match.group(1).strip() + if not fragment: + return command_name, [] + params: List[Dict] = [] + current = "" + optional_depth = 0 + for ch in fragment: + if ch == "[": + token = current.strip() + if token: + params.append(parse_command_token(token, optional_depth > 0)) + current = "" + optional_depth += 1 + continue + if ch == "]": + token = current.strip() + if token: + params.append(parse_command_token(token, optional_depth > 0)) + current = "" + optional_depth = max(optional_depth - 1, 0) + continue + if ch == ",": + token = current.strip() + if token: + params.append(parse_command_token(token, optional_depth > 0)) + current = "" + continue + current += ch + token = current.strip() + if token: + params.append(parse_command_token(token, optional_depth > 0)) + return command_name, params + + +def parse_command_token(token: str, is_optional: bool) -> Dict: + token = token.strip() + param_type = "Variant" + name = token + if ":" in token: + name_part, type_part = token.split(":", 1) + name = name_part.strip() + type_candidate = type_part.strip() + if type_candidate: + param_type = type_candidate + if "=" in param_type: + param_type = param_type.split("=", 1)[0].strip() + if "=" in name: + name = name.split("=", 1)[0].strip() + is_optional = True + return {"name": name, "type": param_type, "required": not is_optional} + + +def reformat_command_docstring( + block_lines: List[str], + indent: str, + path: Path, +) -> Optional[List[str]]: + content_lines = [strip_doc_prefix(line, indent) for line in block_lines] + if not content_lines: + return None + + signature_line = content_lines[0].strip() + command_name, params_info = parse_command_signature(signature_line) + + desc_lines: List[str] = [] + params: List[Dict[str, str]] = [] + param_buffer: Optional[Dict[str, str]] = None + extra_lines: List[str] = [] + in_param_section = False + + for raw_line in content_lines[1:]: + stripped = raw_line.strip() + lowered = stripped.lower() + if not stripped or lowered == "[br]": + continue + if stripped.startswith("@ESC"): + break + if lowered.startswith("#### parameters") or lowered.startswith("**parameters"): + in_param_section = True + param_buffer = None + continue + if lowered.startswith("parameters"): + in_param_section = True + param_buffer = None + continue + if in_param_section and is_table_structure_line(raw_line): + continue + if in_param_section: + table_entry = parse_table_row(raw_line) + if table_entry: + entry = {"name": table_entry["name"], "desc": table_entry["desc"]} + params.append(entry) + param_buffer = entry + continue + bullet = parse_param_bullet(raw_line) + if bullet and in_param_section: + name, desc = bullet + entry = {"name": name, "desc": desc} + params.append(entry) + param_buffer = entry + continue + if in_param_section and param_buffer: + param_buffer["desc"] = append_extra_text(param_buffer.get("desc", ""), stripped) + continue + if in_param_section: + extra_entry = strip_trailing_br(stripped) + if extra_entry and extra_entry.strip().lower() != "none.": + extra_lines.append(extra_entry) + continue + desc_lines.append(strip_trailing_br(stripped)) + + desc_text = " ".join(filter(None, desc_lines)).strip() + desc_text = clean_desc_text(desc_text) + param_map = {entry["name"]: entry.get("desc", "") for entry in params} + rows: List[Tuple[str, str, str, str]] = [] + for param in params_info: + name = param["name"] + type_name = format_type_cell(param["type"]) + required = "yes" if param["required"] else "no" + desc = param_map.pop(name, "").strip() + desc_key = desc.strip() + if not desc_key or desc_key.startswith(DEFAULT_PARAM_DESC): + override = get_param_override("command", path, command_name, name) + if override: + desc = override.strip() + desc_key = desc + if not desc_key: + desc = DEFAULT_PARAM_DESC + else: + desc = desc_key + rows.append((name, type_name, desc, required)) + rows = merge_extra_rows(rows, list(param_map.items())) + + new_block: List[str] = [] + new_block.append(f"{indent}## {signature_line}") + new_block.append(f"{indent}##") + if desc_text: + new_block.append(f"{indent}## {desc_text}[br]") + else: + new_block.append(f"{indent}## [br]") + new_block.append(f"{indent}## [br]") + new_block.append(f"{indent}## #### Parameters[br]") + new_block.append(f"{indent}## [br]") + if rows: + new_block.append(f"{indent}## | Name | Type | Description | Required? |[br]") + new_block.append(f"{indent}## |:-----|:-----|:------------|:----------|[br]") + for name, type_name, description, required in rows: + new_block.append(f"{indent}## |{name}|{type_name}|{description}|{required}|[br]") + new_block.append(f"{indent}## [br]") + for line in extra_lines: + if line: + new_block.append(f"{indent}## {line}[br]") + else: + new_block.append(f"{indent}## None.") + new_block.append(f"{indent}## [br]") + for line in extra_lines: + if line: + new_block.append(f"{indent}## {line}[br]") + return new_block + + +def normalize_return_sentence(text: str) -> str: + text = text.strip() + if not text: + return text + first_word = text.split(" ", 1)[0].lower() + if first_word in {"the", "a", "an"} and len(text) > 1: + text = text[0].lower() + text[1:] + return text + + +def infer_return_type(declared_type: str, return_desc: str) -> str: + declared_type = (declared_type or "").strip() + if declared_type and declared_type != "void": + return declared_type + if not return_desc: + return declared_type or "void" + lowered = return_desc.lower() + if "nothing" in lowered or "no value" in lowered: + return "void" + if "true" in lowered or "false" in lowered: + return "bool" + if "array" in lowered: + return "Array" + if "dictionary" in lowered: + return "Dictionary" + if "string" in lowered: + return "String" + if "float" in lowered: + return "float" + if "int" in lowered: + return "int" + if "vector2" in lowered: + return "Vector2" + if "vector3" in lowered: + return "Vector3" + return "Variant" + + +def parse_signal_signature(signal_line: str) -> Tuple[str, List[str]]: + match = re.match(r"\s*signal\s+([A-Za-z0-9_]+)\s*(?:\((.*)\))?", signal_line) + if not match: + return "", [] + signal_name = match.group(1) + params_fragment = (match.group(2) or "").strip() + if not params_fragment: + return signal_name, [] + params = [] + current = "" + depth = 0 + for ch in params_fragment: + if ch == "," and depth == 0: + token = current.strip() + if token: + params.append(token) + current = "" + continue + if ch in "([{": + depth += 1 + elif ch in ")]}": + depth = max(depth - 1, 0) + current += ch + token = current.strip() + if token: + params.append(token) + names = [] + for token in params: + if ":" in token: + token = token.split(":", 1)[0].strip() + if "=" in token: + token = token.split("=", 1)[0].strip() + names.append(token) + return signal_name, names + + +def reformat_signal_docstring( + block_lines: List[str], + indent: str, + signal_line: str, + path: Path, +) -> Optional[List[str]]: + content_lines = [strip_doc_prefix(line, indent) for line in block_lines] + if not content_lines: + return None + + desc_lines: List[str] = [] + params: List[Dict[str, str]] = [] + param_buffer: Optional[Dict[str, str]] = None + in_param_section = False + + for raw_line in content_lines: + stripped = raw_line.strip() + lowered = stripped.lower() + if not stripped or lowered == "[br]": + continue + if lowered.startswith("#### parameters") or lowered.startswith("**parameters"): + in_param_section = True + param_buffer = None + continue + if lowered.startswith("parameters"): + in_param_section = True + param_buffer = None + continue + if lowered.startswith("#### returns") or lowered.startswith("**returns"): + in_param_section = False + continue + if in_param_section and is_table_structure_line(raw_line): + continue + if in_param_section: + table_entry = parse_table_row(raw_line) + if table_entry: + entry = {"name": table_entry["name"], "desc": table_entry["desc"]} + params.append(entry) + param_buffer = entry + continue + bullet = parse_param_bullet(raw_line) + if bullet and in_param_section: + name, desc = bullet + entry = {"name": name, "desc": desc} + params.append(entry) + param_buffer = entry + continue + if in_param_section and param_buffer: + param_buffer["desc"] = append_extra_text(param_buffer.get("desc", ""), stripped) + continue + if in_param_section: + continue + desc_lines.append(strip_trailing_br(stripped)) + + desc_text = " ".join(filter(None, desc_lines)).strip() + desc_text = clean_desc_text(desc_text) + if not desc_text: + desc_text = "No description provided." + + signal_name, signature_params = parse_signal_signature(signal_line) + param_map = {entry["name"]: entry.get("desc", "") for entry in params} + rows: List[Tuple[str, str, str, str]] = [] + for param_name in signature_params: + desc = param_map.pop(param_name, "").strip() + desc_key = desc.strip() + if not desc_key or desc_key.startswith(DEFAULT_PARAM_DESC): + override = get_param_override("signal", path, signal_name, param_name) + if override: + desc = override.strip() + desc_key = desc + if not desc_key: + desc = DEFAULT_PARAM_DESC + else: + desc = desc_key + rows.append((param_name, format_type_cell(None), desc, "yes")) + for extra_name, desc in param_map.items(): + name = extra_name.strip() + if not name: + continue + cleaned_desc = desc.strip() + if not cleaned_desc or cleaned_desc.startswith(DEFAULT_PARAM_DESC): + override = get_param_override("signal", path, signal_name, name) + if override: + cleaned_desc = override + if not cleaned_desc: + cleaned_desc = DEFAULT_PARAM_DESC + rows.append((name, format_type_cell(None), cleaned_desc, "yes")) + + new_block: List[str] = [] + new_block.append(f"{indent}## {desc_text}[br]") + new_block.append(f"{indent}## [br]") + new_block.append(f"{indent}## #### Parameters[br]") + new_block.append(f"{indent}## [br]") + if rows: + new_block.append(f"{indent}## | Name | Type | Description | Required? |[br]") + new_block.append(f"{indent}## |:-----|:-----|:------------|:----------|[br]") + for name, type_name, description, required in rows: + new_block.append(f"{indent}## |{name}|{type_name}|{description}|{required}|[br]") + new_block.append(f"{indent}## [br]") + else: + new_block.append(f"{indent}## None.") + new_block.append(f"{indent}## [br]") + return new_block + + +def process_file(path: Path) -> Tuple[str, bool]: + text = path.read_text() + lines = text.splitlines() + i = 0 + changed = False + while i < len(lines): + line = lines[i] + stripped = line.lstrip() + if not stripped.startswith("##") or stripped.startswith("## @"): + i += 1 + continue + indent_match = re.match(r"^(\s*)##", line) + if not indent_match: + i += 1 + continue + indent = indent_match.group(1) + block_start = i + block_end = i + while block_end < len(lines): + stripped_block = lines[block_end].lstrip() + if not stripped_block.startswith("##") or stripped_block.startswith("## @"): + break + block_end += 1 + block_lines = lines[block_start:block_end] + + # Determine context + k = block_end + signature_lines: List[str] = [] + while k < len(lines): + candidate = lines[k].strip() + if candidate == "": + k += 1 + continue + if candidate.startswith("## @"): + k += 1 + continue + if candidate.startswith("##"): + break + break + if k >= len(lines): + i = block_end + continue + next_line = lines[k] + + if re.match(r"\s*(?:static\s+)?func\b", next_line): + signature_lines = [] + sig_index = k + while sig_index < len(lines): + signature_lines.append(lines[sig_index].strip()) + if lines[sig_index].strip().endswith(":"): + break + sig_index += 1 + new_block = reformat_function_docstring(block_lines, indent, signature_lines, path) + if new_block: + lines[block_start:block_end] = new_block + changed = True + block_end = block_start + len(new_block) + i = block_end + continue + elif "extends ESCBaseCommand" in next_line: + new_block = reformat_command_docstring(block_lines, indent, path) + if new_block: + lines[block_start:block_end] = new_block + changed = True + block_end = block_start + len(new_block) + i = block_end + continue + elif next_line.strip().startswith("signal "): + new_block = reformat_signal_docstring(block_lines, indent, next_line.strip(), path) + if new_block: + lines[block_start:block_end] = new_block + changed = True + block_end = block_start + len(new_block) + i = block_end + continue + i = block_end + new_text = "\n".join(lines) + if text.endswith("\n"): + new_text += "\n" + return new_text, changed + + +def main() -> None: + total_changed = 0 + for path in ROOT.rglob("*.gd"): + new_text, changed = process_file(path) + if changed: + path.write_text(new_text) + total_changed += 1 + + +if __name__ == "__main__": + main() diff --git a/ui/Baskerville.ttf b/ui/Baskerville.ttf deleted file mode 100644 index 21f63f8..0000000 Binary files a/ui/Baskerville.ttf and /dev/null differ diff --git a/ui/Baskerville.ttf.import b/ui/Baskerville.ttf.import deleted file mode 100644 index 512c262..0000000 --- a/ui/Baskerville.ttf.import +++ /dev/null @@ -1,36 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://dkm7ju10ix768" -path="res://.godot/imported/Baskerville.ttf-67b2866e0315ee966d2ae5a45e96f132.fontdata" - -[deps] - -source_file="res://ui/Baskerville.ttf" -dest_files=["res://.godot/imported/Baskerville.ttf-67b2866e0315ee966d2ae5a45e96f132.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -modulate_color_glyphs=false -hinting=1 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/ui/cursor.png b/ui/cursor.png deleted file mode 100644 index bbcbef3..0000000 Binary files a/ui/cursor.png and /dev/null differ diff --git a/ui/cursor.png.import b/ui/cursor.png.import deleted file mode 100644 index 8b9b4b2..0000000 --- a/ui/cursor.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://v5iga85oxfat" -path="res://.godot/imported/cursor.png-4a6c9cbc7d12af26a5f0d9ddd83bc907.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/cursor.png" -dest_files=["res://.godot/imported/cursor.png-4a6c9cbc7d12af26a5f0d9ddd83bc907.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/cursor_shadow.png b/ui/cursor_shadow.png deleted file mode 100644 index 38ff630..0000000 Binary files a/ui/cursor_shadow.png and /dev/null differ diff --git a/ui/cursor_shadow.png.import b/ui/cursor_shadow.png.import deleted file mode 100644 index c7b9b8f..0000000 --- a/ui/cursor_shadow.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://sn26gsnlnb2t" -path="res://.godot/imported/cursor_shadow.png-c2c59a9c8d0aee1b76eef7ec7ee517ff.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/cursor_shadow.png" -dest_files=["res://.godot/imported/cursor_shadow.png-c2c59a9c8d0aee1b76eef7ec7ee517ff.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/frame.png b/ui/frame.png deleted file mode 100644 index f9795bf..0000000 Binary files a/ui/frame.png and /dev/null differ diff --git a/ui/frame.png.import b/ui/frame.png.import deleted file mode 100644 index 0a4a5ea..0000000 --- a/ui/frame.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cvjpwcupuqasb" -path="res://.godot/imported/frame.png-947f3fa2287ab12b0ad8c31a6795dd92.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/frame.png" -dest_files=["res://.godot/imported/frame.png-947f3fa2287ab12b0ad8c31a6795dd92.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/settings.png b/ui/settings.png deleted file mode 100644 index b23f064..0000000 Binary files a/ui/settings.png and /dev/null differ diff --git a/ui/settings.png.import b/ui/settings.png.import deleted file mode 100644 index 93b3f40..0000000 --- a/ui/settings.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://i8shy6bxafxd" -path="res://.godot/imported/settings.png-b2aa72797124287466e50ef66cba2e83.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/settings.png" -dest_files=["res://.godot/imported/settings.png-b2aa72797124287466e50ef66cba2e83.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/settings_active.png b/ui/settings_active.png deleted file mode 100644 index 06a9a6b..0000000 Binary files a/ui/settings_active.png and /dev/null differ diff --git a/ui/settings_active.png.import b/ui/settings_active.png.import deleted file mode 100644 index 5e07498..0000000 --- a/ui/settings_active.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://0mee421i7va3" -path="res://.godot/imported/settings_active.png-df5dbb5a993c2546dacd54bf78857c73.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/settings_active.png" -dest_files=["res://.godot/imported/settings_active.png-df5dbb5a993c2546dacd54bf78857c73.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/testmenu.png b/ui/testmenu.png deleted file mode 100644 index f76a387..0000000 Binary files a/ui/testmenu.png and /dev/null differ diff --git a/ui/testmenu.png.import b/ui/testmenu.png.import deleted file mode 100644 index b52c139..0000000 --- a/ui/testmenu.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dlux7p6fc5j8i" -path="res://.godot/imported/testmenu.png-23ade5303336d134514887845890791f.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/testmenu.png" -dest_files=["res://.godot/imported/testmenu.png-23ade5303336d134514887845890791f.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/textbox.png b/ui/textbox.png deleted file mode 100644 index e2f8c27..0000000 Binary files a/ui/textbox.png and /dev/null differ diff --git a/ui/textbox.png.import b/ui/textbox.png.import deleted file mode 100644 index 72d982c..0000000 --- a/ui/textbox.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dt3nreoxkpek6" -path="res://.godot/imported/textbox.png-ce642ba2352e3efa917e621d8d460fbb.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/textbox.png" -dest_files=["res://.godot/imported/textbox.png-ce642ba2352e3efa917e621d8d460fbb.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/textbox_arrow.png b/ui/textbox_arrow.png deleted file mode 100644 index e043b24..0000000 Binary files a/ui/textbox_arrow.png and /dev/null differ diff --git a/ui/textbox_arrow.png.import b/ui/textbox_arrow.png.import deleted file mode 100644 index 40a2edb..0000000 --- a/ui/textbox_arrow.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cckrufe4kgphu" -path="res://.godot/imported/textbox_arrow.png-b028454b381edebf894d123a3679d661.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/textbox_arrow.png" -dest_files=["res://.godot/imported/textbox_arrow.png-b028454b381edebf894d123a3679d661.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/textbox_container.png b/ui/textbox_container.png deleted file mode 100644 index e149b9a..0000000 Binary files a/ui/textbox_container.png and /dev/null differ diff --git a/ui/textbox_container.png.import b/ui/textbox_container.png.import deleted file mode 100644 index 0280124..0000000 --- a/ui/textbox_container.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c4qda255ivqgo" -path="res://.godot/imported/textbox_container.png-ae89d8937b29926c84644648eeed3e1b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/textbox_container.png" -dest_files=["res://.godot/imported/textbox_container.png-ae89d8937b29926c84644648eeed3e1b.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/ui/title.png b/ui/title.png deleted file mode 100644 index ff46d34..0000000 Binary files a/ui/title.png and /dev/null differ diff --git a/ui/title.png.import b/ui/title.png.import deleted file mode 100644 index 3c330cd..0000000 --- a/ui/title.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bhpfnlt234n2y" -path="res://.godot/imported/title.png-5eea9f4654390823b21fcaef01d8e1c7.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://ui/title.png" -dest_files=["res://.godot/imported/title.png-5eea9f4654390823b21fcaef01d8e1c7.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 -- cgit v1.3