From b230966fc625fb5dd1a5cebcea8945bfaab22f61 Mon Sep 17 00:00:00 2001 From: Julian Murgia Date: Tue, 24 Aug 2021 08:19:55 +0200 Subject: [PATCH] Moved and split escoria._ev_left_click_on_item() function into ESCController (#371) * Moved and split escoria._ev_left_click_on_item() function into ESCController. Added a boolean in ESCWalkContext to hold whether interact should happen after destination is reached. Co-authored-by: StraToN --- .../game/core-scripts/esc_controller.gd | 247 ++++++++++++++++++ .../game/core-scripts/esc_walk_context.gd | 9 +- addons/escoria-core/game/escoria.gd | 188 ++----------- docs/api/ESCController.md | 18 ++ docs/api/ESCInputsManager.md | 8 + docs/api/ESCWalkContext.md | 12 +- docs/api/escoria.gd.md | 21 +- game/characters/mark/mark.tscn | 108 ++++---- game/translations/main_menu.en.translation | Bin 921 -> 560 bytes game/translations/main_menu.fr.translation | Bin 956 -> 576 bytes project.godot | 10 + 11 files changed, 396 insertions(+), 225 deletions(-) create mode 100644 addons/escoria-core/game/core-scripts/esc_controller.gd create mode 100644 docs/api/ESCController.md diff --git a/addons/escoria-core/game/core-scripts/esc_controller.gd b/addons/escoria-core/game/core-scripts/esc_controller.gd new file mode 100644 index 00000000..0b67782a --- /dev/null +++ b/addons/escoria-core/game/core-scripts/esc_controller.gd @@ -0,0 +1,247 @@ +# This script contains functions called by gamedev's game.gd. +# These functions convert input actions into game actions. For example: +# "click on background" -> player walks to position +# "click on item" -> player walks to item then performs the action defined +# by current verb + +class_name ESCController + + +# Makes an object walk to a destination. This can be either a 2D position or +# another object. +# +# #### Parameters +# +# - moving_obj_id: global id of the object that needs to move +# - destination: Position2D or ESCObject holding the moving object to head to +# - is_fast: if true, the walk is performed at fast speed (defined in the moving +# object. +func perform_walk( + moving_obj: ESCObject, + destination, + is_fast: bool = false +): + # 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.interact_position + + var walk_context = ESCWalkContext.new( + destination.node, + Vector2(), + is_fast, + true + ) + + moving_obj.node.walk_to(target_position, walk_context) + + else: + escoria.logger.report_errors( + "esc_controller.gd:perform_walk()", + [ + "Function expected either a Vector2 or ESCObject type " + \ + "for destination parameter. Actual was: %s " % destination + ] + ) + return + + +# Event handler when an object/item was clicked +# +# #### Parameters +# +# - obj: Object that was left clicked +# - event: Input event that was received +# - default_action: if true, run the inventory default action +func perform_inputevent_on_object( + obj: ESCObject, + event: InputEvent, + default_action: bool = false +): + """ + This algorithm: + - 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("%s left-clicked with event " % obj.global_id, [event]) + + # Don't interact after player movement towards object + # (because object is inactive for example) + var dont_interact = false + + var destination_position: Vector2 = escoria.main.current_scene.player.\ + global_position + + # If clicked object not in inventory, player walks towards it + if not escoria.inventory_manager.inventory_has(obj.global_id): + var context = _walk_towards_object( + obj, + event.position, + event.doubleclick + ) + if context is GDScriptFunctionState: + context = yield(_walk_towards_object( + obj, + event.position, + event.doubleclick + ), "completed") + destination_position = context.target_position + dont_interact = context.dont_interact_on_arrival + + # If no interaction should happen after player has arrived, leave + # immediately. + if dont_interact: + return + + var player_global_pos = escoria.main.current_scene.player.global_position + var clicked_position = event.position + + # If player has arrived at the position he was supposed to reach + # so he can interact + if player_global_pos == destination_position: + # Manage exits + if obj.node.is_exit and escoria.action_manager.current_action \ + in ["", "walk"]: + escoria.action_manager.activate("exit_scene", obj) + else: + # Manage movements towards object before activating it + if escoria.action_manager.current_action in ["", "walk"] and \ + not escoria.inventory_manager.inventory_has(obj.global_id): + escoria.action_manager.activate("arrived", obj) + # Manage action on object + elif not escoria.action_manager.current_action in ["", "walk"]: + # Check if clicked item awaits a combination + var need_combine = _check_item_needs_combine( + obj, + default_action + ) + + # If apply_interact, perform combine between items + if need_combine: + escoria.action_manager.activate( + escoria.action_manager.current_action, + escoria.action_manager.current_tool, + obj + ) + + else: + escoria.action_manager.activate( + escoria.action_manager.current_action, + obj + ) + +# Checks if object requires a combination with another, according to +# currently selected action verb (or check with default action of the item). +# +# #### Parameters +# +# - obj: the ESCObject to test +# - default_action: if true, the check is done on the object's default action +func _check_item_needs_combine(obj: ESCObject, default_action: bool) -> bool: + var need_combine = false + # Check if current_action and current_tool are already set + if escoria.action_manager.current_action and \ + escoria.action_manager.current_tool: + if escoria.action_manager.current_action in escoria.action_manager\ + .current_tool.node.combine_if_action_used_among: + need_combine = true + else: + escoria.action_manager.current_tool = obj + elif default_action: + if escoria.inventory_manager.inventory_has(obj.global_id): + escoria.action_manager.current_action = \ + obj.node.default_action_inventory + else: + escoria.action_manager.current_action = \ + obj.node.default_action + elif escoria.action_manager.current_action in \ + obj.node.combine_if_action_used_among: + escoria.action_manager.current_tool = obj + return need_combine + + +# Makes the player character walk towards the clicked item. +# Returns the resulting walk context. +# +# #### Parameters +# +# - obj: the object that was clicked +# - clicked_position: the Position2D of the input click +# - walk_fast: if true, the player will walk fast to the object +func _walk_towards_object( + obj: ESCObject, + clicked_position: Vector2, + walk_fast: bool +) -> ESCWalkContext: + var destination_position: Vector2 + var dont_interact: bool = false + + # If clicked object is interactive, get destination position from it. + if escoria.object_manager.get_object(obj.global_id).interactive: + if obj.node.get_interact_position() != null: + destination_position = obj.node.get_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) + + # Wait for the player to arrive before continuing with action. + var context: ESCWalkContext = yield( + escoria.main.current_scene.player, + "arrived" + ) + escoria.logger.info("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 diff --git a/addons/escoria-core/game/core-scripts/esc_walk_context.gd b/addons/escoria-core/game/core-scripts/esc_walk_context.gd index 5d0cd794..f58af740 100644 --- a/addons/escoria-core/game/core-scripts/esc_walk_context.gd +++ b/addons/escoria-core/game/core-scripts/esc_walk_context.gd @@ -10,15 +10,20 @@ var target_object: ESCObject = null # The target position var target_position: Vector2 = Vector2() -# Wether to move fast +# Whether to move fast var fast: bool +# Whether an interaction should NOT happen after walk reaches destination +var dont_interact_on_arrival: bool + func _init( p_target_object: ESCObject, p_target_position: Vector2, - p_fast: bool + 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/escoria.gd b/addons/escoria-core/game/escoria.gd index 0efd4c56..3b230453 100644 --- a/addons/escoria-core/game/escoria.gd +++ b/addons/escoria-core/game/escoria.gd @@ -80,6 +80,10 @@ var inputs_manager: ESCInputsManager # Savegames and settings manager var save_manager: ESCSaveManager +# The controller in charge of converting an action verb on a game object +# into an actual action +var controller: ESCController + # Initialize various objects func _init(): @@ -97,6 +101,7 @@ func _init(): self.resource_cache.start() self.save_manager = ESCSaveManager.new() self.inputs_manager = ESCInputsManager.new() + self.controller = ESCController.new() # Load settings @@ -137,9 +142,13 @@ func do(action: String, params: Array = []) -> void: "walk": self.action_manager.clear_current_action() + var walk_fast = false + if params.size() > 2: + walk_fast = true if params[2] else false + # Check moving object. - if not self.object_manager.has(params[0]): - self.logger.report_errors( + if not escoria.object_manager.has(params[0]): + escoria.logger.report_errors( "escoria.gd:do()", [ "Walk action requested on inexisting " + \ @@ -148,53 +157,26 @@ func do(action: String, params: Array = []) -> void: ) return - var moving_obj = self.object_manager.get_object(params[0])\ - .node + var moving_obj = escoria.object_manager.get_object(params[0]) + var target - # Walk to Position2D. - if params[1] is Vector2: - var target_position = params[1] - var is_fast: bool = false - if params.size() > 2 and params[2] == true: - is_fast = true - var walk_context = ESCWalkContext.new( - null, - target_position, - is_fast - ) - moving_obj.walk_to(target_position, walk_context) - - # Walk to object from its id - elif params[1] is String: - if not self.object_manager.has(params[1]): - self.logger.report_errors( + if params[1] is String: + if not escoria.object_manager.has(params[1]): + escoria.logger.report_errors( "escoria.gd:do()", [ - "Walk action requested TOWARDS " +\ - "inexisting object: %s" % params[1] + "Walk action requested to inexisting " + \ + "object: %s " % params[1] ] ) return - - var object = self.object_manager.get_object(params[1]) - if object: - var target_position: Vector2 - if object.node is ESCLocation: - target_position = object.node.global_position - else: - target_position = object.node.interact_position - - var is_fast: bool = false - if params.size() > 2 and params[2] == true: - is_fast = true - var walk_context = ESCWalkContext.new( - object, - Vector2(), - is_fast - ) - moving_obj.walk_to(target_position, walk_context) - + target = escoria.object_manager.get_object(params[1]) + elif params[1] is Vector2: + target = params[1] + + self.controller.perform_walk(moving_obj, target, walk_fast) + "item_left_click": if params[0] is String: self.logger.info( @@ -202,7 +184,7 @@ func do(action: String, params: Array = []) -> void: [params[0]] ) var item = self.object_manager.get_object(params[0]) - _ev_left_click_on_item(item, params[1]) + self.controller.perform_inputevent_on_object(item, params[1]) "item_right_click": if params[0] is String: @@ -211,7 +193,7 @@ func do(action: String, params: Array = []) -> void: [params[0]] ) var item = self.object_manager.get_object(params[0]) - _ev_left_click_on_item(item, params[1], true) + self.controller.perform_inputevent_on_object(item, params[1], true) "trigger_in": var trigger_id = params[0] @@ -248,124 +230,6 @@ func do(action: String, params: Array = []) -> void: pass -# Event handler when an object/item was clicked -# -# #### Parameters -# -# - ob: Object that was left clicked -# - event: Input event that was received -# - default_action: Run the inventory default action -func _ev_left_click_on_item(obj, event, default_action = false): - if obj is String: - obj = object_manager.get_object(obj) - self.logger.info(obj.global_id + " left-clicked with event ", [event]) - - var need_combine = false - # Check if current_action and current_tool are already set - if self.action_manager.current_action: - if self.action_manager.current_tool: - if self.action_manager.current_action in self.action_manager\ - .current_tool.node.combine_if_action_used_among: - need_combine = true - else: - self.action_manager.current_tool = obj - else: - if default_action: - if self.inventory_manager.inventory_has(obj.global_id): - self.action_manager.current_action = \ - obj.node.default_action_inventory - else: - self.action_manager.current_action = \ - obj.node.default_action - elif self.action_manager.current_action in \ - obj.node.combine_if_action_used_among: - self.action_manager.current_tool = obj - - - # Don't interact after player movement towards object - # (because object is inactive for example) - var dont_interact = false - var destination_position: Vector2 = main.current_scene.player.\ - global_position - - # Create walk context - var walk_context = ESCWalkContext.new( - obj, - Vector2(), - event.doubleclick - ) - - # If object not in inventory, player walks towards it - if not inventory_manager.inventory_has(obj.global_id): - var clicked_object_has_interact_position = false - - if object_manager.get_object(obj.global_id).interactive: - if obj.node.get_interact_position() != null: - destination_position = obj.node.get_interact_position() - clicked_object_has_interact_position = true - else: - destination_position = obj.node.position - else: - destination_position = event.position - dont_interact = true - - main.current_scene.player.walk_to( - destination_position, - walk_context - ) - - # Wait for the player to arrive before continuing with action. - var context: ESCWalkContext = yield( - main.current_scene.player, - "arrived" - ) - - self.logger.info("Context arrived: %s" % context) - - if context.target_object and \ - context.target_object.global_id != walk_context.\ - target_object.global_id: - dont_interact = true - elif context.target_position != walk_context.target_position: - dont_interact = true - - # If no interaction should happen after player has arrived, leave immediately. - if dont_interact: - return - - var player_global_pos = main.current_scene.player.global_position - var clicked_position = event.position - - # If player has arrived at the position he was supposed to reach so he can interact - if player_global_pos == destination_position: - # Manage exits - if obj.node.is_exit and self.action_manager.current_action == "" \ - or self.action_manager.current_action == "walk": - self.action_manager.activate("exit_scene", obj) - else: - # Manage movements towards object before activating it - if self.action_manager.current_action == "" \ - or self.action_manager.current_action == "walk": - if destination_position != clicked_position \ - and not inventory_manager.inventory_has(obj.global_id): - self.action_manager.activate("arrived", obj) - # Manage action on object - elif self.action_manager.current_action != "" and \ - self.action_manager.current_action != "walk": - # If apply_interact, perform combine between items - if need_combine: - self.action_manager.activate( - self.action_manager.current_action, - self.action_manager.current_tool, - obj - ) - - else: - self.action_manager.activate( - self.action_manager.current_action, - obj - ) - # Apply the loaded settings # diff --git a/docs/api/ESCController.md b/docs/api/ESCController.md new file mode 100644 index 00000000..c3a1031d --- /dev/null +++ b/docs/api/ESCController.md @@ -0,0 +1,18 @@ + + +# ESCController + +## Method Descriptions + +### perform\_walk + +```gdscript +func perform_walk(moving_obj: ESCObject, destination, is_fast: bool = false) +``` + +### perform\_inputevent\_on\_object + +```gdscript +func perform_inputevent_on_object(obj: ESCObject, event: InputEvent, default_action: bool = false) +``` + diff --git a/docs/api/ESCInputsManager.md b/docs/api/ESCInputsManager.md index 894329d4..436205ed 100644 --- a/docs/api/ESCInputsManager.md +++ b/docs/api/ESCInputsManager.md @@ -57,6 +57,14 @@ The global id fo the topmost item from the hover_stack ## Method Descriptions +### register\_core + +```gdscript +func register_core() +``` + +Register core signals (from escoria.gd) + ### register\_inventory\_item ```gdscript diff --git a/docs/api/ESCWalkContext.md b/docs/api/ESCWalkContext.md index 912b3493..94b674d9 100644 --- a/docs/api/ESCWalkContext.md +++ b/docs/api/ESCWalkContext.md @@ -33,13 +33,21 @@ The target position var fast: bool ``` -Wether to move fast +Whether to move fast + +### dont\_interact\_on\_arrival + +```gdscript +var dont_interact_on_arrival: bool +``` + +Whether an interaction should NOT happen after walk reaches destination ## Method Descriptions ### \_init ```gdscript -func _init(p_target_object: ESCObject, p_target_position: Vector2, p_fast: bool) +func _init(p_target_object: ESCObject, p_target_position: Vector2, p_fast: bool, p_dont_interact_on_arrival: bool) ``` diff --git a/docs/api/escoria.gd.md b/docs/api/escoria.gd.md index 54d022c4..ba5cc75e 100644 --- a/docs/api/escoria.gd.md +++ b/docs/api/escoria.gd.md @@ -193,6 +193,15 @@ var save_manager: ESCSaveManager Savegames and settings manager +### controller + +```gdscript +var controller: ESCController +``` + +The controller in charge of converting an action verb on a game object +into an actual action + ## Method Descriptions ### new\_game @@ -219,12 +228,14 @@ Run a generic action ### set\_game\_paused ```gdscript -func set_game_paused() +func set_game_paused(p_paused: bool) ``` -### set\_game\_unpaused +Pauses or unpause the game -```gdscript -func set_game_unpaused() -``` +#### Parameters +- p_paused: if true, pauses the game. If false, unpauses the game. +## Signals + +- signal request_pause_menu(): Signal sent when pause menu has to be displayed diff --git a/game/characters/mark/mark.tscn b/game/characters/mark/mark.tscn index b5b8dcc1..013026f5 100644 --- a/game/characters/mark/mark.tscn +++ b/game/characters/mark/mark.tscn @@ -9,202 +9,202 @@ [ext_resource path="res://game/characters/mark/png/mark_talk_right.png" type="Texture" id=7] [sub_resource type="AtlasTexture" id=1] -atlas = ExtResource( 4 ) -region = Rect2( 120, 0, 24, 70 ) +atlas = ExtResource( 2 ) +region = Rect2( 0, 0, 24, 70 ) [sub_resource type="AtlasTexture" id=2] atlas = ExtResource( 2 ) -region = Rect2( 0, 0, 24, 70 ) +region = Rect2( 24, 0, 24, 70 ) [sub_resource type="AtlasTexture" id=3] atlas = ExtResource( 2 ) -region = Rect2( 24, 0, 24, 70 ) +region = Rect2( 48, 0, 24, 70 ) [sub_resource type="AtlasTexture" id=4] -atlas = ExtResource( 2 ) +atlas = ExtResource( 4 ) region = Rect2( 48, 0, 24, 70 ) [sub_resource type="AtlasTexture" id=5] -atlas = ExtResource( 4 ) -region = Rect2( 48, 0, 24, 70 ) +atlas = ExtResource( 7 ) +region = Rect2( 0, 0, 24, 70 ) [sub_resource type="AtlasTexture" id=6] atlas = ExtResource( 7 ) -region = Rect2( 0, 0, 24, 70 ) +region = Rect2( 24, 0, 24, 70 ) [sub_resource type="AtlasTexture" id=7] atlas = ExtResource( 7 ) -region = Rect2( 24, 0, 24, 70 ) +region = Rect2( 48, 0, 24, 70 ) [sub_resource type="AtlasTexture" id=8] atlas = ExtResource( 7 ) -region = Rect2( 48, 0, 24, 70 ) +region = Rect2( 72, 0, 24, 70 ) [sub_resource type="AtlasTexture" id=9] atlas = ExtResource( 7 ) -region = Rect2( 72, 0, 24, 70 ) - -[sub_resource type="AtlasTexture" id=10] -atlas = ExtResource( 7 ) region = Rect2( 96, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=11] +[sub_resource type="AtlasTexture" id=10] atlas = ExtResource( 4 ) region = Rect2( 144, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=12] +[sub_resource type="AtlasTexture" id=11] atlas = ExtResource( 4 ) region = Rect2( 168, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=13] +[sub_resource type="AtlasTexture" id=12] atlas = ExtResource( 4 ) region = Rect2( 192, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=14] +[sub_resource type="AtlasTexture" id=13] atlas = ExtResource( 4 ) region = Rect2( 216, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=15] +[sub_resource type="AtlasTexture" id=14] atlas = ExtResource( 4 ) region = Rect2( 240, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=16] +[sub_resource type="AtlasTexture" id=15] atlas = ExtResource( 4 ) region = Rect2( 264, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=17] +[sub_resource type="AtlasTexture" id=16] atlas = ExtResource( 4 ) region = Rect2( 288, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=18] +[sub_resource type="AtlasTexture" id=17] atlas = ExtResource( 4 ) region = Rect2( 312, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=19] +[sub_resource type="AtlasTexture" id=18] atlas = ExtResource( 4 ) region = Rect2( 0, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=20] +[sub_resource type="AtlasTexture" id=19] atlas = ExtResource( 4 ) region = Rect2( 336, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=21] +[sub_resource type="AtlasTexture" id=20] atlas = ExtResource( 4 ) region = Rect2( 360, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=22] +[sub_resource type="AtlasTexture" id=21] atlas = ExtResource( 4 ) region = Rect2( 384, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=23] +[sub_resource type="AtlasTexture" id=22] atlas = ExtResource( 4 ) region = Rect2( 72, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=24] +[sub_resource type="AtlasTexture" id=23] atlas = ExtResource( 4 ) region = Rect2( 96, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=25] +[sub_resource type="AtlasTexture" id=24] atlas = ExtResource( 4 ) region = Rect2( 24, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=26] +[sub_resource type="AtlasTexture" id=25] atlas = ExtResource( 5 ) region = Rect2( 0, 0, 24, 70 ) +[sub_resource type="AtlasTexture" id=26] +atlas = ExtResource( 5 ) +region = Rect2( 24, 0, 24, 70 ) + [sub_resource type="AtlasTexture" id=27] atlas = ExtResource( 5 ) -region = Rect2( 24, 0, 24, 70 ) - -[sub_resource type="AtlasTexture" id=28] -atlas = ExtResource( 5 ) region = Rect2( 48, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=29] +[sub_resource type="AtlasTexture" id=28] atlas = ExtResource( 6 ) region = Rect2( 0, 0, 24, 70 ) -[sub_resource type="AtlasTexture" id=30] +[sub_resource type="AtlasTexture" id=29] atlas = ExtResource( 6 ) region = Rect2( 24, 0, 24, 70 ) +[sub_resource type="AtlasTexture" id=30] +atlas = ExtResource( 4 ) +region = Rect2( 120, 0, 24, 70 ) + [sub_resource type="SpriteFrames" id=31] animations = [ { -"frames": [ SubResource( 1 ) ], -"loop": true, -"name": "idle_down_left", -"speed": 5.0 -}, { -"frames": [ SubResource( 2 ), SubResource( 3 ), SubResource( 4 ), SubResource( 3 ), SubResource( 4 ) ], +"frames": [ SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 2 ), SubResource( 3 ) ], "loop": true, "name": "speak_down", "speed": 6.0 }, { -"frames": [ SubResource( 5 ) ], +"frames": [ SubResource( 4 ) ], "loop": true, "name": "idle_right", "speed": 5.0 }, { -"frames": [ SubResource( 6 ), SubResource( 7 ), SubResource( 8 ), SubResource( 9 ), SubResource( 10 ) ], +"frames": [ SubResource( 5 ), SubResource( 6 ), SubResource( 7 ), SubResource( 8 ), SubResource( 9 ) ], "loop": true, "name": "speak_right", "speed": 5.0 }, { -"frames": [ SubResource( 11 ), SubResource( 12 ), SubResource( 13 ), SubResource( 12 ) ], +"frames": [ SubResource( 10 ), SubResource( 11 ), SubResource( 12 ), SubResource( 11 ) ], "loop": true, "name": "walk_down", "speed": 6.0 }, { -"frames": [ SubResource( 14 ), SubResource( 15 ), SubResource( 16 ), SubResource( 17 ), SubResource( 18 ) ], +"frames": [ SubResource( 13 ), SubResource( 14 ), SubResource( 15 ), SubResource( 16 ), SubResource( 17 ) ], "loop": true, "name": "walk_right", "speed": 6.0 }, { -"frames": [ SubResource( 19 ) ], +"frames": [ SubResource( 18 ) ], "loop": true, "name": "idle_down", "speed": 5.0 }, { -"frames": [ SubResource( 20 ), SubResource( 21 ), SubResource( 22 ), SubResource( 21 ) ], +"frames": [ SubResource( 19 ), SubResource( 20 ), SubResource( 21 ), SubResource( 20 ) ], "loop": true, "name": "walk_up", "speed": 6.0 }, { -"frames": [ SubResource( 23 ) ], +"frames": [ SubResource( 22 ) ], "loop": true, "name": "idle_up", "speed": 5.0 }, { -"frames": [ SubResource( 24 ) ], +"frames": [ SubResource( 23 ) ], "loop": true, "name": "idle_left", "speed": 5.0 }, { -"frames": [ SubResource( 25 ) ], +"frames": [ SubResource( 24 ) ], "loop": true, "name": "idle_down_right", "speed": 5.0 }, { -"frames": [ SubResource( 26 ), SubResource( 27 ), SubResource( 28 ) ], +"frames": [ SubResource( 25 ), SubResource( 26 ), SubResource( 27 ) ], "loop": true, "name": "speak_down_right", "speed": 6.0 }, { -"frames": [ SubResource( 29 ), SubResource( 30 ), SubResource( 29 ), SubResource( 30 ), SubResource( 30 ) ], +"frames": [ SubResource( 28 ), SubResource( 29 ), SubResource( 28 ), SubResource( 29 ), SubResource( 29 ) ], "loop": true, "name": "speak_up", "speed": 3.0 +}, { +"frames": [ SubResource( 30 ) ], +"loop": true, +"name": "idle_down_left", +"speed": 5.0 } ] [sub_resource type="CapsuleShape2D" id=32] height = 0.0 [node name="mark" type="Area2D"] +pause_mode = 1 script = ExtResource( 1 ) global_id = "player" is_movable = true dialog_color = Color( 1, 1, 1, 1 ) -animation_player_node = NodePath("sprite") animations = ExtResource( 3 ) [node name="sprite" type="AnimatedSprite" parent="."] diff --git a/game/translations/main_menu.en.translation b/game/translations/main_menu.en.translation index f5f9c94618aaef5988e9d671cf5e73983e500067..7959765cca87e17d5fff412974df0ddb234b668e 100644 GIT binary patch literal 560 zcmV-00?++YQ$s@n000005C8zY0{{RR0ssIgwJ-f(yaII)0M^=GMgXxUjRAc1s};S1 z02&csvTptA^)`1nB&cgVeJ03Gz3ok?NTMvUiSJZ)&Co3T;42E(z!yhKGQ@Yi!n<=w zJHFRIv5^!(06zd-0F~Cg(LkFY{P3L;H3NgqCd2pT#X%-@k^~}l$rTOHa;AG%N0tcE^wg!(hu{q{0#mC|4GSzK-BsN z`~iN@U*ISCvFsGNDWF_WCAsKjv}WtYEz3qK|5aJsr_oJvUmj*+!Xup4I0{PmKl~#q z2t;a5{*aFqPK79iqtnyTVnH+k&CiUH&j_3tDK@l1$n;zuJ0TYxN=}g8uxcVnC>XIm zG)xfq1-!^XvBIli(rF1j1q?hTI#A975T_&*4eI|!i!jrzaL7Ug%PHKY!NhhzV!js` zr|_`GzzT%$-6FWjSjrG*>B@lM$7- zb8rPbAV2~nFjaR0V~4Tu{HBwD4a!t!1t28RVwQ$s_0AOjWv literal 921 zcmbW0ziSjh6vy9PPCfHOi69Dsxk6IJK#W0)GchD*FyMI?FDFD)j>$S~mh9~wJG=M; zt+chUh^aH#2xQYo5mXOva`@2KyYi2&Tb2sDcLI{sGJ-a0f6a(rgxU zGnlzYtX_Y5WfNvE7v^Ji|JBajo3L3dR>A5sTeaWqee@4+H@~vVN5S#T`PaYloic3O z5H`;Fm%%6t$(j(WyHa_>`!bhoK94>Zw|P&NoY?bU7N2z=!2b7M@4j!mV4avh$piX~ z{LF`pYx5aZ7e29e%$@v=yS33|{rH%C59|Nln;M|acVnN8tqh-?#BBW;djvcI*AwG8 zQoknY!qVg1va5wQc;1UeW~gDC71nL3*|=Q#ZnE` yyAxIQ$s@n000005C8z*0{{Rh0ssIgwJ-f(-vadz00v!VMgXxkYdEfU6tpre zkN}M0=f9Im>%uh@A{vOlWDX)*NxQPmP)XS)4J$^&rStXeA{{etOW@>G4|awx@$C+QnZ*rg|MdSm@Z-6Ahld<4e@PjHHS5Qq2 z7FohV(GC+43ksbaJ|H=-6EH!l9t@~N3XIcTt3+2s82tv8h{ubPKLL&%o*6g+r<8}( zbqvLs+8ebmjU1D9zYuzDp%t^?l(c?AuRmE`Kh*W3;W>6x%3NnxT>;9ESGP{i4V`nhe$=`<$gtI0X86Df+42NJOsIk_Y~lonJtqP zGO=}8n_>K+Cao<-k=fL|8!<_yS4s4KqM3AQ?4P^Gn?58Ui<3GDE4#Jty OGV(qnhJpoBQ$s_4pA1(3 literal 956 zcmbW0ziSjh6vy9fqMo_Jm;~gCByj#97J5ARt4O2c+dVH<&K_@;Cu9Ziopq9*Aul$!K? zQIS3!g43BHDTi(q*%^UYvLzjXyA+yFWe^LzlOK4NQ`w#7o1RqzV$4Wq|Q=wYhiSYe*Ss=VWT(iUR9q&kR~RSp279a(Y}bL>vfxBJ(_l$ z(1wl558ZdUqIy?}sEj`p-OCp~M^vf`uY%z@;Z(5n?N-xiH9cWdJJV_mEgQ3qYr7jj WxK5E;T=^D}8d{{u0Dp%x{M-Ky