From a0f15af10b1064d231349c1487299549ce8502e0 Mon Sep 17 00:00:00 2001 From: Julian Murgia Date: Mon, 13 Dec 2021 14:06:43 +0100 Subject: [PATCH] Removed ESCController and greatly reworked ESCActionManager (#480) --- .../core-scripts/esc/esc_action_manager.gd | 353 +++++++++++++++++- .../game/core-scripts/esc_controller.gd | 252 ------------- .../game/core-scripts/esc_tooltip.gd | 4 +- addons/escoria-core/game/escoria.gd | 11 +- addons/escoria-ui-9verbs/game.gd | 139 ++++++- .../tooltip/action_target_tooltip.tscn | 28 +- 6 files changed, 468 insertions(+), 319 deletions(-) delete mode 100644 addons/escoria-core/game/core-scripts/esc_controller.gd 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 index e5be39c8..c608299a 100644 --- a/addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd +++ b/addons/escoria-core/game/core-scripts/esc/esc_action_manager.gd @@ -3,12 +3,37 @@ extends Object class_name ESCActionManager -# The current action was changed +# The current action verb was changed signal action_changed # Emitted, when an action has been completed signal action_finished +# Emitted when the action input state has changed +signal action_input_state_changed + + +# States of the action input (verb, item, target) +# (I) -> AWAITING_VERB_OR_ITEM -> AWAITING_ITEM -> COMPLETED -> (E) +# or +# (I) -> AWAITING_VERB_OR_ITEM -> AWAITING_ITEM -> AWAITING_TARGET_ITEM -> COMPLETED -> (E) +# or +# (I) -> AWAITING_VERB_OR_ITEM -> AWAITING_VERB -> AWAITING_VERB_CONFIRMATION -> COMPLETED -> (E) +enum ACTION_INPUT_STATE { + # Initial state + AWAITING_VERB_OR_ITEM, + # After initial state, verb is defined + AWAITING_ITEM, + # Item defined requires combine, waiting for  target + AWAITING_TARGET_ITEM + # After initial state, item is defined + AWAITING_VERB, + # Item was defined first, next verb, need verb confirmation + AWAITING_VERB_CONFIRMATION, + # Final state + COMPLETED +} + # Current verb used var current_action: String = "" setget set_current_action @@ -16,24 +41,56 @@ var current_action: String = "" setget 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 -# Set the current action +# Current action input state +var action_state = ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM \ + setget set_action_input_state + + +# Sets the current state of action input. +# +# ## Parameters +# - p_state: the action input state to set +func set_action_input_state(p_state): + action_state = p_state + emit_signal("action_input_state_changed") + + +# Set the current action verb +# +# ## Parameters +# - action: The action verb to set func set_current_action(action: String): 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_CONFIRM) + emit_signal("action_changed") # Clear the current action func clear_current_action(): set_current_action("") + set_action_input_state(ACTION_INPUT_STATE.AWAITING_VERB_OR_ITEM) + emit_signal("action_changed") # Clear the current tool func clear_current_tool(): 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) # Activates the action for given params @@ -44,7 +101,7 @@ func clear_current_tool(): # action verbs UI) eg: arrived, use, look, pickup... # - target: Target ESC object # - combine_with: ESC object to combine with -func activate( +func _activate( action: String, target: ESCObject, combine_with: ESCObject = null @@ -90,8 +147,7 @@ func activate( "event_finished" ) if event_returned[0] == ESCExecution.RC_OK: - escoria.action_manager\ - .clear_current_action() + escoria.action_manager.clear_current_action() emit_signal("action_finished") return event_returned[0] elif combine_with.events.has(combine_with_event)\ @@ -111,8 +167,7 @@ func activate( "event_finished" ) if event_returned[0] == ESCExecution.RC_OK: - escoria.action_manager\ - .clear_current_action() + escoria.action_manager.clear_current_action() emit_signal("action_finished") return event_returned[0] else: @@ -151,11 +206,11 @@ func activate( emit_signal("action_finished") return ESCExecution.RC_ERROR else: - # We're missing a target here. - # Tell the Label to add a conjunction and wait for another - # click to add the target to p_param. Until then, return + # We're missing a target here for our tool to be used on current_tool = target - emit_signal("action_finished") + set_action_input_state( + ACTION_INPUT_STATE.AWAITING_TARGET_ITEM + ) return ESCExecution.RC_OK else: escoria.logger.report_warnings( @@ -170,7 +225,6 @@ func activate( ] ) - if target.events.has(action): escoria.event_manager.queue_event(target.events[action]) var event_returned = yield( @@ -183,7 +237,7 @@ func activate( "event_finished" ) if event_returned[0] == ESCExecution.RC_OK: - escoria.action_manager.clear_current_action() + clear_current_action() emit_signal("action_finished") return event_returned[0] else: @@ -198,3 +252,276 @@ func activate( ) emit_signal("action_finished") return ESCExecution.RC_ERROR + + +# 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.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.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]) + + var event_flags = 0 + var has_current_action: bool = false + if obj.events.has(current_action): + event_flags = obj.events[current_action].flags + has_current_action = true + + # 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 current tool was not set, this is our first item, make it tool + if not current_tool or (current_tool and not need_combine): + current_tool = obj + # Else, if we have a tool an combination required, this is our second item, + # make it 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) + + 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 has_current_action or not event_flags & ESCEvent.FLAG_TK): + 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 + + var player_global_pos = escoria.main.current_scene.player.global_position + var clicked_position = event.position + + if not player_global_pos.is_equal_approx(destination_position): + dont_interact = true + + # If no interaction should happen after player has arrived, leave + # immediately. + if dont_interact: + return + + # Manage exits + if obj.node.is_exit and current_action in ["", "walk"]: + _activate("exit_scene", obj) + else: + # Manage movements towards object before activating it + if current_action in ["", "walk"] and \ + not escoria.inventory_manager.inventory_has(obj.global_id): + _activate("arrived", obj) + # Manage action on object + elif not current_action in ["", "walk"]: + if need_combine and current_target: + _activate( + current_action, + current_tool, + current_target + ) + + else: + _activate( + current_action, + obj + ) + + +# Prepare the "obj" object for current_action: if required, set the object as +# current tool. +# +# #### Parameters +# +# - obj: the ESCObject to prepare +# - default_action: if true, the default action set on the item is used +# +# *Returns* True if the tool was set in this function +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). +# +# *Returns* True if current action on "obj" requires a combination +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. +# +# #### 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_controller.gd b/addons/escoria-core/game/core-scripts/esc_controller.gd deleted file mode 100644 index 68ef1ad3..00000000 --- a/addons/escoria-core/game/core-scripts/esc_controller.gd +++ /dev/null @@ -1,252 +0,0 @@ -# This class performs certain tasks as a reaction to certain inputs, such as -# player walking, player walking towards an object, etc. -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.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.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]) - - var event_flags = 0 - var has_current_action: bool = false - if obj.events.has(escoria.action_manager.current_action): - event_flags = obj.events[escoria.action_manager.current_action].flags - has_current_action = true - - # Don't interact after player movement towards object - # (because object is inactive for example) - var dont_interact = false - - 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 has_current_action or not event_flags & ESCEvent.FLAG_TK): - 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 - - var player_global_pos = escoria.main.current_scene.player.global_position - var clicked_position = event.position - - if not player_global_pos.is_equal_approx(destination_position): - dont_interact = true - - # If no interaction should happen after player has arrived, leave - # immediately. - if dont_interact: - return - - # 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_when_selected_action_is_in: - 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_when_selected_action_is_in: - 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_tooltip.gd b/addons/escoria-core/game/core-scripts/esc_tooltip.gd index 063a2e1a..a2db296f 100644 --- a/addons/escoria-core/game/core-scripts/esc_tooltip.gd +++ b/addons/escoria-core/game/core-scripts/esc_tooltip.gd @@ -94,8 +94,7 @@ func set_debug_mode(p_debug_mode: bool): # - needs_second_target: if true, the label will prepare for a second target func set_target(target: String, needs_second_target: bool = false) -> void: current_target = target - if needs_second_target: - waiting_for_target2 = true + waiting_for_target2 = needs_second_target update_tooltip_text() @@ -219,6 +218,7 @@ func tooltip_distance_to_edge_right(position: Vector2): # Clear the tooltip targets texts func clear(): + waiting_for_target2 = false set_target("") set_target2("") diff --git a/addons/escoria-core/game/escoria.gd b/addons/escoria-core/game/escoria.gd index 6da93349..0ffad38b 100644 --- a/addons/escoria-core/game/escoria.gd +++ b/addons/escoria-core/game/escoria.gd @@ -86,10 +86,6 @@ 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 - # The game scene loaded var game_scene: ESCGame @@ -115,7 +111,6 @@ func _init(): self.resource_cache.start() self.save_manager = ESCSaveManager.new() self.inputs_manager = ESCInputsManager.new() - self.controller = ESCController.new() self.room_manager = ESCRoomManager.new() settings = ESCSaveSettings.new() @@ -218,7 +213,7 @@ func do(action: String, params: Array = [], can_interrupt: bool = false) -> void elif params[1] is Vector2: target = params[1] - self.controller.perform_walk(moving_obj, target, walk_fast) + self.action_manager.perform_walk(moving_obj, target, walk_fast) "item_left_click": if params[0] is String: @@ -231,7 +226,7 @@ func do(action: String, params: Array = [], can_interrupt: bool = false) -> void event_manager.interrupt_running_event() var item = self.object_manager.get_object(params[0]) - self.controller.perform_inputevent_on_object(item, params[1]) + self.action_manager.perform_inputevent_on_object(item, params[1]) "item_right_click": if params[0] is String: @@ -244,7 +239,7 @@ func do(action: String, params: Array = [], can_interrupt: bool = false) -> void event_manager.interrupt_running_event() var item = self.object_manager.get_object(params[0]) - self.controller.perform_inputevent_on_object(item, params[1], true) + self.action_manager.perform_inputevent_on_object(item, params[1], true) "trigger_in": var trigger_id = params[0] diff --git a/addons/escoria-ui-9verbs/game.gd b/addons/escoria-ui-9verbs/game.gd index cb12a061..beffaef8 100644 --- a/addons/escoria-ui-9verbs/game.gd +++ b/addons/escoria-ui-9verbs/game.gd @@ -82,6 +82,8 @@ func left_click_on_bg(position: Vector2) -> void: true ) escoria.action_manager.clear_current_action() + escoria.action_manager.clear_current_tool() + tooltip.clear() verbs_menu.unselect_actions() @@ -93,6 +95,8 @@ func right_click_on_bg(position: Vector2) -> void: true ) escoria.action_manager.clear_current_action() + escoria.action_manager.clear_current_tool() + tooltip.clear() verbs_menu.unselect_actions() @@ -111,21 +115,72 @@ func left_double_click_on_bg(position: Vector2) -> void: func element_focused(element_id: String) -> void: var target_obj = escoria.object_manager.get_object(element_id).node - tooltip.set_target(target_obj.tooltip_name) - if escoria.action_manager.current_action != "use" \ - and escoria.action_manager.current_tool == null: - if target_obj is ESCItem: - verbs_menu.set_by_name(target_obj.default_action) + 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 != "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 element_unfocused() -> void: - tooltip.clear() + 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.do("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: @@ -140,7 +195,29 @@ func left_double_click_on_item(item_global_id: String, event: InputEvent) -> voi ## INVENTORY ## func left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void: escoria.do("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) @@ -153,20 +230,45 @@ func left_double_click_on_inventory_item(_inventory_item_global_id: String, _eve func inventory_item_focused(inventory_item_global_id: String) -> void: var target_obj = escoria.object_manager.get_object( - inventory_item_global_id - ).node - tooltip.set_target(target_obj.tooltip_name) - - if escoria.action_manager.current_action != "use" \ - and escoria.action_manager.current_tool == null: - if target_obj is ESCItem: - verbs_menu.set_by_name(target_obj.default_action_inventory) + 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 != "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: - tooltip.set_target("") - verbs_menu.unselect_actions() - + + 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 @@ -237,3 +339,4 @@ func _on_MenuButton_pressed() -> void: func _on_action_finished() -> void: verbs_menu.unselect_actions() + tooltip.clear() diff --git a/addons/escoria-ui-9verbs/tooltip/action_target_tooltip.tscn b/addons/escoria-ui-9verbs/tooltip/action_target_tooltip.tscn index 84573b01..5d81dfbc 100644 --- a/addons/escoria-ui-9verbs/tooltip/action_target_tooltip.tscn +++ b/addons/escoria-ui-9verbs/tooltip/action_target_tooltip.tscn @@ -1,30 +1,6 @@ [gd_scene load_steps=2 format=2] -[sub_resource type="GDScript" id=1] -script/source = "extends ESCTooltip - - -func update_tooltip_text(): - bbcode_text = \"[center]\" - - if !current_action.empty(): - bbcode_text += current_action - bbcode_text += \"\\t\" - - bbcode_text += current_target - - if waiting_for_target2 and current_target2.empty(): - bbcode_text += \"\\t\" - bbcode_text += current_prep - - if !current_target2.empty(): - bbcode_text += \"\\t\" - bbcode_text += current_prep - bbcode_text += \"\\t\" - bbcode_text += current_target2 - - bbcode_text += \"[/center]\" -" +[ext_resource path="res://addons/escoria-ui-9verbs/tooltip/tooltip_action_target.gd" type="Script" id=1] [node name="tooltip" type="RichTextLabel"] anchor_right = 1.0 @@ -32,7 +8,7 @@ anchor_bottom = 1.0 bbcode_enabled = true bbcode_text = "[center][/center]" scroll_active = false -script = SubResource( 1 ) +script = ExtResource( 1 ) __meta__ = { "_edit_use_anchors_": false }