feat: allows for default/fallback actions to be specified
This commit is contained in:
2
action_defaults.esc
Normal file
2
action_defaults.esc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
:use | TK
|
||||||
|
say player "I can't use this!"
|
||||||
@@ -230,27 +230,36 @@ func clear_current_tool():
|
|||||||
set_action_input_state(ACTION_INPUT_STATE.AWAITING_ITEM)
|
set_action_input_state(ACTION_INPUT_STATE.AWAITING_ITEM)
|
||||||
|
|
||||||
|
|
||||||
# Activates the action for given params
|
# 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
|
# #### Parameters
|
||||||
#
|
#
|
||||||
# - action String Action to execute (defined in attached ESC file and in
|
# - action: Action to execute (defined in attached ESC file and in
|
||||||
# action verbs UI) eg: arrived, use, look, pickup...
|
# action verbs UI) eg: arrived, use, look, pickup...
|
||||||
# - target: Target ESC object
|
# - target: Target ESC object
|
||||||
# - combine_with: ESC object to combine with
|
# - combine_with: ESC object to combine with
|
||||||
func _activate(
|
#
|
||||||
|
# *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,
|
action: String,
|
||||||
target: ESCObject,
|
target: ESCObject,
|
||||||
combine_with: ESCObject = null
|
combine_with: ESCObject = null
|
||||||
) -> int:
|
) -> ESCEvent:
|
||||||
|
|
||||||
escoria.logger.info(
|
escoria.logger.info(
|
||||||
self,
|
self,
|
||||||
"Activated action %s on %s." % [action, target]
|
"Checking action %s on %s..." % [action, target]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var event_to_return: ESCEvent = null
|
||||||
|
|
||||||
# If we're using an action which item requires to combine
|
# If we're using an action which item requires to combine
|
||||||
if target.node is ESCItem\
|
if target.node is ESCItem \
|
||||||
and action in target.node.combine_when_selected_action_is_in:
|
and action in target.node.combine_when_selected_action_is_in:
|
||||||
|
|
||||||
# Check if object must be in inventory to be used
|
# Check if object must be in inventory to be used
|
||||||
if target.node.use_from_inventory_only:
|
if target.node.use_from_inventory_only:
|
||||||
if escoria.inventory_manager.inventory_has(target.global_id):
|
if escoria.inventory_manager.inventory_has(target.global_id):
|
||||||
@@ -273,64 +282,39 @@ func _activate(
|
|||||||
action,
|
action,
|
||||||
target.global_id
|
target.global_id
|
||||||
]
|
]
|
||||||
if target.events.has(target_event):
|
|
||||||
escoria.event_manager.queue_event(target.events[
|
|
||||||
target_event
|
|
||||||
])
|
|
||||||
var event_returned = yield(
|
|
||||||
escoria.event_manager,
|
|
||||||
"event_finished"
|
|
||||||
)
|
|
||||||
while event_returned[1] != target_event:
|
|
||||||
event_returned = yield(
|
|
||||||
escoria.event_manager,
|
|
||||||
"event_finished"
|
|
||||||
)
|
|
||||||
|
|
||||||
clear_current_action()
|
if target.events.has(target_event):
|
||||||
emit_signal("action_finished")
|
event_to_return = target.events[target_event]
|
||||||
return event_returned[0]
|
|
||||||
elif combine_with.events.has(combine_with_event)\
|
elif combine_with.events.has(combine_with_event)\
|
||||||
and not combine_with.node.combine_is_one_way:
|
and not combine_with.node.combine_is_one_way:
|
||||||
escoria.event_manager.queue_event(
|
|
||||||
combine_with.events[
|
|
||||||
combine_with_event
|
|
||||||
]
|
|
||||||
)
|
|
||||||
var event_returned = yield(
|
|
||||||
escoria.event_manager,
|
|
||||||
"event_finished"
|
|
||||||
)
|
|
||||||
while event_returned[1] != combine_with_event:
|
|
||||||
event_returned = yield(
|
|
||||||
escoria.event_manager,
|
|
||||||
"event_finished"
|
|
||||||
)
|
|
||||||
|
|
||||||
clear_current_action()
|
event_to_return = combine_with.events[combine_with_event]
|
||||||
emit_signal("action_finished")
|
|
||||||
return event_returned[0]
|
|
||||||
else:
|
else:
|
||||||
var errors = [
|
# Check to see if there isn't a "fallback" action to
|
||||||
"Attempted to execute action %s between item %s and item %s" % [
|
# run before we declare this a failure.
|
||||||
action,
|
if escoria.action_default_script \
|
||||||
target.global_id,
|
and escoria.action_default_script.events.has(action):
|
||||||
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)
|
|
||||||
)
|
|
||||||
|
|
||||||
clear_current_action()
|
event_to_return = escoria.action_default_script.events[action]
|
||||||
emit_signal("action_finished")
|
else:
|
||||||
return ESCExecution.RC_ERROR
|
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:
|
else:
|
||||||
escoria.logger.warn(
|
escoria.logger.warn(
|
||||||
self,
|
self,
|
||||||
@@ -344,17 +328,6 @@ func _activate(
|
|||||||
combine_with.global_id
|
combine_with.global_id
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
clear_current_action()
|
|
||||||
emit_signal("action_finished")
|
|
||||||
return ESCExecution.RC_ERROR
|
|
||||||
else:
|
|
||||||
# We're missing a target here for our tool to be used on
|
|
||||||
current_tool = target
|
|
||||||
set_action_input_state(
|
|
||||||
ACTION_INPUT_STATE.AWAITING_TARGET_ITEM
|
|
||||||
)
|
|
||||||
return ESCExecution.RC_OK
|
|
||||||
else:
|
else:
|
||||||
escoria.logger.warn(
|
escoria.logger.warn(
|
||||||
self,
|
self,
|
||||||
@@ -366,33 +339,45 @@ func _activate(
|
|||||||
]
|
]
|
||||||
+ "but item must be in inventory."
|
+ "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 target.events.has(action):
|
# If there's a "fallback" action to run, return it
|
||||||
escoria.event_manager.queue_event(target.events[action])
|
event_to_return = escoria.action_default_script.events[action]
|
||||||
var event_returned = yield(
|
else:
|
||||||
|
escoria.logger.warn(
|
||||||
|
self,
|
||||||
|
"Invalid action: " +
|
||||||
|
"Event for action %s on object %s not found." % [
|
||||||
|
action,
|
||||||
|
target.global_id
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
return event_to_return
|
||||||
|
|
||||||
|
|
||||||
|
func _run_event(event: ESCEvent) -> int:
|
||||||
|
escoria.event_manager.queue_event(event)
|
||||||
|
|
||||||
|
var event_returned = yield(
|
||||||
|
escoria.event_manager,
|
||||||
|
"event_finished"
|
||||||
|
)
|
||||||
|
|
||||||
|
while event_returned[1] != event.name:
|
||||||
|
event_returned = yield(
|
||||||
escoria.event_manager,
|
escoria.event_manager,
|
||||||
"event_finished"
|
"event_finished"
|
||||||
)
|
)
|
||||||
while event_returned[1] != action:
|
|
||||||
event_returned = yield(
|
clear_current_action()
|
||||||
escoria.event_manager,
|
emit_signal("action_finished")
|
||||||
"event_finished"
|
|
||||||
)
|
return event_returned[0]
|
||||||
clear_current_action()
|
|
||||||
emit_signal("action_finished")
|
|
||||||
return event_returned[0]
|
|
||||||
else:
|
|
||||||
escoria.logger.warn(
|
|
||||||
self,
|
|
||||||
"Invalid action: " +
|
|
||||||
"Event for action %s on object %s not found." % [
|
|
||||||
action,
|
|
||||||
target.global_id
|
|
||||||
]
|
|
||||||
)
|
|
||||||
clear_current_action()
|
|
||||||
emit_signal("action_finished")
|
|
||||||
return ESCExecution.RC_ERROR
|
|
||||||
|
|
||||||
|
|
||||||
# Makes an object walk to a destination. This can be either a 2D position or
|
# Makes an object walk to a destination. This can be either a 2D position or
|
||||||
@@ -460,6 +445,8 @@ func perform_inputevent_on_object(
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
This algorithm:
|
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
|
- makes the player move to the clicked object location, if needed
|
||||||
(if it is located in the room for example) and wait for reaching.
|
(if it is located in the room for example) and wait for reaching.
|
||||||
- when reached, performs an action depending on current defined action
|
- when reached, performs an action depending on current defined action
|
||||||
@@ -485,12 +472,6 @@ func perform_inputevent_on_object(
|
|||||||
"%s to perform event %s." % [obj.global_id, event]
|
"%s to perform event %s." % [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
|
# Don't interact after player movement towards object
|
||||||
# (because object is inactive for example)
|
# (because object is inactive for example)
|
||||||
var dont_interact = false
|
var dont_interact = false
|
||||||
@@ -501,11 +482,11 @@ func perform_inputevent_on_object(
|
|||||||
var tool_just_set = _set_tool_and_action(obj, default_action)
|
var tool_just_set = _set_tool_and_action(obj, default_action)
|
||||||
var need_combine = _check_item_needs_combine()
|
var need_combine = _check_item_needs_combine()
|
||||||
|
|
||||||
# If the current tool was not set, this is our first item, make it tool
|
# 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):
|
if not current_tool or (current_tool and not need_combine):
|
||||||
current_tool = obj
|
current_tool = obj
|
||||||
# Else, if we have a tool an combination required, this is our second item,
|
# Else, if we have a tool and combination required, this is our second item,
|
||||||
# make it target.
|
# make it the target.
|
||||||
elif need_combine and not tool_just_set:
|
elif need_combine and not tool_just_set:
|
||||||
current_target = obj
|
current_target = obj
|
||||||
|
|
||||||
@@ -518,19 +499,68 @@ func perform_inputevent_on_object(
|
|||||||
elif action_state == ACTION_INPUT_STATE.AWAITING_ITEM and need_combine and not tool_just_set:
|
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)
|
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):
|
||||||
|
|
||||||
|
# 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:
|
if escoria.main.current_scene.player:
|
||||||
var destination_position: Vector2 = escoria.main.current_scene.player.\
|
var destination_position: Vector2 = escoria.main.current_scene.player \
|
||||||
global_position
|
.global_position
|
||||||
|
|
||||||
# If clicked object not in inventory, player walks towards it
|
# If clicked object not in inventory, player walks towards it
|
||||||
if not obj.node is ESCPlayer and \
|
if not obj.node is ESCPlayer and \
|
||||||
not escoria.inventory_manager.inventory_has(obj.global_id) and \
|
not escoria.inventory_manager.inventory_has(obj.global_id) and \
|
||||||
(not has_current_action or not event_flags & ESCEvent.FLAG_TK):
|
not event_flags & ESCEvent.FLAG_TK:
|
||||||
var context = _walk_towards_object(
|
var context = _walk_towards_object(
|
||||||
obj,
|
obj,
|
||||||
event.position,
|
event.position,
|
||||||
event.doubleclick
|
event.doubleclick
|
||||||
)
|
)
|
||||||
|
|
||||||
if context is GDScriptFunctionState:
|
if context is GDScriptFunctionState:
|
||||||
context = yield(context, "completed")
|
context = yield(context, "completed")
|
||||||
|
|
||||||
@@ -562,36 +592,10 @@ func perform_inputevent_on_object(
|
|||||||
[escoria.event_manager.EVENT_CANT_REACH, obj.global_id]
|
[escoria.event_manager.EVENT_CANT_REACH, obj.global_id]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# If no interaction should happen after player has arrived, leave
|
# If no interaction should happen after player has arrived, leave
|
||||||
# immediately.
|
# immediately.
|
||||||
if dont_interact:
|
if not dont_interact and event_to_queue:
|
||||||
return
|
_run_event(event_to_queue)
|
||||||
|
|
||||||
# Manage exits
|
|
||||||
if obj.node.is_exit and current_action in ["", ACTION_WALK]:
|
|
||||||
escoria.event_manager.interrupt()
|
|
||||||
escoria.event_manager.clear_event_queue()
|
|
||||||
_activate(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):
|
|
||||||
_activate(ACTION_ARRIVED, obj)
|
|
||||||
# Manage action on object
|
|
||||||
elif not current_action in ["", ACTION_WALK]:
|
|
||||||
if need_combine and current_target:
|
|
||||||
_activate(
|
|
||||||
current_action,
|
|
||||||
current_tool,
|
|
||||||
current_target
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
_activate(
|
|
||||||
current_action,
|
|
||||||
obj
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Determines whether the object in question can be acted upon.
|
# Determines whether the object in question can be acted upon.
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ var player_camera: ESCCamera
|
|||||||
# escoria/main/game_start_script
|
# escoria/main/game_start_script
|
||||||
var start_script: ESCScript
|
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
|
# Whether we ran a room directly from editor, not a full game
|
||||||
var is_direct_room_run: bool = false
|
var is_direct_room_run: bool = false
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const FORCE_QUIT = "%s/%s/force_quit" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
|||||||
const GAME_MIGRATION_PATH = "%s/%s/game_migration_path" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
const GAME_MIGRATION_PATH = "%s/%s/game_migration_path" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
||||||
const GAME_VERSION = "%s/%s/game_version" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
const GAME_VERSION = "%s/%s/game_version" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
||||||
const GAME_START_SCRIPT = "%s/%s/game_start_script" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
const GAME_START_SCRIPT = "%s/%s/game_start_script" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
||||||
|
const ACTION_DEFAULT_SCRIPT = "%s/%s/action_default_script" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
||||||
const SAVEGAMES_PATH = "%s/%s/savegames_path" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
const SAVEGAMES_PATH = "%s/%s/savegames_path" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
||||||
const SETTINGS_PATH = "%s/%s/settings_path" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
const SETTINGS_PATH = "%s/%s/settings_path" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
||||||
const TEXT_LANG = "%s/%s/text_lang" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
const TEXT_LANG = "%s/%s/text_lang" % [_ESCORIA_SETTINGS_ROOT, _MAIN_ROOT]
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ func _ready():
|
|||||||
|
|
||||||
escoria.room_manager.register_reserved_globals()
|
escoria.room_manager.register_reserved_globals()
|
||||||
escoria.inputs_manager.register_core()
|
escoria.inputs_manager.register_core()
|
||||||
|
|
||||||
if ESCProjectSettingsManager.get_setting(
|
if ESCProjectSettingsManager.get_setting(
|
||||||
ESCProjectSettingsManager.GAME_START_SCRIPT
|
ESCProjectSettingsManager.GAME_START_SCRIPT
|
||||||
).empty():
|
).empty():
|
||||||
@@ -66,6 +67,21 @@ func _ready():
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ESCProjectSettingsManager.get_setting(
|
||||||
|
ESCProjectSettingsManager.ACTION_DEFAULT_SCRIPT
|
||||||
|
).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
|
escoria.main = main
|
||||||
|
|
||||||
_perform_plugins_checks()
|
_perform_plugins_checks()
|
||||||
|
|||||||
@@ -156,6 +156,16 @@ func set_escoria_main_settings():
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
register_setting(
|
||||||
|
ESCProjectSettingsManager.ACTION_DEFAULT_SCRIPT,
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
"type": TYPE_STRING,
|
||||||
|
"hint": PROPERTY_HINT_FILE,
|
||||||
|
"hint_string": "*.esc"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
register_setting(
|
register_setting(
|
||||||
ESCProjectSettingsManager.FORCE_QUIT,
|
ESCProjectSettingsManager.FORCE_QUIT,
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -792,6 +792,7 @@ dialog_simple/avatars_path="res://game/dialog_avatars/"
|
|||||||
dialog_simple/text_speed_per_character=0.1
|
dialog_simple/text_speed_per_character=0.1
|
||||||
dialog_simple/fast_text_speed_per_character=0.25
|
dialog_simple/fast_text_speed_per_character=0.25
|
||||||
dialog_simple/max_time_to_disappear=1.0
|
dialog_simple/max_time_to_disappear=1.0
|
||||||
|
main/action_default_script="res://action_defaults.esc"
|
||||||
|
|
||||||
[input]
|
[input]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user