feat: made sure the aspect ration fit a pixel art game and added useful addons
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 6s
Create tag and build when new code gets to main / Export (push) Successful in 3m16s

This commit is contained in:
2025-06-27 15:19:12 +02:00
parent 4ec91c1277
commit 9a79715e47
550 changed files with 18812 additions and 0 deletions

View File

@ -0,0 +1,65 @@
@tool
@icon("res://addons/guide/triggers/guide_trigger.svg")
class_name GUIDETrigger
extends Resource
enum GUIDETriggerState {
## The trigger did not fire.
NONE,
## The trigger's conditions are partially met
ONGOING,
## The trigger has fired.
TRIGGERED
}
enum GUIDETriggerType {
# If there are more than one explicit triggers at least one must trigger
# for the action to trigger.
EXPLICIT = 1,
# All implicit triggers must trigger for the action to trigger.
IMPLICIT = 2,
# All blocking triggers prevent the action from triggering.
BLOCKING = 3
}
@export var actuation_threshold:float = 0.5
var _last_value:Vector3
## Returns the trigger type of this trigger.
func _get_trigger_type() -> GUIDETriggerType:
return GUIDETriggerType.EXPLICIT
func _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
return GUIDETriggerState.NONE
func _is_actuated(input:Vector3, value_type:GUIDEAction.GUIDEActionValueType) -> bool:
match value_type:
GUIDEAction.GUIDEActionValueType.AXIS_1D, GUIDEAction.GUIDEActionValueType.BOOL:
return _is_axis1d_actuated(input)
GUIDEAction.GUIDEActionValueType.AXIS_2D:
return _is_axis2d_actuated(input)
GUIDEAction.GUIDEActionValueType.AXIS_3D:
return _is_axis3d_actuated(input)
return false
## Checks if a 1D input is actuated.
func _is_axis1d_actuated(input:Vector3) -> bool:
return is_finite(input.x) and abs(input.x) > actuation_threshold
## Checks if a 2D input is actuated.
func _is_axis2d_actuated(input:Vector3) -> bool:
return is_finite(input.x) and is_finite(input.y) and Vector2(input.x, input.y).length_squared() > actuation_threshold * actuation_threshold
## Checks if a 3D input is actuated.
func _is_axis3d_actuated(input:Vector3) -> bool:
return input.is_finite() and input.length_squared() > actuation_threshold * actuation_threshold
func _editor_name() -> String:
return "GUIDETrigger"
func _editor_description() -> String:
return ""

View File

@ -0,0 +1 @@
uid://x74mnwgr08a7

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1.07241,0,0,1.07396,-3.11767,-2.34767)">
<path d="M17.827,2.164C26.061,2.164 32.747,8.85 32.747,17.084C32.747,25.319 26.061,32.004 17.827,32.004C9.592,32.004 2.907,25.319 2.907,17.084C2.907,8.85 9.592,2.164 17.827,2.164ZM17.827,4.857C11.08,4.857 5.604,10.337 5.604,17.084C5.604,23.831 11.08,29.311 17.827,29.311C24.574,29.311 30.05,23.831 30.05,17.084C30.05,10.337 24.574,4.857 17.827,4.857Z" style="fill:rgb(253,150,0);"/>
</g>
<g transform="matrix(1,0,0,1,-2.69665,-2.69876)">
<g transform="matrix(24,0,0,24,11.6286,27.2968)">
<path d="M0.575,-0.717L0.575,-0.635L0.344,-0.635L0.344,-0L0.247,-0L0.247,-0.635L0.014,-0.635L0.014,-0.717L0.575,-0.717Z" style="fill:rgb(253,150,0);fill-rule:nonzero;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,38 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ca1eiagyinhl7"
path="res://.godot/imported/guide_trigger.svg-cd87acbd491929cf49a255f8481b0b63.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/guide/triggers/guide_trigger.svg"
dest_files=["res://.godot/imported/guide_trigger.svg-cd87acbd491929cf49a255f8481b0b63.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=0.5
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,28 @@
## Fires, when the given action is currently triggering. This trigger is implicit,
## so it will prevent the action from triggering even if other triggers are successful.
@tool
class_name GUIDETriggerChordedAction
extends GUIDETrigger
@export var action:GUIDEAction
func _get_trigger_type() -> GUIDETriggerType:
return GUIDETriggerType.IMPLICIT
func _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
if action == null:
push_warning("Chorded trigger without action will never trigger.")
return GUIDETriggerState.NONE
if action.is_triggered():
return GUIDETriggerState.TRIGGERED
return GUIDETriggerState.NONE
func _editor_name() -> String:
return "Chorded Action"
func _editor_description() -> String:
return "Fires, when the given action is currently triggering. This trigger is implicit,\n" + \
"so it will prevent the action from triggering even if other triggers are successful."

View File

@ -0,0 +1 @@
uid://brsxcrai2te83

View File

@ -0,0 +1,117 @@
@tool
class_name GUIDETriggerCombo
extends GUIDETrigger
enum ActionEventType {
TRIGGERED = 1,
STARTED = 2,
ONGOING = 4,
CANCELLED = 8,
COMPLETED = 16
}
## If set to true, the combo trigger will print information
## about state changes to the debug log.
@export var enable_debug_print:bool = false
@export var steps:Array[GUIDETriggerComboStep] = []
@export var cancellation_actions:Array[GUIDETriggerComboCancelAction] = []
var _current_step:int = -1
var _remaining_time:float = 0
func _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
if steps.is_empty():
push_warning("Combo with no steps will never fire.")
return GUIDETriggerState.NONE
# initial setup
if _current_step == -1:
for step in steps:
step._prepare()
for action in cancellation_actions:
action._prepare()
_reset()
var current_action := steps[_current_step].action
if current_action == null:
push_warning("Step ", _current_step , " has no action ", resource_path)
return GUIDETriggerState.NONE
# check if any of our cancellation actions fired
for action in cancellation_actions:
# if the action is the current action we don't count its firing as cancellation
if action.action == current_action:
continue
if action._has_fired:
if enable_debug_print:
print("Combo cancelled by action '", action.action._editor_name(), "'.")
_reset()
return GUIDETriggerState.NONE
# check if any of the steps has fired out of order
for step in steps:
if step.action == current_action:
continue
if step._has_fired:
if enable_debug_print:
print("Combo out of order step by action '", step.action._editor_name(), "'.")
_reset()
return GUIDETriggerState.NONE
# check if we took too long (unless we're in the first step)
if _current_step > 0:
_remaining_time -= delta
if _remaining_time <= 0.0:
if enable_debug_print:
print("Step time for step ", _current_step , " exceeded.")
_reset()
return GUIDETriggerState.NONE
# if the current action was fired, if so advance to the next
if steps[_current_step]._has_fired:
# reset this step, so it will not count as misfired next round
steps[_current_step]._has_fired = false
if _current_step + 1 >= steps.size():
# we finished the combo
if enable_debug_print:
print("Combo fired.")
_reset()
return GUIDETriggerState.TRIGGERED
# otherwise, pick the next step
_current_step += 1
if enable_debug_print:
print("Combo advanced to step " , _current_step, ".")
_remaining_time = steps[_current_step].time_to_actuate
# Reset all steps and cancellation actions to "not fired" in
# case they were triggered by this action. Otherwise a double-tap
# would immediately fire for both taps once the first is through
for step in steps:
step._has_fired = false
for action in cancellation_actions:
action._has_fired = false
# and in any case we're still processing.
return GUIDETriggerState.ONGOING
func _reset():
if enable_debug_print:
print("Combo reset.")
_current_step = 0
_remaining_time = steps[0].time_to_actuate
for step in steps:
step._has_fired = false
for action in cancellation_actions:
action._has_fired = false
func _editor_name() -> String:
return "Combo"
func _editor_description() -> String:
return "Fires, when the input exceeds the actuation threshold."

View File

@ -0,0 +1 @@
uid://biioody2ca0e7

View File

@ -0,0 +1,27 @@
@icon("res://addons/guide/guide_internal.svg")
class_name GUIDETriggerComboCancelAction
extends Resource
@export var action:GUIDEAction
@export_flags("Triggered:1", "Started:2", "Ongoing:4", "Cancelled:8","Completed:16")
var completion_events:int = GUIDETriggerCombo.ActionEventType.TRIGGERED
var _has_fired:bool = false
func _prepare():
if completion_events & GUIDETriggerCombo.ActionEventType.TRIGGERED:
action.triggered.connect(_fired)
if completion_events & GUIDETriggerCombo.ActionEventType.STARTED:
action.started.connect(_fired)
if completion_events & GUIDETriggerCombo.ActionEventType.ONGOING:
action.ongoing.connect(_fired)
if completion_events & GUIDETriggerCombo.ActionEventType.CANCELLED:
action.cancelled.connect(_fired)
if completion_events & GUIDETriggerCombo.ActionEventType.COMPLETED:
action.completed.connect(_fired)
_has_fired = false
func _fired():
_has_fired = true

View File

@ -0,0 +1 @@
uid://ddgp5tashyo8o

View File

@ -0,0 +1,29 @@
@icon("res://addons/guide/guide_internal.svg")
class_name GUIDETriggerComboStep
extends Resource
@export var action:GUIDEAction
@export_flags("Triggered:1", "Started:2", "Ongoing:4", "Cancelled:8","Completed:16")
var completion_events:int = GUIDETriggerCombo.ActionEventType.TRIGGERED
@export var time_to_actuate:float = 0.5
var _has_fired:bool = false
func _prepare():
if completion_events & GUIDETriggerCombo.ActionEventType.TRIGGERED:
action.triggered.connect(_fired)
if completion_events & GUIDETriggerCombo.ActionEventType.STARTED:
action.started.connect(_fired)
if completion_events & GUIDETriggerCombo.ActionEventType.ONGOING:
action.ongoing.connect(_fired)
if completion_events & GUIDETriggerCombo.ActionEventType.CANCELLED:
action.cancelled.connect(_fired)
if completion_events & GUIDETriggerCombo.ActionEventType.COMPLETED:
action.completed.connect(_fired)
_has_fired = false
func _fired():
_has_fired = true

View File

@ -0,0 +1 @@
uid://be47edmg8hwpo

View File

@ -0,0 +1,20 @@
## Fires, when the input exceeds the actuation threshold. This is
## the default trigger when no trigger is specified.
@tool
class_name GUIDETriggerDown
extends GUIDETrigger
func _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
# if the input is actuated, then the trigger is triggered.
if _is_actuated(input, value_type):
return GUIDETriggerState.TRIGGERED
# otherwise, the trigger is not triggered.
return GUIDETriggerState.NONE
func _editor_name() -> String:
return "Down"
func _editor_description() -> String:
return "Fires, when the input exceeds the actuation threshold. This is\n" +\
"the default trigger when no trigger is specified."

View File

@ -0,0 +1 @@
uid://b4cdrn4paoj3i

View File

@ -0,0 +1,43 @@
@tool
## A trigger that activates when the input is held down for a certain amount of time.
class_name GUIDETriggerHold
extends GUIDETrigger
## The time for how long the input must be held.
@export var hold_treshold:float = 1.0
## If true, the trigger will only fire once until the input is released. Otherwise the trigger will fire every frame.
@export var is_one_shot:bool = false
var _accumulated_time:float = 0
var _did_shoot:bool = false
func _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
# if the input is actuated, accumulate time and check if the hold threshold has been reached
if _is_actuated(input, value_type):
_accumulated_time += delta
if _accumulated_time >= hold_treshold:
# if the trigger is one shot and we already shot, then we will not trigger again.
if is_one_shot and _did_shoot:
return GUIDETriggerState.NONE
else:
# otherwise, we will just trigger.
_did_shoot = true
return GUIDETriggerState.TRIGGERED
else:
# if the hold threshold has not been reached, then the trigger is ongoing.
return GUIDETriggerState.ONGOING
else:
# if the input is not actuated, then the trigger is not triggered and we reset the accumulated time.
# and our one shot flag.
_accumulated_time = 0
_did_shoot = false
return GUIDETriggerState.NONE
func _editor_name() -> String:
return "Hold"
func _editor_description() -> String:
return "Fires, once the input has remained actuated for hold_threshold seconds.\n" + \
"My fire once or repeatedly."

View File

@ -0,0 +1 @@
uid://cfvgpvihp74si

View File

@ -0,0 +1,22 @@
@tool
## A trigger that activates when the input is pushed down. Will only emit a
## trigger event once. Holding the input will not trigger further events.
class_name GUIDETriggerPressed
extends GUIDETrigger
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):
return GUIDETriggerState.TRIGGERED
return GUIDETriggerState.NONE
func _editor_name() -> String:
return "Pressed"
func _editor_description() -> String:
return "Fires once, when the input exceeds actuation threshold. Holding the input\n" + \
"will not fire additional triggers."

View File

@ -0,0 +1 @@
uid://b52rqq28tuqpg

View File

@ -0,0 +1,86 @@
@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."

View File

@ -0,0 +1 @@
uid://dna8tkcuk528v

View File

@ -0,0 +1,21 @@
@tool
## A trigger that activates when the input is released down. Will only emit a
## trigger event once.
class_name GUIDETriggerReleased
extends GUIDETrigger
func _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
if not _is_actuated(input, value_type):
if _is_actuated(_last_value, value_type):
return GUIDETriggerState.TRIGGERED
return GUIDETriggerState.NONE
func _editor_name() -> String:
return "Released"
func _editor_description() -> String:
return "Fires once, when the input goes from actuated to not actuated. The opposite of the Pressed trigger."

View File

@ -0,0 +1 @@
uid://biiggjw6tv4uq

View File

@ -0,0 +1,72 @@
@tool
## Triggers depending on whether the input changes while actuated. This trigger is
## is implicit, so it must succeed for all other triggers to succeed.
class_name GUIDETriggerStability
extends GUIDETrigger
enum TriggerWhen {
## Input must be stable
INPUT_IS_STABLE,
## Input must change
INPUT_CHANGES
}
## The maximum amount that the input can change after actuation before it is
## considered "changed".
@export var max_deviation:float = 1
## When should the trigger trigger?
@export var trigger_when:TriggerWhen = TriggerWhen.INPUT_IS_STABLE
var _initial_value:Vector3
var _deviated:bool = false
func _get_trigger_type() -> GUIDETriggerType:
return GUIDETriggerType.IMPLICIT
func _update_state(input:Vector3, delta:float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
if _is_actuated(input, value_type):
if _deviated:
if trigger_when == TriggerWhen.INPUT_IS_STABLE:
return GUIDETriggerState.NONE
return GUIDETriggerState.TRIGGERED
if not _is_actuated(_last_value, value_type):
# we went from "not actuated" to actuated, start
_initial_value = input
if trigger_when == TriggerWhen.INPUT_IS_STABLE:
return GUIDETriggerState.TRIGGERED
else:
return GUIDETriggerState.ONGOING
# calculate how far the input is from the initial value
if _initial_value.distance_squared_to(input) > (max_deviation * max_deviation):
_deviated = true
if trigger_when == TriggerWhen.INPUT_IS_STABLE:
return GUIDETriggerState.NONE
return GUIDETriggerState.TRIGGERED
if trigger_when == TriggerWhen.INPUT_IS_STABLE:
return GUIDETriggerState.TRIGGERED
return GUIDETriggerState.ONGOING
# if the input is not actuated
_deviated = false
return GUIDETriggerState.NONE
func _editor_name() -> String:
return "Stability"
func _editor_description() -> String:
return "Triggers depending on whether the input changes while actuated. This trigger\n" +\
"is implicit, so it must succeed for all other triggers to succeed."

View File

@ -0,0 +1 @@
uid://deoksgw6vfo5v

View File

@ -0,0 +1,48 @@
@tool
## A trigger that activates when the input is tapped and released before the time threshold is reached.
class_name GUIDETriggerTap
extends GUIDETrigger
## The time threshold for the tap to be considered a tap.
@export var tap_threshold: float = 0.2
var _accumulated_time: float = 0
func _update_state(input: Vector3, delta: float, value_type:GUIDEAction.GUIDEActionValueType) -> GUIDETriggerState:
if _is_actuated(input, value_type):
# if the input was actuated before, and the tap threshold has been exceeded, the trigger is locked down
# until the input is released and we can exit out early
if _is_actuated(_last_value, value_type) and _accumulated_time > tap_threshold:
return GUIDETriggerState.NONE
# accumulate time
_accumulated_time += delta
if _accumulated_time < tap_threshold:
return GUIDETriggerState.ONGOING
else:
# we have exceeded the tap threshold, so the tap is not triggered.
return GUIDETriggerState.NONE
else: # not actuated right now
# if the input was actuated before...
if _is_actuated(_last_value, value_type):
# ... and the accumulated time is less than the threshold, then the tap is triggered.
if _accumulated_time < tap_threshold:
_accumulated_time = 0
return GUIDETriggerState.TRIGGERED
# Otherwise, the tap is not triggered, but we reset the accumulated time
# so the trigger is now again ready to be triggered.
_accumulated_time = 0
# in either case, the trigger is not triggered.
return GUIDETriggerState.NONE
func _editor_name() -> String:
return "Tap"
func _editor_description() -> String:
return "Fires when the input is actuated and released within the given timeframe."

View File

@ -0,0 +1 @@
uid://c76fmncyucwqc