@@ -0,0 +1,364 @@
# Manages currently carried out actions
# MODIFIED FOR RETURN TO MONKEY UI
extends ESCActionManager
class_name ESCActionManagerMonkey
# Set the current action verb
#
# ## Parameters
# - action: The action verb to set
func set_current_action ( action : String ) :
# MODIFIED FOR RETURN TO MONKEY UI
if ( action != current_action ) && ( action_state != ACTION_INPUT_STATE . AWAITING_TARGET_ITEM ) :
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 " )
# 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.
#
# #### Parameters
#
# - action: Action to execute (defined in attached ESC file and in
# action verbs UI) eg: arrived, use, look, pickup...
# - target: Target ESC object
# - combine_with: ESC object to combine with
#
# *Returns* the appropriate ESCEvent to queue/run, or null if none can be found
# or there's a reason not to run an event.
func _get_event_to_queue (
action : String ,
target : ESCObject ,
combine_with : ESCObject = null
) - > ESCEvent :
escoria . logger . info (
self ,
" Checking if action ' %s ' on ' %s ' is valid... " % [ action , target ]
)
var event_to_return : ESCEvent = 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
# MODIFIED FOR RETURN TO MONKEY UI
or ( combine_with && action in combine_with . node . combine_when_selected_action_is_in ) ) :
# Check if object must be in inventory to be used
if target . node . use_from_inventory_only :
if escoria . inventory_manager . inventory_has ( target . global_id ) :
# Player has item in inventory, 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 target_event = " %s %s " % [
action ,
combine_with . global_id
]
var combine_with_event = " %s %s " % [
action ,
target . global_id
]
if target . events . has ( target_event ) :
event_to_return = target . events [ target_event ]
elif combine_with . events . has ( combine_with_event ) \
and not combine_with . node . combine_is_one_way :
event_to_return = combine_with . events [ combine_with_event ]
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
]
]
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 target . events . has ( action ) :
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
# 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:
- 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 )
# MODIFIED FOR RETURN TO MONKEY UI
var need_combine = _check_item_needs_combine_obj ( obj )
# If the current tool was not set, this is our first item, make it the tool
if 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 : ESCEvent = null
# Manage exits
if obj . node . is_exit and current_action in [ " " , ACTION_WALK ] :
event_to_queue = _get_event_to_queue ( ACTION_EXIT_SCENE , obj )
else :
# Manage movements towards object before activating it
if 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 )
# Manage action on object
elif not current_action in [ " " , ACTION_WALK ] :
if need_combine and current_target :
event_to_queue = _get_event_to_queue (
current_action ,
current_tool ,
current_target
)
else :
# Check if object must be in inventory to be used and update
# action state if necessary
if obj . node . use_from_inventory_only and \
escoria . inventory_manager . inventory_has ( obj . global_id ) and \
need_combine :
# We're missing a target here for our tool to be used on
current_tool = obj
set_action_input_state (
ACTION_INPUT_STATE . AWAITING_TARGET_ITEM
)
# We need to wait for that target
return
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 ( )
emit_signal ( " action_finished " )
return
var event_flags = event_to_queue . 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 event_flags & ESCEvent . FLAG_TK :
var context = _walk_towards_object (
obj ,
event . position ,
event . doubleclick
)
if context is GDScriptFunctionState :
context = yield ( context , " completed " )
# 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 :
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 )
# 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 :
# MODIFIED FOR RETURN TO MONKEY UI
if ( not current_action in escoria . action_manager \
. current_tool . node . combine_when_selected_action_is_in and not current_action in obj . 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
# MODIFIED FOR RETURN TO MONKEY UI
func _check_item_needs_combine_obj ( obj : ESCObject ) - > bool :
return current_action \
and current_tool \
and ( current_action in current_tool . node . combine_when_selected_action_is_in
# MODIFIED FOR RETURN TO MONKEY UI
or current_action in obj . node . combine_when_selected_action_is_in )