@icon("res://addons/guide/guide_internal.svg") @tool ## A mapping from actuated input to a trigger result class_name GUIDEInputMapping extends Resource ## Whether the remapping configuration in this input mapping ## should override the configuration of the bound action. Enable ## this, to give a key a custom name or category for remapping. @export var override_action_settings:bool = false: set(value): if override_action_settings == value: return override_action_settings = value emit_changed() ## If true, players can remap this input mapping. Note that the ## action to which this input is bound also needs to be remappable ## for this setting to have an effect. @export var is_remappable:bool = false: set(value): if is_remappable == value: return is_remappable = value emit_changed() ## The display name of the input mapping shown to the player. If empty, ## the display name of the action is used. @export var display_name:String = "": set(value): if display_name == value: return display_name = value emit_changed() ## The display category of the input mapping. If empty, the display name of the ## action is used. @export var display_category:String = "": set(value): if display_category == value: return display_category = value emit_changed() @export_group("Mappings") ## The input to be actuated @export var input:GUIDEInput: set(value): if value == input: return input = value emit_changed() ## A list of modifiers that preprocess the actuated input before ## it is fed to the triggers. @export var modifiers:Array[GUIDEModifier] = []: set(value): if value == modifiers: return modifiers = value emit_changed() ## A list of triggers that could trigger the mapped action. @export var triggers:Array[GUIDETrigger] = []: set(value): if value == triggers: return triggers = value emit_changed() ## Hint for how long the input must remain actuated (in seconds) before the mapping triggers. ## If the mapping has no hold trigger it will be -1. If it has multiple hold triggers ## the shortest hold time will be used. var _trigger_hold_threshold:float = -1.0 var _state:GUIDETrigger.GUIDETriggerState = GUIDETrigger.GUIDETriggerState.NONE var _value:Vector3 = Vector3.ZERO var _trigger_list:Array[GUIDETrigger] = [] var _implicit_count:int = 0 var _explicit_count:int = 0 ## Called when the mapping is started to be used by GUIDE. Calculates ## the number of implicit and explicit triggers so we don't need to do this ## per frame. Also creates a default trigger when none is set. func _initialize() -> void : _trigger_list.clear() _implicit_count = 0 _explicit_count = 0 _trigger_hold_threshold = -1.0 if triggers.is_empty(): # make a default trigger and use that var default_trigger = GUIDETriggerDown.new() default_trigger.actuation_threshold = 0 _explicit_count = 1 _trigger_list.append(default_trigger) return for trigger in triggers: match trigger._get_trigger_type(): GUIDETrigger.GUIDETriggerType.EXPLICIT: _explicit_count += 1 GUIDETrigger.GUIDETriggerType.IMPLICIT: _implicit_count += 1 _trigger_list.append(trigger) # collect the hold threshold for hinting the UI about how long # the input must be held down. This is only relevant for hold triggers if trigger is GUIDETriggerHold: if _trigger_hold_threshold == -1: _trigger_hold_threshold = trigger.hold_treshold else: _trigger_hold_threshold = min(_trigger_hold_threshold, trigger.hold_treshold) func _update_state(delta:float, value_type:GUIDEAction.GUIDEActionValueType): # Collect the current input value var input_value:Vector3 = input._value if input != null else Vector3.ZERO # Run it through all modifiers for modifier:GUIDEModifier in modifiers: input_value = modifier._modify_input(input_value, delta, value_type) _value = input_value var triggered_implicits:int = 0 var triggered_explicits:int = 0 var triggered_blocked:int = 0 # Run over all triggers var result:int = GUIDETrigger.GUIDETriggerState.NONE for trigger:GUIDETrigger in _trigger_list: var trigger_result:GUIDETrigger.GUIDETriggerState = trigger._update_state(_value, delta, value_type) trigger._last_value = _value var trigger_type = trigger._get_trigger_type() if trigger_result == GUIDETrigger.GUIDETriggerState.TRIGGERED: match trigger_type: GUIDETrigger.GUIDETriggerType.EXPLICIT: triggered_explicits += 1 GUIDETrigger.GUIDETriggerType.IMPLICIT: triggered_implicits += 1 GUIDETrigger.GUIDETriggerType.BLOCKING: triggered_blocked += 1 # we only care about the nuances of explicit triggers. implicits and blocking # can only really return yes or no, so they have no nuance if trigger_type == GUIDETrigger.GUIDETriggerType.EXPLICIT: # Higher value results take precedence over lower value results result = max(result, trigger_result) # final collection if triggered_blocked > 0: # some blocker triggered which means that this cannot succeed _state = GUIDETrigger.GUIDETriggerState.NONE return if triggered_implicits < _implicit_count: # not all implicits triggered, which also fails this binding _state = GUIDETrigger.GUIDETriggerState.NONE return if _explicit_count == 0 and _implicit_count > 0: # if no explicits exist, its enough when all implicits trigger _state = GUIDETrigger.GUIDETriggerState.TRIGGERED return # return the best result _state = result