Files
MovementTests/addons/guide/triggers/guide_trigger_pulse.gd
2025-05-27 19:20:46 +02:00

87 lines
3.0 KiB
GDScript

@tool
## A trigger that activates when the input is pushed down and then repeatedly sends trigger events at a fixed interval.
## Note: the trigger will be either triggering or ongoing until the input is released.
## Note: at most one pulse will be emitted per frame.
class_name GUIDETriggerPulse
extends GUIDETrigger
## If true, the trigger will trigger immediately when the input is actuated. Otherwise, the trigger will wait for the initial delay.
@export var trigger_on_start:bool = true
## The delay after the initial actuation before pulsing begins.
@export var initial_delay:float = 0.3:
set(value):
initial_delay = max(0, value)
## The interval between pulses. Set to 0 to pulse every frame.
@export var pulse_interval:float = 0.1:
set(value):
pulse_interval = max(0, value)
## Maximum number of pulses. If <= 0, the trigger will pulse indefinitely.
@export var max_pulses:int = 0
var _delay_until_next_pulse:float = 0
var _emitted_pulses:int = 0
func _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
if _is_actuated(input, value_type):
if not _is_actuated(_last_value, value_type):
# we went from "not actuated" to actuated, pulsing starts
_delay_until_next_pulse = initial_delay
if trigger_on_start:
return GUIDETriggerState.TRIGGERED
else:
return GUIDETriggerState.ONGOING
# if we already are pulsing and have exceeded the maximum number of pulses, we will not pulse anymore.
if max_pulses > 0 and _emitted_pulses >= max_pulses:
return GUIDETriggerState.NONE
# subtract the delta from the delay until the next pulse
_delay_until_next_pulse -= delta
if _delay_until_next_pulse > 0:
# we are still waiting for the next pulse, nothing to do.
return GUIDETriggerState.ONGOING
# now delta could be larger than our pulse, in which case we loose a few pulses.
# as we can pulse at most once per frame.
# in case someone sets the pulse interval to 0, we will pulse every frame.
if is_equal_approx(pulse_interval, 0):
_delay_until_next_pulse = 0
if max_pulses > 0:
_emitted_pulses += 1
return GUIDETriggerState.TRIGGERED
# Now add the delay until the next pulse
_delay_until_next_pulse += pulse_interval
# If the interval is really small, we can potentially have skipped some pulses
if _delay_until_next_pulse <= 0:
# we have skipped some pulses
var skipped_pulses:int = int(-_delay_until_next_pulse / pulse_interval)
_delay_until_next_pulse += skipped_pulses * pulse_interval
if max_pulses > 0:
_emitted_pulses += skipped_pulses
if _emitted_pulses >= max_pulses:
return GUIDETriggerState.NONE
# Record a pulse and return triggered
if max_pulses > 0:
_emitted_pulses += 1
return GUIDETriggerState.TRIGGERED
# if the input is not actuated, then the trigger is not triggered.
_emitted_pulses = 0
_delay_until_next_pulse = 0
return GUIDETriggerState.NONE
func _editor_name() -> String:
return "Pulse"
func _editor_description() -> String:
return "Fires at an interval while the input is actuated."