Some script improvements, clean-up and refactoring

- Refactored VibrationComponent (RumbleComponent)
-- Now supports multiple vibrations and adjustable curves.
-- Inspired by the RumblePak addon.

- Added a new startup scene.
This commit is contained in:
AyurooLee 2026-06-22 01:52:04 +02:00
parent 774b481865
commit 94beee112e
58 changed files with 1144 additions and 285 deletions

View File

@ -7,7 +7,8 @@
[ext_resource type="AnimationLibrary" uid="uid://dhcvtj55pdbi0" path="res://_development/ayuroo/maestro/_mo_anim.tres" id="4_q2x38"] [ext_resource type="AnimationLibrary" uid="uid://dhcvtj55pdbi0" path="res://_development/ayuroo/maestro/_mo_anim.tres" id="4_q2x38"]
[ext_resource type="Script" uid="uid://cx1nws4t8hs2u" path="res://src/core/rhythm/tempo_section_info.gd" id="4_we4r8"] [ext_resource type="Script" uid="uid://cx1nws4t8hs2u" path="res://src/core/rhythm/tempo_section_info.gd" id="4_we4r8"]
[ext_resource type="Material" uid="uid://c38215ysnknyk" path="res://assets/dev/dark/dark_01.tres" id="7_6lemm"] [ext_resource type="Material" uid="uid://c38215ysnknyk" path="res://assets/dev/dark/dark_01.tres" id="7_6lemm"]
[ext_resource type="Script" uid="uid://bbwtct3hoxwws" path="res://src/core/vibration_component.gd" id="9_yut28"] [ext_resource type="Script" uid="uid://dwwingj0dsxdg" path="res://src/core/controller_rumble/rumble_config.gd" id="9_51o3i"]
[ext_resource type="Script" uid="uid://bbwtct3hoxwws" path="res://src/core/controller_rumble/rumble_component.gd" id="9_yut28"]
[sub_resource type="Resource" id="Resource_gvx8b"] [sub_resource type="Resource" id="Resource_gvx8b"]
script = ExtResource("3_7lyik") script = ExtResource("3_7lyik")
@ -30,6 +31,19 @@ height = 0.3
radius = 0.15 radius = 0.15
height = 0.3 height = 0.3
[sub_resource type="Resource" id="Resource_51o3i"]
script = ExtResource("9_51o3i")
duration = 0.1
weak_intensity = 0.575
strong_intensity = 0.75
[sub_resource type="Resource" id="Resource_ploqb"]
script = ExtResource("9_51o3i")
duration = 0.175
weak_intensity = 0.375
strong_intensity = 0.0
metadata/_custom_type_script = "uid://dwwingj0dsxdg"
[sub_resource type="BoxShape3D" id="BoxShape3D_gsfaw"] [sub_resource type="BoxShape3D" id="BoxShape3D_gsfaw"]
size = Vector3(14, 5, 1) size = Vector3(14, 5, 1)
@ -96,6 +110,7 @@ material = ExtResource("7_6lemm")
[node name="HeavyBeatVibration" type="Node" parent="." unique_id=1136863204] [node name="HeavyBeatVibration" type="Node" parent="." unique_id=1136863204]
script = ExtResource("9_yut28") script = ExtResource("9_yut28")
data = SubResource("Resource_51o3i")
duration = 0.1 duration = 0.1
sync_to_audio = true sync_to_audio = true
weak_magnitude = 0.575 weak_magnitude = 0.575
@ -103,6 +118,7 @@ strong_magnitude = 0.75
[node name="LightBeatVibration" type="Node" parent="." unique_id=2006943016] [node name="LightBeatVibration" type="Node" parent="." unique_id=2006943016]
script = ExtResource("9_yut28") script = ExtResource("9_yut28")
data = SubResource("Resource_ploqb")
duration = 0.175 duration = 0.175
sync_to_audio = true sync_to_audio = true
weak_magnitude = 0.375 weak_magnitude = 0.375

View File

@ -101,24 +101,24 @@ func _on_action(action: Action):
var node = anim_player\ var node = anim_player\
.get_node(anim_player.root_node)\ .get_node(anim_player.root_node)\
.get_node_or_null(_current_info.path) .get_node_or_null(_current_info.path)
if node: if node:
track_path = node.name track_path = node.name
var property := _current_info.path.get_concatenated_subnames() var property := _current_info.path.get_concatenated_subnames()
if not property.is_empty(): if not property.is_empty():
track_path += ":" + property track_path += ":" + property
var msg = 'Delete all tracks with the path "%s"?' % track_path var msg = 'Delete all tracks with the path "%s"?' % track_path
if _current_info.type == EditInfo.Type.NODE: if _current_info.type == EditInfo.Type.NODE:
msg = 'Delete tracks belonging to the node "%s"?' % track_path msg = 'Delete tracks belonging to the node "%s"?' % track_path
_show_confirmation(msg, _remove) _show_confirmation(msg, _remove)
func _render_edit_dialogue(): func _render_edit_dialogue():
var info := _current_info var info := _current_info
if info.type == EditInfo.Type.METHOD_TRACK: if info.type == EditInfo.Type.METHOD_TRACK:
is_full_path = false is_full_path = false
edit_full_path_toggle.disabled = true edit_full_path_toggle.disabled = true
@ -126,7 +126,7 @@ func _render_edit_dialogue():
else: else:
edit_full_path_toggle.disabled = false edit_full_path_toggle.disabled = false
edit_full_path_toggle.visible = true edit_full_path_toggle.visible = true
if is_full_path: if is_full_path:
edit_dialogue_input.text = info.path edit_dialogue_input.text = info.path
else: else:
@ -161,7 +161,7 @@ func _on_full_path_toggled(pressed: bool):
## Callback on rename ## Callback on rename
func _on_rename_confirmed(_arg0 = null): func _on_rename_confirmed(_arg0 = null):
var new := edit_dialogue_input.text var new := edit_dialogue_input.text
edit_dialogue.hide() edit_dialogue.hide()
if not _anim_player or not _anim_player is AnimationPlayer: if not _anim_player or not _anim_player is AnimationPlayer:
push_error("AnimationPlayer is null or invalid") push_error("AnimationPlayer is null or invalid")
@ -232,4 +232,3 @@ func _show_confirmation(text: String, on_confirmed: Callable):
confirmation_dialogue.confirmed.connect(on_confirmed, CONNECT_ONE_SHOT) confirmation_dialogue.confirmed.connect(on_confirmed, CONNECT_ONE_SHOT)
confirmation_dialogue.popup_centered() confirmation_dialogue.popup_centered()
confirmation_dialogue.dialog_text = text confirmation_dialogue.dialog_text = text

View File

@ -1,16 +1,16 @@
[gd_scene load_steps=4 format=3 uid="uid://cyfxysds8uhnx"] [gd_scene format=3 uid="uid://cyfxysds8uhnx"]
[ext_resource type="Script" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.gd" id="1_nkqdl"] [ext_resource type="Script" uid="uid://ft8r8earybjw" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.gd" id="1_nkqdl"]
[ext_resource type="Script" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/components/anim_player_tree.gd" id="2_7pqfs"] [ext_resource type="Script" uid="uid://bk2ganon2sasp" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/components/anim_player_tree.gd" id="2_7pqfs"]
[ext_resource type="Script" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/components/node_select.gd" id="3_87x4i"] [ext_resource type="Script" uid="uid://4alsx2krjeo8" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/components/node_select.gd" id="3_87x4i"]
[node name="RefactorDialogue" type="AcceptDialog"] [node name="RefactorDialogue" type="AcceptDialog" unique_id=480531508]
title = "Refactor Animations" title = "Refactor Animations"
size = Vector2i(400, 599) size = Vector2i(400, 599)
ok_button_text = "Close" ok_button_text = "Close"
script = ExtResource("1_nkqdl") script = ExtResource("1_nkqdl")
[node name="VBoxContainer" type="VBoxContainer" parent="."] [node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=960944020]
offset_left = 8.0 offset_left = 8.0
offset_top = 8.0 offset_top = 8.0
offset_right = 392.0 offset_right = 392.0
@ -19,20 +19,20 @@ size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
theme_override_constants/separation = 8 theme_override_constants/separation = 8
[node name="TreeContainer" type="VBoxContainer" parent="VBoxContainer"] [node name="TreeContainer" type="VBoxContainer" parent="VBoxContainer" unique_id=1854530831]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/TreeContainer"] [node name="Label" type="Label" parent="VBoxContainer/TreeContainer" unique_id=524900720]
layout_mode = 2 layout_mode = 2
text = "Properties:" text = "Properties:"
[node name="FilterInput" type="LineEdit" parent="VBoxContainer/TreeContainer"] [node name="FilterInput" type="LineEdit" parent="VBoxContainer/TreeContainer" unique_id=309777513]
layout_mode = 2 layout_mode = 2
placeholder_text = "Filter..." placeholder_text = "Filter..."
caret_blink = true caret_blink = true
caret_blink_interval = 0.5 caret_blink_interval = 0.5
[node name="AnimPlayerTree" type="Tree" parent="VBoxContainer/TreeContainer"] [node name="AnimPlayerTree" type="Tree" parent="VBoxContainer/TreeContainer" unique_id=222752454]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(300, 400) custom_minimum_size = Vector2(300, 400)
layout_mode = 2 layout_mode = 2
@ -41,95 +41,95 @@ hide_root = true
scroll_horizontal_enabled = false scroll_horizontal_enabled = false
script = ExtResource("2_7pqfs") script = ExtResource("2_7pqfs")
[node name="RootNodeContainer" type="VBoxContainer" parent="VBoxContainer"] [node name="RootNodeContainer" type="VBoxContainer" parent="VBoxContainer" unique_id=646228580]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/RootNodeContainer"] [node name="Label" type="Label" parent="VBoxContainer/RootNodeContainer" unique_id=1112656841]
layout_mode = 2 layout_mode = 2
text = "Root Node" text = "Root Node"
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/RootNodeContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/RootNodeContainer" unique_id=1988478185]
layout_mode = 2 layout_mode = 2
[node name="ChangeRoot" type="Button" parent="VBoxContainer/RootNodeContainer/HBoxContainer"] [node name="ChangeRoot" type="Button" parent="VBoxContainer/RootNodeContainer/HBoxContainer" unique_id=162815316]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
text = "Change Root" text = "Change Root"
[node name="EditDialogue" type="ConfirmationDialog" parent="."] [node name="EditDialogue" type="ConfirmationDialog" parent="." unique_id=2018114141]
unique_name_in_owner = true unique_name_in_owner = true
title = "Renaming" title = "Renaming"
position = Vector2i(0, 36) position = Vector2i(0, 36)
size = Vector2i(230, 239) size = Vector2i(230, 239)
[node name="VBoxContainer" type="VBoxContainer" parent="EditDialogue"] [node name="VBoxContainer" type="VBoxContainer" parent="EditDialogue" unique_id=1119827862]
offset_left = 8.0 offset_left = 8.0
offset_top = 8.0 offset_top = 8.0
offset_right = 222.0 offset_right = 222.0
offset_bottom = 190.0 offset_bottom = 190.0
[node name="HBoxContainer" type="HBoxContainer" parent="EditDialogue/VBoxContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="EditDialogue/VBoxContainer" unique_id=935198969]
layout_mode = 2 layout_mode = 2
[node name="EditDialogueButton" type="Button" parent="EditDialogue/VBoxContainer/HBoxContainer"] [node name="EditDialogueButton" type="Button" parent="EditDialogue/VBoxContainer/HBoxContainer" unique_id=456759932]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
focus_mode = 0 focus_mode = 0
[node name="EditInput" type="LineEdit" parent="EditDialogue/VBoxContainer"] [node name="EditInput" type="LineEdit" parent="EditDialogue/VBoxContainer" unique_id=2101598468]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_vertical = 0 size_flags_vertical = 0
[node name="HBoxContainer2" type="HBoxContainer" parent="EditDialogue/VBoxContainer"] [node name="HBoxContainer2" type="HBoxContainer" parent="EditDialogue/VBoxContainer" unique_id=1105990044]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="EditDialogue/VBoxContainer/HBoxContainer2"] [node name="Label" type="Label" parent="EditDialogue/VBoxContainer/HBoxContainer2" unique_id=1241184059]
layout_mode = 2 layout_mode = 2
text = "Used in:" text = "Used in:"
[node name="EditFullPathToggle" type="CheckButton" parent="EditDialogue/VBoxContainer/HBoxContainer2"] [node name="EditFullPathToggle" type="CheckButton" parent="EditDialogue/VBoxContainer/HBoxContainer2" unique_id=1158699283]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 10 size_flags_horizontal = 10
text = "Edit full path" text = "Edit full path"
[node name="MarginContainer" type="MarginContainer" parent="EditDialogue/VBoxContainer"] [node name="MarginContainer" type="MarginContainer" parent="EditDialogue/VBoxContainer" unique_id=1578839664]
custom_minimum_size = Vector2(0, 100) custom_minimum_size = Vector2(0, 100)
layout_mode = 2 layout_mode = 2
[node name="ColorRect" type="ColorRect" parent="EditDialogue/VBoxContainer/MarginContainer"] [node name="ColorRect" type="ColorRect" parent="EditDialogue/VBoxContainer/MarginContainer" unique_id=1203243085]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
color = Color(0, 0, 0, 0.211765) color = Color(0, 0, 0, 0.211765)
[node name="ScrollContainer" type="ScrollContainer" parent="EditDialogue/VBoxContainer/MarginContainer"] [node name="ScrollContainer" type="ScrollContainer" parent="EditDialogue/VBoxContainer/MarginContainer" unique_id=1073527380]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
horizontal_scroll_mode = 0 horizontal_scroll_mode = 0
[node name="MarginContainer" type="MarginContainer" parent="EditDialogue/VBoxContainer/MarginContainer/ScrollContainer"] [node name="MarginContainer" type="MarginContainer" parent="EditDialogue/VBoxContainer/MarginContainer/ScrollContainer" unique_id=1889816102]
layout_mode = 2 layout_mode = 2
theme_override_constants/margin_left = 2 theme_override_constants/margin_left = 2
theme_override_constants/margin_top = 2 theme_override_constants/margin_top = 2
theme_override_constants/margin_right = 2 theme_override_constants/margin_right = 2
theme_override_constants/margin_bottom = 2 theme_override_constants/margin_bottom = 2
[node name="EditAnimationList" type="Label" parent="EditDialogue/VBoxContainer/MarginContainer/ScrollContainer/MarginContainer"] [node name="EditAnimationList" type="Label" parent="EditDialogue/VBoxContainer/MarginContainer/ScrollContainer/MarginContainer" unique_id=1056061212]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Test text = "Test
Test 2" Test 2"
[node name="NodeSelectDialogue" type="ConfirmationDialog" parent="."] [node name="NodeSelectDialogue" type="ConfirmationDialog" parent="." unique_id=1598902309]
unique_name_in_owner = true unique_name_in_owner = true
title = "Select a node..." title = "Select a node..."
size = Vector2i(616, 557) size = Vector2i(616, 557)
ok_button_text = "Change" ok_button_text = "Change"
[node name="NodeSelect" type="Tree" parent="NodeSelectDialogue"] [node name="NodeSelect" type="Tree" parent="NodeSelectDialogue" unique_id=1495313424]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(600, 500) custom_minimum_size = Vector2(600, 500)
offset_left = 8.0 offset_left = 8.0
@ -139,7 +139,7 @@ offset_bottom = 508.0
scroll_horizontal_enabled = false scroll_horizontal_enabled = false
script = ExtResource("3_87x4i") script = ExtResource("3_87x4i")
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] [node name="ConfirmationDialog" type="ConfirmationDialog" parent="." unique_id=585191931]
unique_name_in_owner = true unique_name_in_owner = true
size = Vector2i(300, 200) size = Vector2i(300, 200)
ok_button_text = "Delete" ok_button_text = "Delete"

View File

@ -15,10 +15,10 @@ void vertex() {
void fragment() { void fragment() {
float phase = get_beat_phase(); float phase = get_beat_phase();
vec4 tex = texture(albedo, UV); vec4 tex = texture(albedo, UV);
float edge = dot(NORMAL, VIEW); float edge = dot(NORMAL, VIEW);
edge = clamp(pow(edge, mix(8.0, 2.0, edge_fade)), 0.0, 1.0); edge = clamp(pow(edge, mix(8.0, 2.0, edge_fade)), 0.0, 1.0);
ALBEDO = mix(tex.rgb, color.rgb, 0.5); ALBEDO = mix(tex.rgb, color.rgb, 0.5);
ALPHA = max(tex.a * ease(1.0 - phase, flash_exponent) * alpha_multiplier * edge, 0.0); ALPHA = max(tex.a * ease(1.0 - phase, flash_exponent) * alpha_multiplier * edge, 0.0);
} }

View File

@ -29,6 +29,7 @@ ControllerIcons="*uid://bdosbfkp568je"
ShaderGlobals="*uid://d2lr860r1ysrm" ShaderGlobals="*uid://d2lr860r1ysrm"
LimboConsole="*uid://dyxornv8vwibg" LimboConsole="*uid://dyxornv8vwibg"
SettingsHandler="*uid://dj1t6rc6fpiti" SettingsHandler="*uid://dj1t6rc6fpiti"
RumbleConductor="*uid://by4n7qykdds0o"
[debug] [debug]
@ -239,3 +240,7 @@ time={
"type": "float", "type": "float",
"value": 0.0 "value": 0.0
} }
is_editor_hint={
"type": "bool",
"value": false
}

View File

@ -34,7 +34,6 @@ func _set_handle(id: int, secondary: bool, camera: Camera3D, point: Vector2) ->
var position: Vector3 = _aabb.position var position: Vector3 = _aabb.position
func _redraw() -> void: func _redraw() -> void:
clear() clear()
@ -88,7 +87,7 @@ func _box_commit_handle(
func _box_get_handle_name(id: int) -> String: func _box_get_handle_name(id: int) -> String:
match id: match id:
0,2,4: 0, 2, 4:
return "" return ""
1: 1:
return "Size X" return "Size X"
@ -102,7 +101,7 @@ func _box_get_handle_name(id: int) -> String:
func _box_set_handle(segment: Vector3, id: int, size: Vector3, position: Vector3) -> void: func _box_set_handle(segment: Vector3, id: int, size: Vector3, position: Vector3) -> void:
var axis: int = id / 2 var axis: int = id / 2
var sign = id % 2 * -2 + 1 var sign: float = id % 2 * -2 + 1
var _initial_size: Vector3 = initial_value var _initial_size: Vector3 = initial_value
var neg_end: float = _initial_size[axis] * -0.5 var neg_end: float = _initial_size[axis] * -0.5
var pos_end: float = _initial_size[axis] * 0.5 var pos_end: float = _initial_size[axis] * 0.5

View File

@ -45,7 +45,9 @@ func release_mouse() -> void:
func is_keyboard_or_mouse_event(event: InputEvent) -> bool: func is_keyboard_or_mouse_event(event: InputEvent) -> bool:
return ( return (
event is InputEventMouseButton or event is InputEventMouseMotion or event is InputEventKey event is InputEventMouseButton
or event is InputEventMouseMotion
or event is InputEventKey
) )

View File

@ -69,8 +69,8 @@ func apply_settings() -> void:
if not Engine.is_embedded_in_editor(): if not Engine.is_embedded_in_editor():
DisplayServer.window_set_mode(get_setting("video", "window_mode", DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN)) DisplayServer.window_set_mode(get_setting("video", "window_mode", DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN))
# Controls # Controls
VibrationComponent.min_weak_magnitude_threshold = get_setting("controls", "vibration_min_weak_threshold", 0.0) RumbleConductor.min_weak_magnitude_threshold = get_setting("controls", "vibration_min_weak_threshold", 0.0)
VibrationComponent.min_strong_magnitude_threshold = get_setting("controls", "vibration_min_strong_threshold", 0.0) RumbleConductor.min_strong_magnitude_threshold = get_setting("controls", "vibration_min_strong_threshold", 0.0)
func commit_settings() -> void: func commit_settings() -> void:

View File

@ -12,9 +12,16 @@ var user_offset_ms: float = 0.0
func _notification(what: int) -> void: func _notification(what: int) -> void:
# Just in case.
if what == NOTIFICATION_EDITOR_PRE_SAVE: if what == NOTIFICATION_EDITOR_PRE_SAVE:
# Just in case.
RenderingServer.global_shader_parameter_set(&"time", 0.0) RenderingServer.global_shader_parameter_set(&"time", 0.0)
RenderingServer.global_shader_parameter_set(&"is_editor_hint", false)
elif what == NOTIFICATION_EDITOR_POST_SAVE:
RenderingServer.global_shader_parameter_set(&"is_editor_hint", Engine.is_editor_hint())
func _ready() -> void:
RenderingServer.global_shader_parameter_set(&"is_editor_hint", Engine.is_editor_hint())
func _process(delta: float) -> void: func _process(delta: float) -> void:

View File

@ -1,5 +1,5 @@
@tool @tool
class_name VibrationComponent class_name RumbleComponent
extends Node extends Node
## A small helper node to quickly perform controller vibrations. ## A small helper node to quickly perform controller vibrations.
@ -10,10 +10,13 @@ extends Node
#static var total_weak_vibration: float = 0.0 #static var total_weak_vibration: float = 0.0
#static var total_strong_vibration: float = 0.0 #static var total_strong_vibration: float = 0.0
#static var _vibration_handled: bool = false #static var _vibration_handled: bool = false
static var min_weak_magnitude_threshold: float = 0.0 #static var min_weak_magnitude_threshold: float = 0.0
static var min_strong_magnitude_threshold: float = 0.0 #static var min_strong_magnitude_threshold: float = 0.0
#static var min_duration_threshold: float = 0.0 #static var min_duration_threshold: float = 0.0
@export var data: RumbleConfig
## If [code]false[/code], calling [method vibrate] will not start a controller vibration. ## If [code]false[/code], calling [method vibrate] will not start a controller vibration.
@export var enabled: bool = true: @export var enabled: bool = true:
get = is_enabled, get = is_enabled,
@ -28,8 +31,7 @@ static var min_strong_magnitude_threshold: float = 0.0
@export var device: int = 0: @export var device: int = 0:
get = get_device, get = get_device,
set = set_device set = set_device
## Delay the vibration by this amount.
@export var delay: float = 0.0
@export var sync_to_audio: bool = false @export var sync_to_audio: bool = false
#region Editor tooling #region Editor tooling
@ -65,18 +67,26 @@ var animated_strong_magnitude: float = 0.0:
animated_strong_magnitude = clampf(value, 0.0, 1.0) animated_strong_magnitude = clampf(value, 0.0, 1.0)
func _process(delta: float) -> void: func _init() -> void:
if not is_instance_valid(data):
data = RumbleConfig.new()
func _physics_process(delta: float) -> void:
if ( if (
can_vibrate() can_vibrate()
and not is_zero_approx(animated_weak_magnitude + animated_strong_magnitude) and not is_zero_approx(animated_weak_magnitude + animated_strong_magnitude)
): ):
var _weak_magnitude: float = animated_weak_magnitude var config := RumbleConfig.new()
var _strong_magnitude: float = animated_strong_magnitude config.weak_intensity = animated_weak_magnitude
config.strong_intensity = animated_strong_magnitude
config.duration = delta
_weak_magnitude = remap(_weak_magnitude, 0.0, 1.0, min_weak_magnitude_threshold * float(_weak_magnitude > 0.0), 1.0) #_weak_magnitude = remap(_weak_magnitude, 0.0, 1.0, min_weak_magnitude_threshold * float(_weak_magnitude > 0.0), 1.0)
_strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_magnitude > 0.0), 1.0) #_strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_magnitude > 0.0), 1.0)
Input.start_joy_vibration.call_deferred(device, _weak_magnitude, _strong_magnitude, delta) RumbleConductor.add_rumble(config, device)
#Input.start_joy_vibration.call_deferred(device, _weak_magnitude, _strong_magnitude, delta)
animated_weak_magnitude = 0.0 animated_weak_magnitude = 0.0
animated_strong_magnitude = 0.0 animated_strong_magnitude = 0.0
@ -87,20 +97,20 @@ func vibrate() -> void:
if not can_vibrate(): if not can_vibrate():
return return
if delay > 0.0:
await get_tree().create_timer(delay).timeout
if sync_to_audio: if sync_to_audio:
var last_mix_time: float = AudioServer.get_time_since_last_mix() var last_mix_time: float = AudioServer.get_time_since_last_mix()
var output_latency: float = AudioServer.get_output_latency() var output_latency: float = AudioServer.get_output_latency()
#SPrint.print_msg("Audio Delay: %s" % (last_mix_time + output_latency)) #SPrint.print_msg("Audio Delay: %s" % (last_mix_time + output_latency))
await get_tree().create_timer(last_mix_time + output_latency).timeout await get_tree().create_timer(last_mix_time + output_latency).timeout
data.rumble(device)
return
var _weak_magnitude: float = clampf(weak_magnitude * magnitude_multiplier, 0.0, 1.0) var _weak_magnitude: float = clampf(weak_magnitude * magnitude_multiplier, 0.0, 1.0)
var _strong_magnitude: float = clampf(strong_magnitude * magnitude_multiplier, 0.0, 1.0) var _strong_magnitude: float = clampf(strong_magnitude * magnitude_multiplier, 0.0, 1.0)
_weak_magnitude = remap(_weak_magnitude, 0.0, 1.0, min_weak_magnitude_threshold * float(_weak_magnitude > 0.0), 1.0) #_weak_magnitude = remap(_weak_magnitude, 0.0, 1.0, min_weak_magnitude_threshold * float(_weak_magnitude > 0.0), 1.0)
_strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_magnitude > 0.0), 1.0) #_strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_magnitude > 0.0), 1.0)
#duration = maxf(duration, min_duration_threshold) #duration = maxf(duration, min_duration_threshold)

View File

@ -0,0 +1,64 @@
@tool
extends Node
# Inspired by the RumblePak addon by Elliptical
# (https://store.godotengine.org/asset/elliptical/rumblepak/)
static var min_weak_magnitude_threshold: float = 0.0
static var min_strong_magnitude_threshold: float = 0.0
var vibrations: Array[RumbleData] = []
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
for device: int in Input.get_connected_joypads():
var total_rumble := Vector2.ZERO
for i: int in range(vibrations.size() - 1, -1, -1):
var rumble_data: RumbleData = vibrations.get(i)
if rumble_data.device != device:
continue
var config: RumbleConfig = rumble_data.config
var time_elapsed: float = rumble_data.time_elapsed
if not is_instance_valid(config) or time_elapsed > config.duration:
vibrations.remove_at(i)
continue
var rumble := Vector2(config.weak_intensity, config.strong_intensity)
var curve_offset: Vector2
if is_instance_valid(config.weak_curve):
curve_offset.x = config.weak_curve.max_domain * (time_elapsed / config.duration)
rumble.x = config.weak_curve.sample_baked(curve_offset.x) * config.weak_intensity
if is_instance_valid(config.strong_curve):
curve_offset.y = config.strong_curve.max_domain * (time_elapsed / config.duration)
rumble.y = config.strong_curve.sample_baked(curve_offset.y) * config.strong_intensity
rumble.x = remap(rumble.x, 0.0, 1.0, min_weak_magnitude_threshold * float(rumble.x > 0.0), 1.0)
rumble.y = remap(rumble.y, 0.0, 1.0, min_strong_magnitude_threshold * float(rumble.y > 0.0), 1.0)
rumble *= config.intensity
total_rumble = (total_rumble + rumble).clampf(0.0, 1.0)
rumble_data.time_elapsed += delta
Input.start_joy_vibration(device, total_rumble.x, total_rumble.y)
func add_rumble(rumble_config: RumbleConfig, device: int = 0) -> void:
var rumble_data := RumbleData.new()
rumble_data.config = rumble_config
rumble_data.device = device
vibrations.append(rumble_data)
class RumbleData:
var config: RumbleConfig
var time_elapsed: float = 0.0
var device: int = 0

View File

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

View File

@ -0,0 +1,22 @@
@tool
class_name RumbleConfig
extends Resource
@warning_ignore("unused_private_class_variable")
@export_tool_button("Test Rumble", "Noise")
var _editor_test_vibration: Callable = rumble
@export_range(0.0, 1.0) var intensity: float = 1.0
@export_range(0.0, 1.0, 0.001, "or_greater", "suffix:s") var duration: float = 0.15
@export_group("Weak Magnitude", "weak_")
@export_range(0.0, 1.0) var weak_intensity: float = 1.0
@export var weak_curve: Curve
@export_group("Strong Magnitude", "strong_")
@export_range(0.0, 1.0) var strong_intensity: float = 1.0
@export var strong_curve: Curve
func rumble(device: int = 0) -> void:
RumbleConductor.add_rumble(self, device)

View File

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

View File

@ -6,7 +6,8 @@ var _node_hash: String = ""
func _init(node: Node, world: World) -> void: func _init(node: Node, world: World) -> void:
assert(is_instance_valid(world), "world must be valid. Are you sure you have defined set the reference?") assert(is_instance_valid(world),
"'world' must be valid. Are you sure you have defined/set the reference correctly?")
_node = node _node = node
_node_hash = str(world.get_path_to(node).hash()) _node_hash = str(world.get_path_to(node).hash())
_world = world _world = world

View File

@ -26,7 +26,7 @@ func load_threaded(path: String) -> Resource:
while ResourceLoader.load_threaded_get_status(path) != ResourceLoader.THREAD_LOAD_LOADED: while ResourceLoader.load_threaded_get_status(path) != ResourceLoader.THREAD_LOAD_LOADED:
match ResourceLoader.load_threaded_get_status(path, load_progress): match ResourceLoader.load_threaded_get_status(path, load_progress):
ResourceLoader.THREAD_LOAD_FAILED || ResourceLoader.THREAD_LOAD_INVALID_RESOURCE: ResourceLoader.THREAD_LOAD_FAILED or ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
#SPrint.print_msg("[Interactive Loader] load failed.", -1.0, SPrint.WARNING) #SPrint.print_msg("[Interactive Loader] load failed.", -1.0, SPrint.WARNING)
return null return null

View File

@ -2,8 +2,8 @@ class_name RhythmListener
extends Node extends Node
signal beat_tick(beat_index: int) signal beat_ticked(beat_index: int)
signal bar_tick(bar_index: int) signal bar_ticked(bar_index: int)
@export_range(0.0, 1.0, 0.001, "or_greater", "or_less") var phase_shift: float = 0.0 @export_range(0.0, 1.0, 0.001, "or_greater", "or_less") var phase_shift: float = 0.0
@export_range(0.0, 1.0, 0.001, "or_greater", "or_less") var phase_multiplier: float = 1.0 @export_range(0.0, 1.0, 0.001, "or_greater", "or_less") var phase_multiplier: float = 1.0
@ -29,12 +29,12 @@ func _process(_delta: float) -> void:
var bar_index: int = floori(bar) var bar_index: int = floori(bar)
if _last_beat_index != beat_index: if _last_beat_index != beat_index:
on_beat_tick(beat_index) on_beat_ticked(beat_index)
_last_beat_index = beat_index _last_beat_index = beat_index
_last_beat_time = RhythmPlayer.song_time + phase_shift _last_beat_time = RhythmPlayer.song_time + phase_shift
if _last_bar_index != bar_index: if _last_bar_index != bar_index:
on_bar_tick(bar_index) on_bar_ticked(bar_index)
_last_bar_index = bar_index _last_bar_index = bar_index
_last_bar_time = RhythmPlayer.song_time + phase_shift _last_bar_time = RhythmPlayer.song_time + phase_shift
@ -57,11 +57,11 @@ func get_time_to_next_bar() -> float:
# #
func on_beat_tick(beat_index: int) -> void: func on_beat_ticked(beat_index: int) -> void:
#SPrint.print_msg("Beat received %s" % beat_index, 0.75) #SPrint.print_msg("Beat received %s" % beat_index, 0.75)
beat_tick.emit(beat_index) beat_ticked.emit(beat_index)
func on_bar_tick(bar_index: int) -> void: func on_bar_ticked(bar_index: int) -> void:
#SPrint.print_msg("Bar received %s" % bar_index, 1.0) #SPrint.print_msg("Bar received %s" % bar_index, 1.0)
bar_tick.emit(bar_index) bar_ticked.emit(bar_index)

View File

@ -1,8 +1,8 @@
class_name RhythmPlayer class_name RhythmPlayer
extends Node extends Node
signal beat_tick(beat_index: int) signal beat_ticked(beat_index: int)
signal bar_tick(bar_index: int) signal bar_ticked(bar_index: int)
signal finished signal finished
static var user_offset_ms: float = 0.0: set = set_user_offset_ms static var user_offset_ms: float = 0.0: set = set_user_offset_ms
@ -172,11 +172,11 @@ func _update_playback() -> void:
#SPrint.print_msgf("Bar: %s (%s) bar on beat: %s" % [bar_to_signature(bar_index, _bars), bar_index, bar_index % _bars == 0]) #SPrint.print_msgf("Bar: %s (%s) bar on beat: %s" % [bar_to_signature(bar_index, _bars), bar_index, bar_index % _bars == 0])
if _last_beat != beat_index: if _last_beat != beat_index:
beat_tick.emit(beat_index) beat_ticked.emit(beat_index)
_last_beat = beat_index _last_beat = beat_index
if _last_bar != bar_index: if _last_bar != bar_index:
bar_tick.emit(bar_index) bar_ticked.emit(bar_index)
_last_bar = bar_index _last_bar = bar_index

View File

@ -136,7 +136,8 @@ func recalculate_times() -> void:
var seconds: float = beat_delta * 60.0 / previous.bpm var seconds: float = beat_delta * 60.0 / previous.bpm
current.time = previous.time + seconds current.time = previous.time + seconds
print_rich("- [b]%s[/b] [i]bpm[/i] ([b]1/%s[/b]) at [b]%s[/b] [i]seconds[/i] (beat delta: [b]%s[/b], seconds delta: [b]%s[/b])." % [current.bpm, current.beats_per_bar, current.time, beat_delta, seconds]) print_rich("- [b]%s[/b] [i]bpm[/i] ([b]1/%s[/b]) at [b]%s[/b] [i]seconds[/i] (beat delta: [b]%s[/b], seconds delta: [b]%s[/b])."
% [current.bpm, current.beats_per_bar, current.time, beat_delta, seconds])
func get_beat_at_time(time: float) -> float: func get_beat_at_time(time: float) -> float:

View File

@ -2,7 +2,7 @@ class_name SignalGroup
## @tutorial(Small Modified Version from ShaggyDev): https://shaggydev.com/2025/06/12/godot-awaiting-signals/ ## @tutorial(Small Modified Version from ShaggyDev): https://shaggydev.com/2025/06/12/godot-awaiting-signals/
signal _all_complete signal _all_completed
var _counter: int = 0 var _counter: int = 0
#var _watched_signals: Array[Signal] = [] #var _watched_signals: Array[Signal] = []
@ -36,7 +36,7 @@ func all(signals: Array[Signal], custom_count: int = -1) -> Array[Signal]:
_on_signal_complete(sig) _on_signal_complete(sig)
), CONNECT_ONE_SHOT) ), CONNECT_ONE_SHOT)
await _all_complete await _all_completed
return _signals_in_order return _signals_in_order
@ -52,7 +52,7 @@ func _on_signal_complete(sig: Signal) -> void:
_signals_in_order.append(sig) _signals_in_order.append(sig)
if _counter == 0: if _counter == 0:
_all_complete.emit() _all_completed.emit()
#class Probe extends RefCounted: #class Probe extends RefCounted:

View File

@ -99,8 +99,7 @@ static func full_rotation_float(
static func get_uid(path: String) -> int: static func get_uid(path: String) -> int:
var uid: int = ResourceUID.text_to_id(ResourceUID.path_to_uid(path)) return ResourceUID.text_to_id(ResourceUID.path_to_uid(path))
return uid
## Converts any given uid (as an [int] or [code]uid://[/code] path) ## Converts any given uid (as an [int] or [code]uid://[/code] path)

View File

@ -8,5 +8,3 @@ shader = ExtResource("1_1ceck")
shader_parameter/base_color = Color(0.3, 1, 0.3, 0.15) shader_parameter/base_color = Color(0.3, 1, 0.3, 0.15)
shader_parameter/edge_color = Color(1, 1, 0.3, 0.8) shader_parameter/edge_color = Color(1, 1, 0.3, 0.8)
shader_parameter/edge_power = 3.0 shader_parameter/edge_power = 3.0
shader_parameter/box_center = Vector3(0, 0, 0)
shader_parameter/box_extents = Vector3(0, 0, 0)

View File

@ -9,18 +9,28 @@ signal loading_finished
@export var enabled: bool = true: set = set_enabled @export var enabled: bool = true: set = set_enabled
@export var threaded_loading: bool = true @export var threaded_loading: bool = true
@export var prevent_unloading: bool = false @export var prevent_unloading: bool = false
@export var unload_delay: float = 3.0 @export var handles_level_visibility: bool = true
@export_range(0.0, 10.0, 0.001, "or_greater", "suffix:s") var unload_delay: float = 3.0
@export var level_id: StringName = &"": @export var level_id: StringName = &"":
set(value): set(value):
level_id = value level_id = value
update_configuration_warnings() update_configuration_warnings()
@export_file("*.tscn", "*.scn") var scene_path: String: @export_file("*.tscn", "*.scn") var scene_path: String:
set(value): set(value):
scene_path = value scene_path = value
update_configuration_warnings() update_configuration_warnings()
@export_tool_button("Edit Level", "Edit") var editor_edit_level: Callable = _editor_edit_level
@export_tool_button("Load Level", "Load") var editor_load_level: Callable = _editor_load_level @export_tool_button("Edit Level", "Edit")
@export_tool_button("Unload Level", "Clear") var editor_unload_level: Callable = _editor_unload_level var editor_edit_level: Callable = _editor_edit_level
# alternative icons: 'Load', 'Override'
@export_tool_button("Load Level", "FileAccess")
var editor_load_level: Callable = _editor_load_level
@export_tool_button("Unload Level", "Clear")
var editor_unload_level: Callable = _editor_unload_level
@export var load_aabbs: Array[AABB] = []: @export var load_aabbs: Array[AABB] = []:
set(value): set(value):
load_aabbs = value load_aabbs = value
@ -33,11 +43,16 @@ signal loading_finished
#clear_gizmos() #clear_gizmos()
#for aabb: AABB in load_aabbs: #for aabb: AABB in load_aabbs:
#add_gizmo.call_deferred(AABBGizmo.new(aabb, self)) #add_gizmo.call_deferred(AABBGizmo.new(aabb, self))
@export_tool_button("Auto Generate AABB", "CSGBox3D") var editor_auto_gen_aabb: Callable = _editor_auto_gen_aabb @export_tool_button("Auto Generate AABB", "CSGBox3D")
@export var editor_visualize_aabbs_with_box: bool = true: set = _editor_set_visualize_aabbs_with_box var editor_auto_gen_aabb: Callable = _editor_auto_gen_aabb
@export var editor_visualize_aabbs_with_box: bool = true:
set = _editor_set_visualize_aabbs_with_box
var loaded_level: Node var loaded_level: Node
var is_loading_level: bool = false var is_loading_level: bool = false
var editor_force_draw_debug: bool = false
var _unload_time_left: float = 0.0 var _unload_time_left: float = 0.0
var _precomputed_aabb: AABB # Big bounding box covering all [_precomputed_aabbs] for faster checking. var _precomputed_aabb: AABB # Big bounding box covering all [_precomputed_aabbs] for faster checking.
var _precomputed_aabbs: Array[AABB] var _precomputed_aabbs: Array[AABB]
@ -49,8 +64,8 @@ static func get_node_aabb(node: Node3D = null, ignore_top_level: bool = true, bo
var aabb: AABB var aabb: AABB
var _transform: Transform3D var _transform: Transform3D
# We are going down the child chain, # We are going down the child chain,
# we want the aabb of each subsequent node to be on the same axis as the parent. # we want the aabb of each subsequent node to be on the same axis as the parent.
if bounds_transform.is_equal_approx(Transform3D()): if bounds_transform.is_equal_approx(Transform3D()):
_transform = node.global_transform _transform = node.global_transform
else: else:
@ -128,7 +143,10 @@ func _process(delta: float) -> void:
elif not prevent_unloading and is_level_loaded(): elif not prevent_unloading and is_level_loaded():
_unload_time_left = maxf(_unload_time_left - delta, 0.0) _unload_time_left = maxf(_unload_time_left - delta, 0.0)
if _unload_time_left == 0.0: if handles_level_visibility:
loaded_level.hide()
if is_zero_approx(_unload_time_left):
unload_level() unload_level()
@ -152,14 +170,20 @@ func get_precomputed_aabbs() -> Array[AABB]:
func load_level() -> void: func load_level() -> void:
_unload_time_left = unload_delay _unload_time_left = unload_delay
if is_level_loaded() or is_loading_level: if is_loading_level:
return
if is_level_loaded():
loaded_level.show()
return return
is_loading_level = true is_loading_level = true
assert(ResourceLoader.exists(scene_path, "PackedScene"), "'scene_path' is invalid or not a PackedScene.")
var scene: PackedScene = await load_scene_threaded() if threaded_loading else load(scene_path) var scene: PackedScene = await load_scene_threaded() if threaded_loading else load(scene_path)
loaded_level = scene.instantiate() loaded_level = scene.instantiate()
add_child(loaded_level) add_child(loaded_level, true)
is_loading_level = false is_loading_level = false
loading_finished.emit() loading_finished.emit()
@ -175,7 +199,7 @@ func is_level_loaded() -> bool:
func load_scene_threaded() -> PackedScene: func load_scene_threaded() -> PackedScene:
var interactive_loader := InteractiveLoader.create_oneshot(self) var interactive_loader := InteractiveLoader.create_oneshot_and_bind(self)
var resource: Resource = await interactive_loader.load_threaded(scene_path) var resource: Resource = await interactive_loader.load_threaded(scene_path)
return resource as PackedScene return resource as PackedScene
@ -183,6 +207,7 @@ func load_scene_threaded() -> PackedScene:
func get_camera() -> Camera3D: func get_camera() -> Camera3D:
var camera: Camera3D = get_viewport().get_camera_3d() var camera: Camera3D = get_viewport().get_camera_3d()
# also prevent godot's override in-game camera from affecting the loading.
if not is_instance_valid(camera) or camera.name == &"OverrideCamera3D": if not is_instance_valid(camera) or camera.name == &"OverrideCamera3D":
return _previous_camera return _previous_camera
@ -259,7 +284,7 @@ func _editor_unload_level() -> void:
func _editor_process_gizmo() -> void: func _editor_process_gizmo() -> void:
var editor_selection: Object = Engine.get_singleton(&"EditorInterface").get_selection() var editor_selection: Object = Engine.get_singleton(&"EditorInterface").get_selection()
if editor_selection.get_selected_nodes().has(self): if editor_selection.get_selected_nodes().has(self) or editor_force_draw_debug:
_draw_debug() _draw_debug()
return return

View File

@ -1,7 +1,7 @@
class_name WorldProxy class_name WorldProxy
extends Node extends Node
signal world_changed signal world_changed(new_world: World)
static var world_proxies: Array[WorldProxy] = [] static var world_proxies: Array[WorldProxy] = []

View File

@ -22,6 +22,7 @@ signal world_unload_requested(do_save: bool)
@export var debug_load_levels_based_on_player_position: bool = true @export var debug_load_levels_based_on_player_position: bool = true
var world_state: WorldState var world_state: WorldState
var game: Game
func _exit_tree() -> void: func _exit_tree() -> void:
@ -31,7 +32,7 @@ func _exit_tree() -> void:
func _ready() -> void: func _ready() -> void:
if debug_enabled: if debug_enabled:
if get_tree().current_scene == self: if get_tree().current_scene == self:
_setup_world_debug() _setup_debug_world_state()
if is_instance_valid(world_state): if is_instance_valid(world_state):
await apply_world_state() await apply_world_state()
@ -83,8 +84,8 @@ func apply_world_state() -> void:
if is_instance_valid(player): if is_instance_valid(player):
player.process_mode = Node.PROCESS_MODE_DISABLED player.process_mode = Node.PROCESS_MODE_DISABLED
if is_instance_valid(player) and player.has_method(&"apply_orientation"): if player.has_method(&"apply_orientation"):
player.apply_orientation(world_state.player_transform) player.apply_orientation(world_state.player_transform)
await _load_levels() await _load_levels()
@ -210,7 +211,7 @@ func _load_levels() -> void:
await SignalGroup.await_signals(signals) await SignalGroup.await_signals(signals)
func _setup_world_debug() -> void: func _setup_debug_world_state() -> void:
world_state = debug_world_state if is_instance_valid(debug_world_state) else WorldState.new() world_state = debug_world_state if is_instance_valid(debug_world_state) else WorldState.new()
if is_instance_valid(debug_player_position): if is_instance_valid(debug_player_position):

View File

@ -4,8 +4,8 @@ extends Node
## TODO: sicherungskasten minigames (Kabel verbinden, oder draufhauen und geht wieder) ## TODO: sicherungskasten minigames (Kabel verbinden, oder draufhauen und geht wieder)
const USER_SETTINGS_PATH: String = "user://settings.ini"
const SAVE_DIR: String = "user://saves/%s/" const SAVE_DIR: String = "user://saves/%d/" # TODO "user://ch1/saves/%d/ # Chapter 1
const SAVE_PATH: String = SAVE_DIR + "%s" const SAVE_PATH: String = SAVE_DIR + "%s"
const INITIAL_SAVE_DATA: SaveData = preload("uid://b8ojagpq5pxr2") const INITIAL_SAVE_DATA: SaveData = preload("uid://b8ojagpq5pxr2")
@ -14,12 +14,14 @@ static var is_debug: bool = true
@export_file("*.tscn", "*.scn") var initial_setup_path: String = "uid://8bxv3c5f8d2j" @export_file("*.tscn", "*.scn") var initial_setup_path: String = "uid://8bxv3c5f8d2j"
@export_file("*.tscn", "*.scn") var main_menu_path: String = "uid://7v62dybcabgw" @export_file("*.tscn", "*.scn") var main_menu_path: String = "uid://7v62dybcabgw"
var current_save_slot: int = 0 var save_slot: int = 0
var current_save_data: SaveData = INITIAL_SAVE_DATA.duplicate() var save_data: SaveData = INITIAL_SAVE_DATA.duplicate()
var world: World var world: World
var main_menu: MainMenu var main_menu: MainMenu
@onready var world_container: Node = %WorldContainer var _os_dialog_window_result: int = -1
@onready var world_holder: Node = %WorldHolder
@onready var loading_screen: LoadingScreen = %LoadingScreen @onready var loading_screen: LoadingScreen = %LoadingScreen
@ -28,39 +30,46 @@ static func _static_init() -> void:
static func debug_draw(draw_shape: String, args: Array) -> void: static func debug_draw(draw_shape: String, args: Array) -> void:
if is_debug and Engine.has_singleton(&"DebugDraw3D"): const DD3D: StringName = &"DebugDraw3D"
Engine.get_singleton(&"DebugDraw3D").callv("draw_" + draw_shape, args) if is_debug and Engine.has_singleton(DD3D):
Engine.get_singleton(DD3D).callv("draw_" + draw_shape, args)
## Turns res://example.tscn into example.tres (and .scn -> .res) and uid://1234567 into 1234567.tres.
static func convert_path_to_resource_filename(path: String) -> String:
path = ResourceUID.path_to_uid(path)
if path.begins_with("res://"):
return path.get_file().replace(".tscn", ".tres").replace(".scn", ".res")
return path.replace("uid://", "") + ".tres"
func _ready() -> void: func _ready() -> void:
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
if needs_game_setup(): load_startup()
load_initial_setup()
else:
load_main_menu()
register_commands() register_commands()
func load_game(save_data: SaveData, save_slot: int) -> void: func load_game(_save_data: SaveData, _save_slot: int) -> void:
current_save_data = save_data.duplicate() if not await check_computer_hash(save_data.com_hash):
current_save_slot = save_slot return
save_data = _save_data.duplicate()
save_slot = _save_slot
InputManager.capture_mouse() InputManager.capture_mouse()
load_world(current_save_data.current_world, false) load_world(save_data.current_world, false)
func save_game() -> Error: func save_game() -> Error:
if not is_instance_valid(world): if not is_instance_valid(world):
return ERR_DOES_NOT_EXIST return ERR_DOES_NOT_EXIST
var save_slot: String = str(current_save_slot)
var file_name: String = "save_data.tres" var file_name: String = "save_data.tres"
if is_instance_valid(world): if is_instance_valid(world):
current_save_data.current_world = ResourceUID.path_to_uid(world.scene_file_path) save_data.current_world = ResourceUID.path_to_uid(world.scene_file_path)
DirAccess.make_dir_recursive_absolute(SAVE_DIR % save_slot) DirAccess.make_dir_recursive_absolute(SAVE_DIR % save_slot)
return ResourceSaver.save(current_save_data, SAVE_PATH % [save_slot, file_name]) return ResourceSaver.save(save_data, SAVE_PATH % [save_slot, file_name])
func load_world(world_path: String, save_previous: bool = true, load_from_save: bool = true) -> Error: func load_world(world_path: String, save_previous: bool = true, load_from_save: bool = true) -> Error:
@ -87,7 +96,7 @@ func load_world(world_path: String, save_previous: bool = true, load_from_save:
world.change_world_requested.connect(load_world) world.change_world_requested.connect(load_world)
world.world_unload_requested.connect(_on_unload_world_request) world.world_unload_requested.connect(_on_unload_world_request)
world_container.add_child.call_deferred(world) world_holder.add_child.call_deferred(world)
await world.loaded await world.loaded
loading_screen.fade_out() loading_screen.fade_out()
return OK return OK
@ -110,8 +119,7 @@ func save_world_state() -> Error:
world.update_world_state() world.update_world_state()
var world_state: WorldState = world.world_state var world_state: WorldState = world.world_state
var save_slot: String = str(current_save_slot) var file_name: String = convert_path_to_resource_filename(world_state.world_path)
var file_name: String = _get_world_save_file_name_from_world_path(world_state.world_path)
print("File name: ", file_name) print("File name: ", file_name)
print(SAVE_PATH % [save_slot, file_name]) print(SAVE_PATH % [save_slot, file_name])
DirAccess.make_dir_recursive_absolute(SAVE_DIR % save_slot) DirAccess.make_dir_recursive_absolute(SAVE_DIR % save_slot)
@ -120,8 +128,7 @@ func save_world_state() -> Error:
func load_world_state(world_path: String) -> WorldState: func load_world_state(world_path: String) -> WorldState:
var save_slot: String = str(current_save_slot) var file_name: String = convert_path_to_resource_filename(world_path)
var file_name: String = _get_world_save_file_name_from_world_path(world_path)
var file_path: String = SAVE_PATH % [save_slot, file_name] var file_path: String = SAVE_PATH % [save_slot, file_name]
if not ResourceLoader.exists(file_path): if not ResourceLoader.exists(file_path):
return null return null
@ -136,29 +143,18 @@ func load_main_menu() -> void:
_main_menu.load_game_request.connect(load_game) _main_menu.load_game_request.connect(load_game)
_main_menu.quit_request.connect(quit_game) _main_menu.quit_request.connect(quit_game)
main_menu = _main_menu main_menu = _main_menu
world_container.add_child(main_menu) world_holder.add_child(main_menu)
func load_initial_setup() -> void: func load_startup() -> void:
var setup_menu: InitialSetupMenu = load(initial_setup_path).instantiate() var startup: StartupScene = load(startup_scene).instantiate()
setup_menu.setup_finished.connect(func() -> void: startup.startup_finished.connect(func() -> void:
setup_menu.queue_free() startup.queue_free()
var config: ConfigFile = Utils.load_config(MainMenu.USER_SETTINGS_PATH)
config.set_value("save", "needs_setup", false)
config.save(MainMenu.USER_SETTINGS_PATH)
await loading_screen.fade_in(0.0) await loading_screen.fade_in(0.0)
load_main_menu() load_main_menu()
loading_screen.fade_out() loading_screen.fade_out()
) )
add_child(setup_menu) add_child(startup)
func needs_game_setup() -> bool:
var config := ConfigFile.new()
if config.load(MainMenu.USER_SETTINGS_PATH) != OK:
return true
return config.get_value("save", "needs_setup", true)
func unload_main_menu() -> void: func unload_main_menu() -> void:
@ -194,13 +190,3 @@ func _on_unload_world_request(do_save: bool) -> void:
await load_main_menu() await load_main_menu()
loading_screen.fade_out() loading_screen.fade_out()
func _get_world_save_file_name_from_world_path(world_path: String) -> String:
world_path = ResourceUID.path_to_uid(world_path)
print("World path: ", world_path)
if world_path.begins_with("res://"):
return world_path.get_file().replace(".tscn", ".tres").replace(".scn", ".res")
return world_path.replace("uid://", "") + ".tres"

View File

@ -7,7 +7,7 @@
script = ExtResource("1_7uq6d") script = ExtResource("1_7uq6d")
metadata/_custom_type_script = "uid://cl1u038dbrou2" metadata/_custom_type_script = "uid://cl1u038dbrou2"
[node name="WorldContainer" type="Node" parent="." unique_id=1835125942] [node name="WorldHolder" type="Node" parent="." unique_id=1835125942]
unique_name_in_owner = true unique_name_in_owner = true
[node name="CanvasLayer" type="CanvasLayer" parent="." unique_id=919699501] [node name="CanvasLayer" type="CanvasLayer" parent="." unique_id=919699501]

View File

@ -103,7 +103,7 @@ func _process_input(delta: float) -> void:
if not using_controller and mouse_acceleration: if not using_controller and mouse_acceleration:
rotational_velocity = _lerp_rotational_velocity(get_sensitivity() / 10, mouse_friction, delta) rotational_velocity = _lerp_rotational_velocity(get_sensitivity() / 10, mouse_friction, delta)
elif using_controller and controller_acceleration: elif using_controller and controller_acceleration:
rotational_velocity = _lerp_rotational_velocity(get_sensitivity(), controller_friction,delta) rotational_velocity = _lerp_rotational_velocity(get_sensitivity(), controller_friction, delta)
apply_rotation(rotational_velocity) apply_rotation(rotational_velocity)
else: else:
rotational_velocity = Vector2.ZERO rotational_velocity = Vector2.ZERO

View File

@ -1,6 +1,9 @@
class_name Hud class_name Hud
extends CanvasLayer extends CanvasLayer
const _CROSSHAIR_ANIM_TO_DOT: StringName = &"to_dot"
const _CROSSHAIR_ANIM_TO_HOLLOW: StringName = &"to_hollow"
@export var interaction_ray: InteractionRay: set = set_interaction_ray @export var interaction_ray: InteractionRay: set = set_interaction_ray
@onready var interaction_prompt: RichTextLabel = $InteractionPrompt @onready var interaction_prompt: RichTextLabel = $InteractionPrompt
@ -8,7 +11,7 @@ extends CanvasLayer
func _ready() -> void: func _ready() -> void:
crosshair.play(&"to_dot", 1.0, true) crosshair.play(_CROSSHAIR_ANIM_TO_DOT, 1.0, true)
func set_interaction_ray(ray: InteractionRay) -> void: func set_interaction_ray(ray: InteractionRay) -> void:
@ -27,15 +30,15 @@ func _on_interaction_ray_focused(area: InteractionArea) -> void:
if is_instance_valid(area): if is_instance_valid(area):
area.hint_prompts_changed.connect(_populate_focused_hints.bind(area)) area.hint_prompts_changed.connect(_populate_focused_hints.bind(area))
_populate_focused_hints(area) _populate_focused_hints(area)
_play_crosshair_anim(&"to_hollow") _play_crosshair_anim(_CROSSHAIR_ANIM_TO_HOLLOW)
else: else:
interaction_prompt.clear() interaction_prompt.clear()
_play_crosshair_anim(&"to_dot") _play_crosshair_anim(_CROSSHAIR_ANIM_TO_DOT)
func _on_interaction_ray_focus_lost(area: InteractionArea) -> void: func _on_interaction_ray_focus_lost(area: InteractionArea) -> void:
interaction_prompt.clear() interaction_prompt.clear()
_play_crosshair_anim(&"to_dot") _play_crosshair_anim(_CROSSHAIR_ANIM_TO_DOT)
if is_instance_valid(area): if is_instance_valid(area):
area.hint_prompts_changed.disconnect(_populate_focused_hints) area.hint_prompts_changed.disconnect(_populate_focused_hints)

View File

@ -10,8 +10,6 @@ const ACTION_MOVE_FORWARD: StringName = &"move_forward"
const ACTION_MOVE_BACKWARD: StringName = &"move_backward" const ACTION_MOVE_BACKWARD: StringName = &"move_backward"
const ACTION_INTERACT: StringName = &"interact" const ACTION_INTERACT: StringName = &"interact"
static var player: PlayerCharacter
@export var flashlight_manager: FlashlightManager @export var flashlight_manager: FlashlightManager
@export_group("Crouching") @export_group("Crouching")
@ -41,10 +39,6 @@ var _flying: bool = false
@onready var flashlight: Flashlight = %Flashlight @onready var flashlight: Flashlight = %Flashlight
func _init() -> void:
player = self
func _exit_tree() -> void: func _exit_tree() -> void:
LimboConsole.unregister_command("player_fly") LimboConsole.unregister_command("player_fly")
LimboConsole.remove_alias("noclip") LimboConsole.remove_alias("noclip")
@ -112,13 +106,11 @@ func _physics_process(delta: float) -> void:
SPrint.print_msgf( SPrint.print_msgf(
"Player on floor: %s (was on floor: %s)\nPlayer Horizontal-Velocity: %s\nPlayer Vertical-Velocity: %s" "Player on floor: %s (was on floor: %s)\nPlayer Horizontal-Velocity: %s\nPlayer Vertical-Velocity: %s"
% [is_on_floor(), was_on_floor, (velocity * Utils.VEC3_HOR).length(), velocity.y], % [is_on_floor(), was_on_floor, (velocity * Utils.VEC3_HOR).length(), velocity.y], true)
true,
)
func align(with_node: Node3D, reset_velocity: bool = true) -> void: func align(with_node: Node3D, reset_velocity: bool = true) -> void:
assert(is_instance_valid(with_node), "with_node is invalid.") assert(is_instance_valid(with_node), "'with_node' is invalid.")
apply_orientation(with_node.global_transform) apply_orientation(with_node.global_transform)
if reset_velocity: if reset_velocity:

View File

@ -61,7 +61,7 @@ light_surface_material = SubResource("ShaderMaterial_nqu5b")
[node name="StudioSpotLight" parent="." unique_id=449918207 instance=ExtResource("1_3cilv")] [node name="StudioSpotLight" parent="." unique_id=449918207 instance=ExtResource("1_3cilv")]
[node name="Cylinder" parent="StudioSpotLight" index="0" unique_id=142845931] [node name="Cylinder" parent="StudioSpotLight" index="0" unique_id=35052532]
surface_material_override/1 = SubResource("ShaderMaterial_nqu5b") surface_material_override/1 = SubResource("ShaderMaterial_nqu5b")
[node name="StudioSpotLightFraming01" parent="." unique_id=2007355874 instance=ExtResource("3_otype")] [node name="StudioSpotLightFraming01" parent="." unique_id=2007355874 instance=ExtResource("3_otype")]

View File

@ -30,9 +30,8 @@ func _unhandled_input(event: InputEvent) -> void:
func try_turn_on() -> void: func try_turn_on() -> void:
if is_instance_valid(manager): if is_instance_valid(manager) and manager.is_drained():
if manager.is_drained(): return
return
turn_on() turn_on()

View File

@ -42,7 +42,7 @@ func _exit_tree() -> void:
func _ready() -> void: func _ready() -> void:
world = await _world_proxy.wait_for_world() world = await _world_proxy.wait_for_world()
_instance_persister= InstancePersister.new(self, world) _instance_persister = InstancePersister.new(self, world)
power = _instance_persister.get_property(&"power", initial_power) power = _instance_persister.get_property(&"power", initial_power)
is_flashlight_active = _instance_persister.get_property(&"is_flashlight_active", is_flashlight_active) is_flashlight_active = _instance_persister.get_property(&"is_flashlight_active", is_flashlight_active)

View File

@ -10,14 +10,14 @@ enum DrainModes {
@export var allowed_bodies: Array[Node3D] @export var allowed_bodies: Array[Node3D]
@export var trigger_once: bool = true @export var trigger_once: bool = true
var world: World
var _world_proxy: WorldProxy
@export var drain_mode := DrainModes.SET @export var drain_mode := DrainModes.SET
@export var drain_value: float = 0.3 @export var drain_value: float = 0.3
@export var change_manager_drain_mode: bool = false @export var change_manager_drain_mode: bool = false
@export var manager_drain_mode := FlashlightManager.DrainModes.MANUAL @export var manager_drain_mode := FlashlightManager.DrainModes.MANUAL
var world: World
var _world_proxy: WorldProxy
func _enter_tree() -> void: func _enter_tree() -> void:
_world_proxy = WorldProxy.new() _world_proxy = WorldProxy.new()

View File

@ -1,6 +1,7 @@
class_name FusePickup class_name FusePickup
extends Node3D extends Node3D
signal picked_up
@export var power_box: PowerBox @export var power_box: PowerBox
@ -23,4 +24,5 @@ func _ready() -> void:
func _on_picked_up() -> void: func _on_picked_up() -> void:
power_box.on_fuse_picked_up() power_box.on_fuse_picked_up()
InstancePersister.new(self, world).set_property(&"is_collected", true) InstancePersister.new(self, world).set_property(&"is_collected", true)
picked_up.emit()
queue_free() queue_free()

View File

@ -80,10 +80,10 @@ func _on_interaction_interacted() -> void:
#start_minigame() #start_minigame()
pass pass
if not is_active: if is_active:
turn_on()
else:
turn_off() turn_off()
else:
turn_on()
# TODO # TODO

View File

@ -4,9 +4,8 @@ extends Control
signal load_game_request(save_data: SaveData, slot_index: int) signal load_game_request(save_data: SaveData, slot_index: int)
signal quit_request signal quit_request
const USER_SETTINGS_PATH: String = "user://settings.ini"
const SAVES_DIR: String = "user://saves/" const SAVES_DIR: String = "user://saves/"
const SAVE_DATA_PATH: String = SAVES_DIR + "%s/save_data.tres" const SAVE_DATA_PATH: String = SAVES_DIR + "%d/save_data.tres"
@onready var continue_button: Button = %ContinueButton @onready var continue_button: Button = %ContinueButton
@onready var load_button: Button = %LoadButton @onready var load_button: Button = %LoadButton
@ -27,7 +26,7 @@ static func get_save_slots() -> PackedInt32Array:
var index: int = 0 var index: int = 0
while index < dirs.size(): while index < dirs.size():
var dir_name: String = dirs[index] var dir_name: String = dirs.get(index)
if dir_name.is_valid_int() and not (dir_name.begins_with("+") or dir_name.begins_with("-")): if dir_name.is_valid_int() and not (dir_name.begins_with("+") or dir_name.begins_with("-")):
index += 1 index += 1
else: else:
@ -73,7 +72,7 @@ func _ready() -> void:
func has_last_save_slot() -> bool: func has_last_save_slot() -> bool:
var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH) var config: ConfigFile = Utils.load_config(Game.USER_SETTINGS_PATH)
return config.has_section_key("save", "last_slot") return config.has_section_key("save", "last_slot")
@ -88,7 +87,7 @@ func populate_save_entries() -> void:
func _on_continue_pressed() -> void: func _on_continue_pressed() -> void:
var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH) var config: ConfigFile = Utils.load_config(Game.USER_SETTINGS_PATH)
var slot: int = config.get_value("save", "last_slot", 0) var slot: int = config.get_value("save", "last_slot", 0)
var path: String = get_save_path(slot) var path: String = get_save_path(slot)
@ -100,7 +99,7 @@ func _on_continue_pressed() -> void:
return return
config.set_value("save", "last_slot", slot) config.set_value("save", "last_slot", slot)
config.save(USER_SETTINGS_PATH) config.save(Game.USER_SETTINGS_PATH)
var save_data: SaveData = load(path) var save_data: SaveData = load(path)
load_game_request.emit(save_data, slot) load_game_request.emit(save_data, slot)
@ -116,9 +115,9 @@ func _on_new_game_pressed() -> void:
var slot_index: int = get_unique_save_slot_index() var slot_index: int = get_unique_save_slot_index()
load_game_request.emit(save_data, slot_index) load_game_request.emit(save_data, slot_index)
var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH) var config: ConfigFile = Utils.load_config(Game.USER_SETTINGS_PATH)
config.set_value("save", "last_slot", slot_index) config.set_value("save", "last_slot", slot_index)
config.save(USER_SETTINGS_PATH) config.save(Game.USER_SETTINGS_PATH)
func _on_options_pressed() -> void: func _on_options_pressed() -> void:
@ -130,9 +129,9 @@ func _on_quit_pressed() -> void:
func _on_save_entry_pressed(save_slot: int) -> void: func _on_save_entry_pressed(save_slot: int) -> void:
var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH) var config: ConfigFile = Utils.load_config(Game.USER_SETTINGS_PATH)
config.set_value("save", "last_slot", save_slot) config.set_value("save", "last_slot", save_slot)
config.save(USER_SETTINGS_PATH) config.save(Game.USER_SETTINGS_PATH)
var save_path: String = get_save_path(save_slot) var save_path: String = get_save_path(save_slot)

View File

@ -60,7 +60,11 @@ func _ready() -> void:
controller_setup_menu.confirmed.connect(_on_vibration_config_confirmed) controller_setup_menu.confirmed.connect(_on_vibration_config_confirmed)
controller_vibrations.toggled.connect(_on_controller_vibrations_toggled) controller_vibrations.toggled.connect(_on_controller_vibrations_toggled)
visibility_changed.connect(func() -> void: if is_visible_in_tree(): update_controls_from_settings()) visibility_changed.connect(
func() -> void:
if is_visible_in_tree():
update_controls_from_settings()
)
set_process(false) set_process(false)
@ -82,8 +86,10 @@ func close() -> void:
func update_controls_from_settings() -> void: func update_controls_from_settings() -> void:
# Gameplay # Gameplay
headbobbing.set_pressed_no_signal(SettingsHandler.get_setting("gameplay", "headbobbing", true)) var enabled: bool = SettingsHandler.get_setting("gameplay", "headbobbing", true)
headbobbing_slider.set_value_no_signal(SettingsHandler.get_setting("gameplay", "headbobbing_multiplier", 1.0)) var multiplier: float = SettingsHandler.get_setting("gameplay", "headbobbing_multiplier", 1.0)
headbobbing.set_pressed_no_signal(enabled)
headbobbing_slider.set_value_no_signal(multiplier)
# Localization # Localization
match SettingsHandler.get_setting("localization", "language", "en_US"): match SettingsHandler.get_setting("localization", "language", "en_US"):

View File

@ -41,7 +41,8 @@ func set_slider(new_slider: Slider) -> void:
func _on_value_changed(new_value: float) -> void: func _on_value_changed(new_value: float) -> void:
var value: float = new_value var value: float = new_value
if do_remap: if do_remap:
value = remap(value, remap_initial_min, remap_initial_max, remap_output_min, remap_output_max) value = remap(value, remap_initial_min, remap_initial_max,
remap_output_min, remap_output_max)
var new_text: String = str(value).pad_zeros(pad_zeros).pad_decimals(pad_decimals) var new_text: String = str(value).pad_zeros(pad_zeros).pad_decimals(pad_decimals)
set_text(str(prefix, positive_prefix if value >= 0.0 else negative_prefix, new_text, suffix)) set_text(str(prefix, positive_prefix if value >= 0.0 else negative_prefix, new_text, suffix))

View File

@ -10,7 +10,7 @@ var is_testing_weak: bool = false
var is_testing_strong: bool = false var is_testing_strong: bool = false
var _duration_idx: int = 0 var _duration_idx: int = 0
@onready var vibration_component: VibrationComponent = %VibrationComponent @onready var rumble_component: RumbleComponent = %RumbleComponent
@onready var repeat_vibration_timer: Timer = %RepeatVibrationTimer @onready var repeat_vibration_timer: Timer = %RepeatVibrationTimer
@onready var min_duration_slider: HSlider = %MinDurationSlider @onready var min_duration_slider: HSlider = %MinDurationSlider
@ -22,9 +22,6 @@ var _duration_idx: int = 0
func _ready() -> void: func _ready() -> void:
_on_visibility_changed()
visibility_changed.connect(_on_visibility_changed)
repeat_vibration_timer.timeout.connect(_test_vibration) repeat_vibration_timer.timeout.connect(_test_vibration)
min_weak_magnitude.focus_entered.connect(_on_weak_slider_focused) min_weak_magnitude.focus_entered.connect(_on_weak_slider_focused)
@ -32,25 +29,25 @@ func _ready() -> void:
min_strong_magnitude.focus_entered.connect(_on_strong_slider_focused) min_strong_magnitude.focus_entered.connect(_on_strong_slider_focused)
min_strong_magnitude.focus_exited.connect(_on_strong_slider_unfocused) min_strong_magnitude.focus_exited.connect(_on_strong_slider_unfocused)
min_weak_magnitude.grab_focus()
_on_weak_slider_focused()
min_weak_magnitude.value_changed.connect(_on_min_weak_magnitude_value_changed) min_weak_magnitude.value_changed.connect(_on_min_weak_magnitude_value_changed)
min_strong_magnitude.value_changed.connect(_on_min_strong_magnitude_value_changed) min_strong_magnitude.value_changed.connect(_on_min_strong_magnitude_value_changed)
confirm_button.pressed.connect(confirm) confirm_button.pressed.connect(confirm)
visibility_changed.connect(_on_visibility_changed)
_on_visibility_changed()
func confirm() -> void: func confirm() -> void:
SettingsHandler.set_setting( SettingsHandler.set_setting(
"controls", "controls",
"vibration_min_weak_threshold", "vibration_min_weak_threshold",
VibrationComponent.min_weak_magnitude_threshold, RumbleConductor.min_weak_magnitude_threshold,
false false
) )
SettingsHandler.set_setting( SettingsHandler.set_setting(
"controls", "controls",
"vibration_min_strong_threshold", "vibration_min_strong_threshold",
VibrationComponent.min_strong_magnitude_threshold, RumbleConductor.min_strong_magnitude_threshold,
true true
) )
@ -58,27 +55,30 @@ func confirm() -> void:
func _test_vibration() -> void: func _test_vibration() -> void:
vibration_component.weak_magnitude = MIN_WEAK_MAGNITUDE * float(is_testing_weak) # If is_testing_weak and is_testing_strong are both false, vibrate both together.
vibration_component.strong_magnitude = MIN_STRONG_MAGNITUDE * float(is_testing_strong) var weak_enabled: bool = is_testing_weak or (not is_testing_weak and not is_testing_strong)
var strong_enabled: bool = is_testing_strong or (not is_testing_weak and not is_testing_strong)
rumble_component.data.weak_intensity = MIN_WEAK_MAGNITUDE * float(weak_enabled)
rumble_component.data.strong_intensity = MIN_STRONG_MAGNITUDE * float(strong_enabled)
match _duration_idx: match _duration_idx:
5: 5:
vibration_component.duration = 0.75 rumble_component.data.duration = 0.75
_: _:
vibration_component.duration = 0.15 rumble_component.data.duration = 0.15
#1: #1:
#vibration_component.duration = 0.35 #rumble_component.duration = 0.35
#2: #2:
#vibration_component.duration = 0.75 #rumble_component.duration = 0.75
_duration_idx = posmod(_duration_idx + 1, 6) _duration_idx = posmod(_duration_idx + 1, 6)
vibration_component.vibrate() rumble_component.vibrate()
_vibrate_texture() _vibrate_texture()
func _vibrate_texture() -> void: func _vibrate_texture() -> void:
var tween: Tween = create_tween().set_loops(maxi(int(vibration_component.duration / 0.1), 1)) var tween: Tween = create_tween().set_loops(maxi(int(rumble_component.data.duration / 0.1), 1))
tween.tween_property(vibrate_texture, ^"rotation_degrees", -5.0, 0.05) tween.tween_property(vibrate_texture, ^"rotation_degrees", -5.0, 0.05)
tween.tween_property(vibrate_texture, ^"rotation_degrees", 5.0, 0.05) tween.tween_property(vibrate_texture, ^"rotation_degrees", 5.0, 0.05)
await tween.finished await tween.finished
@ -91,17 +91,20 @@ func _on_visibility_changed() -> void:
repeat_vibration_timer.start() repeat_vibration_timer.start()
process_mode = Node.PROCESS_MODE_INHERIT process_mode = Node.PROCESS_MODE_INHERIT
min_weak_magnitude.grab_focus() min_weak_magnitude.grab_focus()
min_weak_magnitude.set_value_no_signal(RumbleConductor.min_weak_magnitude_threshold)
min_strong_magnitude.set_value_no_signal(RumbleConductor.min_strong_magnitude_threshold)
else: else:
repeat_vibration_timer.stop() repeat_vibration_timer.stop()
process_mode = Node.PROCESS_MODE_DISABLED process_mode = Node.PROCESS_MODE_DISABLED
func _on_min_weak_magnitude_value_changed(value: float) -> void: func _on_min_weak_magnitude_value_changed(value: float) -> void:
VibrationComponent.min_weak_magnitude_threshold = value RumbleConductor.min_weak_magnitude_threshold = value
func _on_min_strong_magnitude_value_changed(value: float) -> void: func _on_min_strong_magnitude_value_changed(value: float) -> void:
VibrationComponent.min_strong_magnitude_threshold = value RumbleConductor.min_strong_magnitude_threshold = value
func _on_weak_slider_focused() -> void: func _on_weak_slider_focused() -> void:

View File

@ -1,8 +1,12 @@
[gd_scene format=3 uid="uid://dak6f2xw3mgjj"] [gd_scene format=3 uid="uid://dak6f2xw3mgjj"]
[ext_resource type="Script" uid="uid://cs2yn6o8vsp4a" path="res://src/ui/setup/controller/controller_setup_menu.gd" id="1_ee61x"] [ext_resource type="Script" uid="uid://cs2yn6o8vsp4a" path="res://src/ui/setup/controller/controller_setup_menu.gd" id="1_ee61x"]
[ext_resource type="Script" uid="uid://bbwtct3hoxwws" path="res://src/core/vibration_component.gd" id="2_6beo4"] [ext_resource type="Script" uid="uid://bbwtct3hoxwws" path="res://src/core/controller_rumble/rumble_component.gd" id="2_6beo4"]
[ext_resource type="Texture2D" uid="uid://g85advbc1vrw" path="res://addons/controller_icons/assets/xboxseries/diagram_simple.png" id="3_6beo4"] [ext_resource type="Texture2D" uid="uid://g85advbc1vrw" path="res://addons/controller_icons/assets/xboxseries/diagram_simple.png" id="3_6beo4"]
[ext_resource type="Script" uid="uid://dwwingj0dsxdg" path="res://src/core/controller_rumble/rumble_config.gd" id="3_sa2mc"]
[sub_resource type="Resource" id="Resource_gtqq5"]
script = ExtResource("3_sa2mc")
[node name="ControllerSetupMenu" type="Control" unique_id=1563422179] [node name="ControllerSetupMenu" type="Control" unique_id=1563422179]
layout_mode = 3 layout_mode = 3
@ -14,9 +18,10 @@ grow_vertical = 2
script = ExtResource("1_ee61x") script = ExtResource("1_ee61x")
metadata/_custom_type_script = "uid://cs2yn6o8vsp4a" metadata/_custom_type_script = "uid://cs2yn6o8vsp4a"
[node name="VibrationComponent" type="Node" parent="." unique_id=193091150] [node name="RumbleComponent" type="Node" parent="." unique_id=193091150]
unique_name_in_owner = true unique_name_in_owner = true
script = ExtResource("2_6beo4") script = ExtResource("2_6beo4")
data = SubResource("Resource_gtqq5")
weak_magnitude = 0.05 weak_magnitude = 0.05
strong_magnitude = 0.05 strong_magnitude = 0.05
metadata/_custom_type_script = "uid://bbwtct3hoxwws" metadata/_custom_type_script = "uid://bbwtct3hoxwws"
@ -25,10 +30,13 @@ metadata/_custom_type_script = "uid://bbwtct3hoxwws"
unique_name_in_owner = true unique_name_in_owner = true
[node name="Label" type="Label" parent="." unique_id=684594410] [node name="Label" type="Label" parent="." unique_id=684594410]
layout_mode = 0 layout_mode = 1
offset_right = 384.0 anchors_preset = 10
offset_bottom = 49.0 anchor_right = 1.0
offset_bottom = 23.0
grow_horizontal = 2
text = "Adjust the sliders, until you can bearly feel the vibrations." text = "Adjust the sliders, until you can bearly feel the vibrations."
horizontal_alignment = 1
[node name="RichTextLabel" type="RichTextLabel" parent="." unique_id=1446444531] [node name="RichTextLabel" type="RichTextLabel" parent="." unique_id=1446444531]
visible = false visible = false

View File

@ -7,12 +7,25 @@ signal setup_finished
@onready var controller_recommended: Control = %ControllerRecommended @onready var controller_recommended: Control = %ControllerRecommended
@onready var rhythm_setup_menu: RhythmSetupMenu = %RhythmSetupMenu @onready var rhythm_setup_menu: RhythmSetupMenu = %RhythmSetupMenu
@onready var controller_setup_menu: ControllerSetupMenu = %ControllerSetupMenu @onready var controller_setup_menu: ControllerSetupMenu = %ControllerSetupMenu
@onready var configurable_notice: Label = %ConfigurableNotice
@onready var fade_control: Control = %FadeControl
func _ready() -> void: func _ready() -> void:
rhythm_setup_menu.confirmed.connect(_on_rythm_setup_confirmed) rhythm_setup_menu.confirmed.connect(_on_rythm_setup_confirmed)
controller_setup_menu.confirmed.connect(_on_controller_setup_confirmed) controller_setup_menu.confirmed.connect(_on_controller_setup_confirmed)
visibility_changed.connect(_on_visibility_changed)
_on_visibility_changed()
InputManager.input_method_changed.connect(_on_input_method_changed)
func _on_visibility_changed() -> void:
print("On Visibility Changed. Is Visible: ", is_visible_in_tree())
if not is_visible_in_tree():
return
await get_tree().create_timer(0.5).timeout await get_tree().create_timer(0.5).timeout
controller_recommended.show() controller_recommended.show()
fade_out() fade_out()
@ -21,23 +34,24 @@ func _ready() -> void:
controller_recommended.hide() controller_recommended.hide()
controller_setup_menu.show() controller_setup_menu.show()
configurable_notice.show()
fade_out() fade_out()
InputManager.input_method_changed.connect(_on_input_method_changed)
func fade_in() -> void: func fade_in() -> void:
get_viewport().gui_release_focus() get_viewport().gui_release_focus()
foreground_color.mouse_filter = Control.MOUSE_FILTER_STOP foreground_color.mouse_filter = Control.MOUSE_FILTER_STOP
var tween: Tween = create_tween() var tween: Tween = create_tween()
tween.tween_property(foreground_color, ^"color:a", 1.0, 3.0) #tween.tween_property(foreground_color, ^"color:a", 1.0, 3.0)
tween.tween_property(fade_control, ^"modulate:a", 0.0, 3.0)
await tween.finished await tween.finished
func fade_out() -> void: func fade_out() -> void:
foreground_color.mouse_filter = Control.MOUSE_FILTER_IGNORE foreground_color.mouse_filter = Control.MOUSE_FILTER_IGNORE
var tween: Tween = create_tween() var tween: Tween = create_tween()
tween.tween_property(foreground_color, ^"color:a", 0.0, 3.0) #tween.tween_property(foreground_color, ^"color:a", 0.0, 3.0)
tween.tween_property(fade_control, ^"modulate:a", 1.0, 3.0)
func _on_rythm_setup_confirmed() -> void: func _on_rythm_setup_confirmed() -> void:

View File

@ -24,7 +24,17 @@ grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
color = Color(0, 0, 0, 1) color = Color(0, 0, 0, 1)
[node name="ControllerRecommended" type="Control" parent="." unique_id=167704832] [node name="FadeControl" type="Control" parent="." unique_id=2135836694]
unique_name_in_owner = true
modulate = Color(1, 1, 1, 0)
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="ControllerRecommended" type="Control" parent="FadeControl" unique_id=167704832]
unique_name_in_owner = true unique_name_in_owner = true
visible = false visible = false
layout_mode = 1 layout_mode = 1
@ -34,7 +44,7 @@ anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="ControllerRecommended" unique_id=684870312] [node name="VBoxContainer" type="VBoxContainer" parent="FadeControl/ControllerRecommended" unique_id=684870312]
layout_mode = 1 layout_mode = 1
anchors_preset = -1 anchors_preset = -1
anchor_left = 0.15 anchor_left = 0.15
@ -44,17 +54,17 @@ anchor_bottom = 0.97
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="Label" type="Label" parent="ControllerRecommended/VBoxContainer" unique_id=1590844875] [node name="Label" type="Label" parent="FadeControl/ControllerRecommended/VBoxContainer" unique_id=1590844875]
layout_mode = 2 layout_mode = 2
theme_override_font_sizes/font_size = 42 theme_override_font_sizes/font_size = 42
text = "Use of a controller recommended" text = "Use of a controller recommended"
horizontal_alignment = 1 horizontal_alignment = 1
[node name="Control" type="Control" parent="ControllerRecommended/VBoxContainer" unique_id=595986704] [node name="Control" type="Control" parent="FadeControl/ControllerRecommended/VBoxContainer" unique_id=595986704]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="TextureRect" type="TextureRect" parent="ControllerRecommended/VBoxContainer/Control" unique_id=1190977067] [node name="TextureRect" type="TextureRect" parent="FadeControl/ControllerRecommended/VBoxContainer/Control" unique_id=1190977067]
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
@ -68,20 +78,35 @@ texture = ExtResource("4_sr5uw")
expand_mode = 1 expand_mode = 1
stretch_mode = 5 stretch_mode = 5
[node name="RhythmSetupMenu" parent="." unique_id=552044082 instance=ExtResource("2_2gigy")] [node name="RhythmSetupMenu" parent="FadeControl" unique_id=552044082 instance=ExtResource("2_2gigy")]
unique_name_in_owner = true unique_name_in_owner = true
process_mode = 4 process_mode = 4
visible = false visible = false
layout_mode = 1 layout_mode = 1
[node name="ControllerSetupMenu" parent="." unique_id=1563422179 instance=ExtResource("3_sr5uw")] [node name="ControllerSetupMenu" parent="FadeControl" unique_id=1563422179 instance=ExtResource("3_sr5uw")]
unique_name_in_owner = true unique_name_in_owner = true
process_mode = 4 process_mode = 4
visible = false visible = false
layout_mode = 1 layout_mode = 1
[node name="ConfigurableNotice" type="Label" parent="FadeControl" unique_id=2047433102]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 12
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -23.0
grow_horizontal = 2
grow_vertical = 0
text = "You can change this later in the settings."
horizontal_alignment = 1
[node name="ForegroundColor" type="ColorRect" parent="." unique_id=1692094271] [node name="ForegroundColor" type="ColorRect" parent="." unique_id=1692094271]
unique_name_in_owner = true unique_name_in_owner = true
visible = false
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0

View File

@ -32,6 +32,13 @@ func close() -> void:
confirmed.emit() confirmed.emit()
func fade_out_and_stop_setup(duration: float) -> void:
var tween: Tween = create_tween()
tween.tween_property(rhythm_beat, ^"volume_linear", 0.0, duration)
tween.tween_callback(stop_setup)
await tween.finished
func stop_setup() -> void: func stop_setup() -> void:
rhythm_player.stop() rhythm_player.stop()
_restore_song_playback_data() _restore_song_playback_data()
@ -55,7 +62,8 @@ func _on_visibility_changed() -> void:
if not rhythm_beat.playing: if not rhythm_beat.playing:
_store_song_playback_data() _store_song_playback_data()
user_offset_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "user_offset_ms", 0.0)) var user_offset: float = SettingsHandler.get_setting("audio", "user_offset_ms", 0.0)
user_offset_slider.set_value_no_signal(user_offset)
user_offset_slider.grab_focus() user_offset_slider.grab_focus()
rhythm_player.play() rhythm_player.play()

View File

@ -4,13 +4,24 @@
[ext_resource type="Script" uid="uid://bfpr421kg4s" path="res://src/ui/settings_menu/slider_label.gd" id="2_0yond"] [ext_resource type="Script" uid="uid://bfpr421kg4s" path="res://src/ui/settings_menu/slider_label.gd" id="2_0yond"]
[ext_resource type="Script" uid="uid://bdi06itcm6wfp" path="res://src/core/rhythm/rhythm_player.gd" id="2_jsobq"] [ext_resource type="Script" uid="uid://bdi06itcm6wfp" path="res://src/core/rhythm/rhythm_player.gd" id="2_jsobq"]
[ext_resource type="AudioStream" uid="uid://cwpx80o5yaauf" path="res://src/ui/setup/rhythm/120bpm_beat.ogg" id="2_uupbh"] [ext_resource type="AudioStream" uid="uid://cwpx80o5yaauf" path="res://src/ui/setup/rhythm/120bpm_beat.ogg" id="2_uupbh"]
[ext_resource type="Script" uid="uid://bbwtct3hoxwws" path="res://src/core/vibration_component.gd" id="2_yr7ls"] [ext_resource type="Script" uid="uid://bbwtct3hoxwws" path="res://src/core/controller_rumble/rumble_component.gd" id="2_yr7ls"]
[ext_resource type="Script" uid="uid://co7j2qtqpud6b" path="res://src/core/rhythm/rhythm_listener.gd" id="3_o47te"] [ext_resource type="Script" uid="uid://co7j2qtqpud6b" path="res://src/core/rhythm/rhythm_listener.gd" id="3_o47te"]
[ext_resource type="Script" uid="uid://dwwingj0dsxdg" path="res://src/core/controller_rumble/rumble_config.gd" id="3_xnyx0"]
[ext_resource type="Script" uid="uid://c5mqtmsvgt4e8" path="res://src/core/rhythm/song_info.gd" id="4_7a0hf"] [ext_resource type="Script" uid="uid://c5mqtmsvgt4e8" path="res://src/core/rhythm/song_info.gd" id="4_7a0hf"]
[ext_resource type="Script" uid="uid://cx1nws4t8hs2u" path="res://src/core/rhythm/tempo_section_info.gd" id="5_cribi"] [ext_resource type="Script" uid="uid://cx1nws4t8hs2u" path="res://src/core/rhythm/tempo_section_info.gd" id="5_cribi"]
[ext_resource type="Texture2D" uid="uid://t6ydwif1bo1c" path="res://godot_icon.svg" id="6_cribi"] [ext_resource type="Texture2D" uid="uid://t6ydwif1bo1c" path="res://godot_icon.svg" id="6_cribi"]
[ext_resource type="Script" uid="uid://bvmfdeypbeqsn" path="res://src/core/rhythm/rhythm_property_setter.gd" id="7_0yond"] [ext_resource type="Script" uid="uid://bvmfdeypbeqsn" path="res://src/core/rhythm/rhythm_property_setter.gd" id="7_0yond"]
[sub_resource type="Resource" id="Resource_ej02t"]
script = ExtResource("3_xnyx0")
weak_intensity = 0.5
strong_intensity = 0.0
[sub_resource type="Resource" id="Resource_sp7no"]
script = ExtResource("3_xnyx0")
weak_intensity = 0.9
strong_intensity = 0.5
[sub_resource type="Resource" id="Resource_0yond"] [sub_resource type="Resource" id="Resource_0yond"]
script = ExtResource("4_7a0hf") script = ExtResource("4_7a0hf")
audio_stream = ExtResource("2_uupbh") audio_stream = ExtResource("2_uupbh")
@ -27,14 +38,16 @@ mouse_filter = 2
script = ExtResource("1_la56f") script = ExtResource("1_la56f")
metadata/_custom_type_script = "uid://bvs4uvpewolwc" metadata/_custom_type_script = "uid://bvs4uvpewolwc"
[node name="BeatVibration" type="Node" parent="." unique_id=1560105761] [node name="BeatRumble" type="Node" parent="." unique_id=1560105761]
script = ExtResource("2_yr7ls") script = ExtResource("2_yr7ls")
data = SubResource("Resource_ej02t")
weak_magnitude = 0.5 weak_magnitude = 0.5
strong_magnitude = 0.0 strong_magnitude = 0.0
metadata/_custom_type_script = "uid://bbwtct3hoxwws" metadata/_custom_type_script = "uid://bbwtct3hoxwws"
[node name="BarVibration" type="Node" parent="." unique_id=1199048244] [node name="BarRumble" type="Node" parent="." unique_id=1199048244]
script = ExtResource("2_yr7ls") script = ExtResource("2_yr7ls")
data = SubResource("Resource_sp7no")
weak_magnitude = 0.9 weak_magnitude = 0.9
strong_magnitude = 0.5 strong_magnitude = 0.5
metadata/_custom_type_script = "uid://bbwtct3hoxwws" metadata/_custom_type_script = "uid://bbwtct3hoxwws"
@ -155,5 +168,14 @@ texture = ExtResource("6_cribi")
layout_mode = 2 layout_mode = 2
texture = ExtResource("6_cribi") texture = ExtResource("6_cribi")
[connection signal="bar_tick" from="RhythmListener" to="BarVibration" method="vibrate" flags=3 unbinds=1] [node name="Label" type="Label" parent="." unique_id=1207688688]
[connection signal="beat_tick" from="RhythmListener" to="BeatVibration" method="vibrate" unbinds=1] layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 23.0
grow_horizontal = 2
text = "Adjust the slider so that everything syncs up with the beat."
horizontal_alignment = 1
[connection signal="bar_ticked" from="RhythmListener" to="BarRumble" method="vibrate" flags=3 unbinds=1]
[connection signal="beat_ticked" from="RhythmListener" to="BeatRumble" method="vibrate" unbinds=1]

View File

@ -0,0 +1,30 @@
shader_type canvas_item;
uniform sampler2D color_ramp;
uniform float fog_amount = 1.0;
uniform sampler2D fog_texture : repeat_enable;
uniform sampler2D fog_overlay_texture : repeat_enable;
global uniform float time;
void vertex() {
// Called for every vertex the material is visible on.
}
void fragment() {
// Called for every pixel the material is visible on.
if (UV.y > 0.5)
{
vec4 fog = texture(fog_texture, vec2(UV.x + time * 0.02, UV.y));
vec4 fog_overlay = texture(fog_overlay_texture, vec2(UV.x + time * -0.015, UV.y + time * 0.0025));
fog = mix(fog, fog_overlay, 0.5);
vec3 sampled_color = texture(color_ramp, vec2(fog.r, 0.0)).rgb;
fog = mix(fog, vec4(sampled_color, 0.0), 1.0);
COLOR += fog * ((abs(0.5 - UV.x) * 2.0) * (abs(0.5 - UV.y) * 2.0)) * fog_amount;
}
}
//void light() {
// // Called for every pixel for every light affecting the CanvasItem.
// // Uncomment to replace the default light processing function with this one.
//}

View File

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

View File

@ -0,0 +1,52 @@
class_name StartupScene
extends Control
signal startup_finished
@onready var menu_animations: AnimationPlayer = %MenuAnimations
@onready var initial_setup_menu: InitialSetupMenu = %InitialSetupMenu
@onready var splash_screen_button: Button = %SplashScreenButton
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
splash_screen_button.pressed.connect(_on_splash_confirmed)
menu_animations.play(&"fade_splash_in")
func needs_game_setup() -> bool:
var config := ConfigFile.new()
if config.load(Game.USER_SETTINGS_PATH) != OK:
return true
return config.get_value("save", "needs_initial_setup", true)
func show_initial_setup_menu() -> void:
initial_setup_menu.setup_finished.connect(_on_initial_setup_menu_setup_finished)
initial_setup_menu.show()
func _on_splash_confirmed() -> void:
splash_screen_button.disabled = true
if needs_game_setup():
menu_animations.play(&"minimize_splash")
await menu_animations.animation_finished
show_initial_setup_menu()
else:
menu_animations.play(&"fade_out")
await menu_animations.animation_finished
startup_finished.emit()
func _on_initial_setup_menu_setup_finished() -> void:
var config: ConfigFile = Utils.load_config(Game.USER_SETTINGS_PATH)
config.set_value("save", "needs_initial_setup", false)
config.save(Game.USER_SETTINGS_PATH)
menu_animations.play(&"fade_out")
await menu_animations.animation_finished
startup_finished.emit()

View File

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

View File

@ -0,0 +1,491 @@
[gd_scene format=3 uid="uid://0kd77tbv1p3s"]
[ext_resource type="Texture2D" uid="uid://vs1sgal1rlr3" path="res://src/ui/startup/title.png" id="1_8mufd"]
[ext_resource type="Script" uid="uid://o3b1qkysue8d" path="res://src/ui/startup/startup_scene.gd" id="1_b5a2f"]
[ext_resource type="PackedScene" uid="uid://8bxv3c5f8d2j" path="res://src/ui/setup/initial_setup_menu.tscn" id="1_xv1lf"]
[ext_resource type="Shader" uid="uid://cx2yv2l8vbubl" path="res://src/ui/startup/fog_overlay.gdshader" id="2_3kk5a"]
[sub_resource type="Gradient" id="Gradient_b5a2f"]
colors = PackedColorArray(0, 0, 0, 1, 0.18414839, 0.22214463, 0.27734375, 1)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_b5a2f"]
gradient = SubResource("Gradient_b5a2f")
[sub_resource type="FastNoiseLite" id="FastNoiseLite_b5a2f"]
seed = 1
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_3kk5a"]
generate_mipmaps = false
noise = SubResource("FastNoiseLite_b5a2f")
seamless = true
[sub_resource type="FastNoiseLite" id="FastNoiseLite_8mufd"]
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_xv1lf"]
generate_mipmaps = false
noise = SubResource("FastNoiseLite_8mufd")
seamless = true
[sub_resource type="ShaderMaterial" id="ShaderMaterial_qhbau"]
resource_local_to_scene = true
shader = ExtResource("2_3kk5a")
shader_parameter/color_ramp = SubResource("GradientTexture1D_b5a2f")
shader_parameter/fog_amount = 1.0
shader_parameter/fog_texture = SubResource("NoiseTexture2D_xv1lf")
shader_parameter/fog_overlay_texture = SubResource("NoiseTexture2D_3kk5a")
[sub_resource type="Animation" id="Animation_xv1lf"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("LogoSplash/Title:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 1)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("LogoSplash/Label:offset_transform_position_ratio")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(0, 9)]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("LogoSplash/Label:modulate")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 1)]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("LogoSplash/Title:anchor_left")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/4/type = "value"
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/path = NodePath("LogoSplash/Title:anchor_top")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/5/type = "value"
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/path = NodePath("LogoSplash/Title:anchor_right")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [1.0]
}
tracks/6/type = "value"
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/path = NodePath("LogoSplash/Title:anchor_bottom")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [1.0]
}
tracks/7/type = "value"
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/path = NodePath("LogoSplash/Label/SplashScreenButton:disabled")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
tracks/8/type = "value"
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/path = NodePath("BackgroundRect:material:shader_parameter/fog_amount")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [1.0]
}
tracks/9/type = "value"
tracks/9/imported = false
tracks/9/enabled = true
tracks/9/path = NodePath("LogoSplash:modulate")
tracks/9/interp = 1
tracks/9/loop_wrap = true
tracks/9/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 1)]
}
[sub_resource type="Animation" id="Animation_3kk5a"]
resource_name = "fade_out"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("LogoSplash:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("BackgroundRect:material:shader_parameter/fog_amount")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [1.0, 0.0]
}
[sub_resource type="Animation" id="Animation_8mufd"]
resource_name = "fade_splash_in"
length = 8.5
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("LogoSplash/Title:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(4, 6),
"transitions": PackedFloat32Array(4.4382753, 1),
"update": 0,
"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("LogoSplash/Label:offset_transform_position_ratio")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(0, 9)]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("LogoSplash/Label:modulate")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(7.5, 8.5),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("LogoSplash/Title:anchor_left")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/4/type = "value"
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/path = NodePath("LogoSplash/Title:anchor_top")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/5/type = "value"
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/path = NodePath("LogoSplash/Title:anchor_right")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [1.0]
}
tracks/6/type = "value"
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/path = NodePath("LogoSplash/Title:anchor_bottom")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [1.0]
}
tracks/7/type = "value"
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/path = NodePath("LogoSplash/Label/SplashScreenButton:disabled")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/keys = {
"times": PackedFloat32Array(0, 7.71),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [true, false]
}
tracks/8/type = "value"
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/path = NodePath("BackgroundRect:material:shader_parameter/fog_amount")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/keys = {
"times": PackedFloat32Array(0, 4),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, 1.0]
}
[sub_resource type="Animation" id="Animation_b5a2f"]
resource_name = "minimize_splash"
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("LogoSplash/Title:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0.8039216)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("LogoSplash/Title:anchor_left")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, 0.0]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("LogoSplash/Title:anchor_top")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, 0.0]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath("LogoSplash/Title:anchor_right")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [1.0, 1.0]
}
tracks/4/type = "value"
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/path = NodePath("LogoSplash/Title:anchor_bottom")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
"times": PackedFloat32Array(0, 1),
"transitions": PackedFloat32Array(0.5, 1),
"update": 0,
"values": [1.0, 0.35]
}
tracks/5/type = "value"
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/path = NodePath("LogoSplash/Label:modulate")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
"times": PackedFloat32Array(0, 0.375),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_b5a2f"]
_data = {
&"RESET": SubResource("Animation_xv1lf"),
&"fade_out": SubResource("Animation_3kk5a"),
&"fade_splash_in": SubResource("Animation_8mufd"),
&"minimize_splash": SubResource("Animation_b5a2f")
}
[sub_resource type="InputEventAction" id="InputEventAction_mffen"]
action = &"ui_accept"
[sub_resource type="Shortcut" id="Shortcut_qhbau"]
events = [SubResource("InputEventAction_mffen")]
[node name="StartupScene" type="Control" unique_id=40531563]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_b5a2f")
[node name="BackgroundRect" type="ColorRect" parent="." unique_id=9977493]
material = SubResource("ShaderMaterial_qhbau")
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 1)
[node name="MenuAnimations" type="AnimationPlayer" parent="." unique_id=571760447]
unique_name_in_owner = true
libraries/ = SubResource("AnimationLibrary_b5a2f")
[node name="LogoSplash" type="Control" parent="." unique_id=1956225183]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Label" type="RichTextLabel" parent="LogoSplash" unique_id=73966910]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -152.5
offset_top = -11.5
offset_right = 152.5
offset_bottom = 11.5
grow_horizontal = 2
grow_vertical = 2
offset_transform_enabled = true
offset_transform_position_ratio = Vector2(0, 9)
offset_transform_visual_only = false
bbcode_enabled = true
text = "Press [img width=1em]uid://bi8jxa3gcbfdn[/img] to start"
horizontal_alignment = 1
[node name="SplashScreenButton" type="Button" parent="LogoSplash/Label" unique_id=1618938357]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
disabled = true
shortcut = SubResource("Shortcut_qhbau")
flat = true
[node name="Title" type="TextureRect" parent="LogoSplash" unique_id=959861830]
layout_mode = 1
anchors_preset = -1
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_8mufd")
expand_mode = 1
stretch_mode = 5
[node name="Label2" type="Label" parent="LogoSplash" unique_id=430972999]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -334.0
offset_top = -68.5
offset_right = 334.0
offset_bottom = 68.5
grow_horizontal = 2
grow_vertical = 2
theme_override_font_sizes/font_size = 100
text = "Magic N' Stuff"
horizontal_alignment = 1
vertical_alignment = 1
[node name="InitialSetupMenu" parent="." unique_id=1302993740 instance=ExtResource("1_xv1lf")]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = -1
anchor_top = 0.3
[node name="ColorRect" parent="InitialSetupMenu" index="0" unique_id=1648891710]
visible = false
[editable path="InitialSetupMenu"]

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://vs1sgal1rlr3"
path="res://.godot/imported/title.png-f2a8960a23c861534baa8f4e869c6614.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://src/ui/startup/title.png"
dest_files=["res://.godot/imported/title.png-f2a8960a23c861534baa8f4e869c6614.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
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/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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

View File

@ -7,10 +7,9 @@ extends AnimationPlayer
&"free", &"free",
] ]
var EDITOR_PATH: String:
get: return str(get_parent().get_path()) + "/"
var function_calls: Array[Dictionary] = [] var function_calls: Array[Dictionary] = []
var _editor_path: String:
get: return str(get_parent().get_path()) + "/"
func _ready() -> void: func _ready() -> void:
@ -57,7 +56,7 @@ func animation_started_func(_dismiss: Variant) -> void:
if animation.track_get_type(track_index) != Animation.TYPE_METHOD: if animation.track_get_type(track_index) != Animation.TYPE_METHOD:
continue continue
var path: String = EDITOR_PATH + str(animation.track_get_path(track_index)) var path: String = _editor_path + str(animation.track_get_path(track_index))
var track_key_count: int = animation.track_get_key_count(track_index) var track_key_count: int = animation.track_get_key_count(track_index)
for key_index: int in track_key_count: for key_index: int in track_key_count:
var time: float = animation.track_get_key_time(track_index, key_index) var time: float = animation.track_get_key_time(track_index, key_index)

View File

@ -22,7 +22,6 @@ func play_cutscene() -> void:
disable_flashlight() disable_flashlight()
func align_player_with_end_alignment() -> void: func align_player_with_end_alignment() -> void:
if not is_instance_valid(player) or not is_instance_valid(end_alignment): if not is_instance_valid(player) or not is_instance_valid(end_alignment):
return return

View File

@ -8,5 +8,3 @@ shader = ExtResource("1_3pgam")
shader_parameter/base_color = Color(0.3, 1, 0.3, 0.15) shader_parameter/base_color = Color(0.3, 1, 0.3, 0.15)
shader_parameter/edge_color = Color(1, 1, 0.3, 0.8) shader_parameter/edge_color = Color(1, 1, 0.3, 0.8)
shader_parameter/edge_power = 3.0 shader_parameter/edge_power = 3.0
shader_parameter/box_center = Vector3(0, 0, 0)
shader_parameter/box_extents = Vector3(0, 0, 0)

View File

@ -19,12 +19,15 @@ extends AnimationPlayerEditorCalls
## is bigger than the [member max_catchup_threshold], ## is bigger than the [member max_catchup_threshold],
## or lower than the [method AudioServer.get_output_latency]. ## or lower than the [method AudioServer.get_output_latency].
enum CatchupBehaviour { enum CatchUpBehaviour {
NONE, ## Don't catch up slowsly. NONE, ## Don't catch up slowly.
SPEED_SCALE_ANIMATION, ## Adjusts the [member AnimationPlayer.speed_scale] property when desynced. SPEED_SCALE_ANIMATION, ## Adjusts the [member AnimationPlayer.speed_scale] property when desynced.
PITCH_SCALE_MUSIC ## Adjusts the [member AudioStreamPlayer.pitch_scale] property when desynced. PITCH_SCALE_MUSIC, ## Adjusts the [member AudioStreamPlayer.pitch_scale] property when desynced.
} }
## If this is set, it start's the [RhythmPlayer] instead of the [member audio_player].[br]
## [member audio_player] should still be set to the same node as in the [member rhythm_player].
@export var rhythm_player: RhythmPlayer: set = set_rhythm_player
## The [AudioStreamPlayer], [AudioStreamPlayer2D] ## The [AudioStreamPlayer], [AudioStreamPlayer2D]
## or [AudioStreamPlayer3D] the music should be played on.[br] ## or [AudioStreamPlayer3D] the music should be played on.[br]
## [b]Should be set to the same player as defined in your animation.[/b] ## [b]Should be set to the same player as defined in your animation.[/b]
@ -35,30 +38,33 @@ var audio_player: NodePath = ^"": set = set_audio_player
@export var music_anim_track_index: int = 0 @export var music_anim_track_index: int = 0
# The offset from 0.0 seconds when the music starts in the animation. # The offset from 0.0 seconds when the music starts in the animation.
#@export var music_anim_offset: float = 0.0 #@export var music_anim_offset: float = 0.0
## @deprecated: Actually reduntant, because this should always happen if above [member max_catchup_threshold]. ## @deprecated: Actually reduntant. This should always happen if above [member max_catchup_threshold].
## The max difference audio and animation can have to each other, ## The max difference audio and animation can have to each other,
## until the audio is snapped back to the animation.[br] ## until the audio is snapped back to the animation.[br]
## However, [member catch_up_behaviour] takes priority, if this is set to value lower than that. ## However, [member catch_up_behaviour] takes priority, if this is set to value lower than that.
@export var max_error: float = 0.375 # 0.02175 @export var max_error: float = 0.375
## Helper variable in case you want to modify the [member AudioStreamPlayer.pitch_scale] property.
@export var audio_pitch_scale: float = 1.0
## Helper variable in case you want to modify the [member AnimationPlayer.speed_scale] property.
@export var anim_speed_scale: float = 1.0
@export_group("Catching up") @export_group("Catching up")
## Defines in what way we want to sync the audio and animation back up.
@export var catch_up_behaviour := CatchupBehaviour.SPEED_SCALE_ANIMATION
#@export var min_catchup_threshold: float = 0.0015 #@export var min_catchup_threshold: float = 0.0015
## Should be set to a value above- or equal to [member max_error]. ## Should be set to a value above- or equal to [member max_error].
@export var max_catchup_threshold: float = 0.375 @export var max_catchup_threshold: float = 0.375
@export_subgroup("Pitch Scale") ## Defines in what way we want to sync the audio and animation back up.
@export var catch_up_behaviour := CatchUpBehaviour.SPEED_SCALE_ANIMATION:
set(value):
catch_up_behaviour = value
notify_property_list_changed()
#@export_subgroup("Pitch Scale")
## The maximum pitch scale change the audio can be set to, when they are desynced. ## The maximum pitch scale change the audio can be set to, when they are desynced.
@export var max_pitch_scale_difference: float = 1.01 @export var max_pitch_scale_difference: float = 1.01
## Helper variable in case you want to modify the [member AudioStreamPlayer.pitch_scale] property. #@export_subgroup("Speed Scale")
@export var custom_pitch_scale: float = 1.0
@export_subgroup("Speed Scale")
## The maximum speed scale change the animation can be set to, when they are desynced. ## The maximum speed scale change the animation can be set to, when they are desynced.
@export var max_speed_scale_difference: float = 1.15 @export var max_speed_scale_difference: float = 1.15
## Helper variable in case you want to modify the [member AnimationPlayer.speed_scale] property.
@export var custom_speed_scale: float = 1.0
var _audio_player: Node var _audio_player: Node
var _timer := Timer.new() var _audio_playback_delay_timer := Timer.new()
var _track_time: float = 0.0 var _track_time: float = 0.0
@ -66,8 +72,8 @@ func _ready() -> void:
super() super()
if not Engine.is_editor_hint(): if not Engine.is_editor_hint():
add_child(_timer) add_child(_audio_playback_delay_timer)
_timer.one_shot = false _audio_playback_delay_timer.one_shot = false
set_audio_player(audio_player) set_audio_player(audio_player)
animation_started.connect(_anim_started) animation_started.connect(_anim_started)
@ -85,6 +91,10 @@ func _process(delta: float) -> void:
var latency: float = AudioServer.get_output_latency() var latency: float = AudioServer.get_output_latency()
var audio_position: float = _audio_player.get_playback_position() var audio_position: float = _audio_player.get_playback_position()
var anim_position: float = current_animation_position + AudioServer.get_time_since_last_mix() var anim_position: float = current_animation_position + AudioServer.get_time_since_last_mix()
if anim_position < _track_time:
return
var difference: float = anim_position - (audio_position + _track_time) var difference: float = anim_position - (audio_position + _track_time)
var abs_difference: float = absf(difference) var abs_difference: float = absf(difference)
@ -95,22 +105,29 @@ func _process(delta: float) -> void:
SPrint.print_msgf("Audio-Pitch: %s\nLatency: %s" % [_audio_player.pitch_scale, latency]) SPrint.print_msgf("Audio-Pitch: %s\nLatency: %s" % [_audio_player.pitch_scale, latency])
if ( if (
not catch_up_behaviour == CatchupBehaviour.NONE not catch_up_behaviour == CatchUpBehaviour.NONE
and abs_difference < max_catchup_threshold and abs_difference < max_catchup_threshold
#and abs_difference > latency #min_catchup_threshold:
): ):
match catch_up_behaviour: match catch_up_behaviour:
CatchupBehaviour.SPEED_SCALE_ANIMATION: CatchUpBehaviour.SPEED_SCALE_ANIMATION:
_sync_anim_speed_scale(difference, delta) _sync_anim_speed_scale(difference, delta)
CatchupBehaviour.PITCH_SCALE_MUSIC: CatchUpBehaviour.PITCH_SCALE_MUSIC:
_sync_music_pitch_scale(latency, difference, delta) _sync_music_pitch_scale(latency, difference, delta)
elif abs_difference > max_error: elif abs_difference > max_error:
#_audio_player.seek(difference) _audio_player.play(anim_position - _track_time + latency)
_audio_player.play(anim_position + _track_time + latency) SPrint.print_msg("Snapped Audio to: %s" % _audio_player.get_playback_position())
SPrint.print_msg("Snapped Audio to: %s" % [_audio_player.get_playback_position()])
else: else:
_audio_player.pitch_scale = custom_pitch_scale _audio_player.pitch_scale = audio_pitch_scale
speed_scale = custom_speed_scale speed_scale = anim_speed_scale
func _validate_property(property: Dictionary) -> void:
if property.name == &"max_pitch_scale_difference":
if catch_up_behaviour != CatchUpBehaviour.PITCH_SCALE_MUSIC:
property.usage = PROPERTY_USAGE_NO_EDITOR
elif property.name == &"max_speed_scale_difference":
if catch_up_behaviour != CatchUpBehaviour.SPEED_SCALE_ANIMATION:
property.usage = PROPERTY_USAGE_NO_EDITOR
func set_audio_player(path: NodePath) -> void: func set_audio_player(path: NodePath) -> void:
@ -120,6 +137,10 @@ func set_audio_player(path: NodePath) -> void:
_audio_player = node if Utils.is_node_audioplayer(node) else null _audio_player = node if Utils.is_node_audioplayer(node) else null
func set_rhythm_player(player: RhythmPlayer) -> void:
rhythm_player = player
#func play_timed( #func play_timed(
#anim_name: StringName, #anim_name: StringName,
#from_marker: StringName = &"", #from_marker: StringName = &"",
@ -129,13 +150,14 @@ func set_audio_player(path: NodePath) -> void:
#from_end: bool = false #from_end: bool = false
#) -> void: #) -> void:
#_anim_started(anim_name) #_anim_started(anim_name)
#play_section_with_markers(anim_name, from_marker, end_marker, custom_blend, custom_speed, from_end) #play_section_with_markers(anim_name, from_marker,
#end_marker, custom_blend, custom_speed, from_end)
func _sync_anim_speed_scale(difference: float, delta: float) -> void: func _sync_anim_speed_scale(difference: float, delta: float) -> void:
var max_speed_scale: float = max_speed_scale_difference var max_speed_scale: float = max_speed_scale_difference
var min_speed_scale: float = custom_speed_scale + (custom_speed_scale - max_speed_scale) var min_speed_scale: float = anim_speed_scale + (anim_speed_scale - max_speed_scale)
var speed: float = custom_speed_scale - difference var speed: float = anim_speed_scale - difference
speed = clampf(speed, min_speed_scale, max_speed_scale) speed = clampf(speed, min_speed_scale, max_speed_scale)
@ -143,19 +165,20 @@ func _sync_anim_speed_scale(difference: float, delta: float) -> void:
speed = move_toward(speed_scale, speed, delta * 0.01) speed = move_toward(speed_scale, speed, delta * 0.01)
#else: #else:
#speed = lerp(speed_scale, speed, 1.0 - pow(0.5, delta)) #0.001 #speed = lerp(speed_scale, speed, 1.0 - pow(0.5, delta)) #0.001
#speed = clampf(speed, min_speed_scale, max_speed_scale)
speed_scale = speed#clampf(speed, min_speed_scale, max_speed_scale) speed_scale = speed * _audio_player.pitch_scale
func _sync_music_pitch_scale(latency: float, difference: float, delta: float) -> void: func _sync_music_pitch_scale(latency: float, difference: float, delta: float) -> void:
var max_pitch_scale: float = max_pitch_scale_difference var max_pitch_scale: float = max_pitch_scale_difference
var min_pitch_scale: float = custom_pitch_scale + (custom_pitch_scale - max_pitch_scale) var min_pitch_scale: float = audio_pitch_scale + (audio_pitch_scale - max_pitch_scale)
var pitch: float = custom_pitch_scale + difference + latency var pitch: float = audio_pitch_scale + difference + latency
pitch = clampf(pitch, min_pitch_scale, max_pitch_scale) pitch = clampf(pitch, min_pitch_scale, max_pitch_scale)
pitch = lerp(_audio_player.pitch_scale, pitch, 1.0 - pow(0.5, delta)) pitch = lerp(_audio_player.pitch_scale, pitch, 1.0 - pow(0.5, delta))
_audio_player.pitch_scale = clampf(pitch, min_pitch_scale, max_pitch_scale) _audio_player.pitch_scale = clampf(pitch, min_pitch_scale, max_pitch_scale) * anim_speed_scale
func _anim_started(anim_name: StringName) -> void: func _anim_started(anim_name: StringName) -> void:
@ -170,24 +193,30 @@ func _anim_started(anim_name: StringName) -> void:
var duration: float = _track_time - current_animation_position var duration: float = _track_time - current_animation_position
if duration > 0.0: if duration > 0.0:
_timer.start(duration) _audio_playback_delay_timer.start(duration)
var _signals: Array[Signal] = await SignalGroup.await_signals( var _signals: Array[Signal] = await SignalGroup.await_signals(
[_timer.timeout, animation_finished, animation_changed], 1 [_audio_playback_delay_timer.timeout, animation_finished, animation_changed], 1
) )
if not _signals.front() == _timer.timeout: if not _signals.front() == _audio_playback_delay_timer.timeout:
return return
#var latency: float = AudioServer.get_output_latency() - AudioServer.get_time_to_next_mix() var playback_position: float = current_animation_position - _track_time
_audio_player.stream = animation.audio_track_get_key_stream(music_anim_track_index, 0)
_audio_player.play(current_animation_position - _track_time)# - latency) if is_instance_valid(rhythm_player):
rhythm_player.play(playback_position)
elif is_instance_valid(_audio_player):
_audio_player.stream = animation.audio_track_get_key_stream(music_anim_track_index, 0)
_audio_player.play(playback_position)
func _anim_finished(_anim_name: StringName) -> void: func _anim_finished(_anim_name: StringName) -> void:
if Engine.is_editor_hint(): if Engine.is_editor_hint():
return return
if is_instance_valid(_audio_player): if is_instance_valid(rhythm_player):
rhythm_player.stop()
elif is_instance_valid(_audio_player):
_audio_player.stop() _audio_player.stop()
_audio_player.stream = null _audio_player.stream = null