diff --git a/game/_development/ayuroo/maestro/_mo_chase_test.tscn b/game/_development/ayuroo/maestro/_mo_chase_test.tscn index 55c17c1..45f5185 100644 --- a/game/_development/ayuroo/maestro/_mo_chase_test.tscn +++ b/game/_development/ayuroo/maestro/_mo_chase_test.tscn @@ -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="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="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"] script = ExtResource("3_7lyik") @@ -30,6 +31,19 @@ height = 0.3 radius = 0.15 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"] size = Vector3(14, 5, 1) @@ -96,6 +110,7 @@ material = ExtResource("7_6lemm") [node name="HeavyBeatVibration" type="Node" parent="." unique_id=1136863204] script = ExtResource("9_yut28") +data = SubResource("Resource_51o3i") duration = 0.1 sync_to_audio = true weak_magnitude = 0.575 @@ -103,6 +118,7 @@ strong_magnitude = 0.75 [node name="LightBeatVibration" type="Node" parent="." unique_id=2006943016] script = ExtResource("9_yut28") +data = SubResource("Resource_ploqb") duration = 0.175 sync_to_audio = true weak_magnitude = 0.375 diff --git a/game/addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.gd b/game/addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.gd index a0e294f..99e57cd 100644 --- a/game/addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.gd +++ b/game/addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.gd @@ -101,24 +101,24 @@ func _on_action(action: Action): var node = anim_player\ .get_node(anim_player.root_node)\ .get_node_or_null(_current_info.path) - + if node: track_path = node.name var property := _current_info.path.get_concatenated_subnames() if not property.is_empty(): track_path += ":" + property - + var msg = 'Delete all tracks with the path "%s"?' % track_path - + if _current_info.type == EditInfo.Type.NODE: msg = 'Delete tracks belonging to the node "%s"?' % track_path - + _show_confirmation(msg, _remove) func _render_edit_dialogue(): var info := _current_info - + if info.type == EditInfo.Type.METHOD_TRACK: is_full_path = false edit_full_path_toggle.disabled = true @@ -126,7 +126,7 @@ func _render_edit_dialogue(): else: edit_full_path_toggle.disabled = false edit_full_path_toggle.visible = true - + if is_full_path: edit_dialogue_input.text = info.path else: @@ -161,7 +161,7 @@ func _on_full_path_toggled(pressed: bool): ## Callback on rename func _on_rename_confirmed(_arg0 = null): var new := edit_dialogue_input.text - + edit_dialogue.hide() if not _anim_player or not _anim_player is AnimationPlayer: 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.popup_centered() confirmation_dialogue.dialog_text = text - diff --git a/game/addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.tscn b/game/addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.tscn index cc1dae8..3f0331a 100644 --- a/game/addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.tscn +++ b/game/addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.tscn @@ -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" 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://ft8r8earybjw" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.gd" id="1_nkqdl"] +[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" 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" size = Vector2i(400, 599) ok_button_text = "Close" script = ExtResource("1_nkqdl") -[node name="VBoxContainer" type="VBoxContainer" parent="."] +[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=960944020] offset_left = 8.0 offset_top = 8.0 offset_right = 392.0 @@ -19,20 +19,20 @@ size_flags_horizontal = 3 size_flags_vertical = 3 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 -[node name="Label" type="Label" parent="VBoxContainer/TreeContainer"] +[node name="Label" type="Label" parent="VBoxContainer/TreeContainer" unique_id=524900720] layout_mode = 2 text = "Properties:" -[node name="FilterInput" type="LineEdit" parent="VBoxContainer/TreeContainer"] +[node name="FilterInput" type="LineEdit" parent="VBoxContainer/TreeContainer" unique_id=309777513] layout_mode = 2 placeholder_text = "Filter..." caret_blink = true 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 custom_minimum_size = Vector2(300, 400) layout_mode = 2 @@ -41,95 +41,95 @@ hide_root = true scroll_horizontal_enabled = false script = ExtResource("2_7pqfs") -[node name="RootNodeContainer" type="VBoxContainer" parent="VBoxContainer"] +[node name="RootNodeContainer" type="VBoxContainer" parent="VBoxContainer" unique_id=646228580] 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 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 -[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 layout_mode = 2 size_flags_horizontal = 3 text = "Change Root" -[node name="EditDialogue" type="ConfirmationDialog" parent="."] +[node name="EditDialogue" type="ConfirmationDialog" parent="." unique_id=2018114141] unique_name_in_owner = true title = "Renaming" position = Vector2i(0, 36) 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_top = 8.0 offset_right = 222.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 -[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 layout_mode = 2 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 layout_mode = 2 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 -[node name="Label" type="Label" parent="EditDialogue/VBoxContainer/HBoxContainer2"] +[node name="Label" type="Label" parent="EditDialogue/VBoxContainer/HBoxContainer2" unique_id=1241184059] layout_mode = 2 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 layout_mode = 2 size_flags_horizontal = 10 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) 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 size_flags_horizontal = 3 size_flags_vertical = 3 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 size_flags_vertical = 3 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 theme_override_constants/margin_left = 2 theme_override_constants/margin_top = 2 theme_override_constants/margin_right = 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 layout_mode = 2 text = "Test Test 2" -[node name="NodeSelectDialogue" type="ConfirmationDialog" parent="."] +[node name="NodeSelectDialogue" type="ConfirmationDialog" parent="." unique_id=1598902309] unique_name_in_owner = true title = "Select a node..." size = Vector2i(616, 557) 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 custom_minimum_size = Vector2(600, 500) offset_left = 8.0 @@ -139,7 +139,7 @@ offset_bottom = 508.0 scroll_horizontal_enabled = false script = ExtResource("3_87x4i") -[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] +[node name="ConfirmationDialog" type="ConfirmationDialog" parent="." unique_id=585191931] unique_name_in_owner = true size = Vector2i(300, 200) ok_button_text = "Delete" diff --git a/game/assets/shaders/rhythm/beating_lightbeam.gdshader b/game/assets/shaders/rhythm/beating_lightbeam.gdshader index 1500ed1..5b1a6f8 100644 --- a/game/assets/shaders/rhythm/beating_lightbeam.gdshader +++ b/game/assets/shaders/rhythm/beating_lightbeam.gdshader @@ -15,10 +15,10 @@ void vertex() { void fragment() { float phase = get_beat_phase(); vec4 tex = texture(albedo, UV); - + float edge = dot(NORMAL, VIEW); edge = clamp(pow(edge, mix(8.0, 2.0, edge_fade)), 0.0, 1.0); - + ALBEDO = mix(tex.rgb, color.rgb, 0.5); ALPHA = max(tex.a * ease(1.0 - phase, flash_exponent) * alpha_multiplier * edge, 0.0); } diff --git a/game/project.godot b/game/project.godot index 6da6e04..ad97b9f 100644 --- a/game/project.godot +++ b/game/project.godot @@ -29,6 +29,7 @@ ControllerIcons="*uid://bdosbfkp568je" ShaderGlobals="*uid://d2lr860r1ysrm" LimboConsole="*uid://dyxornv8vwibg" SettingsHandler="*uid://dj1t6rc6fpiti" +RumbleConductor="*uid://by4n7qykdds0o" [debug] @@ -239,3 +240,7 @@ time={ "type": "float", "value": 0.0 } +is_editor_hint={ +"type": "bool", +"value": false +} diff --git a/game/src/core/aabb_gizmo.gd b/game/src/core/aabb_gizmo.gd index 69ad5c2..f2c472c 100644 --- a/game/src/core/aabb_gizmo.gd +++ b/game/src/core/aabb_gizmo.gd @@ -34,7 +34,6 @@ func _set_handle(id: int, secondary: bool, camera: Camera3D, point: Vector2) -> var position: Vector3 = _aabb.position - func _redraw() -> void: clear() @@ -88,7 +87,7 @@ func _box_commit_handle( func _box_get_handle_name(id: int) -> String: match id: - 0,2,4: + 0, 2, 4: return "" 1: 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: 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 neg_end: float = _initial_size[axis] * -0.5 var pos_end: float = _initial_size[axis] * 0.5 diff --git a/game/src/core/autoloads/input_manager.gd b/game/src/core/autoloads/input_manager.gd index 23ef0c9..bb2cec4 100644 --- a/game/src/core/autoloads/input_manager.gd +++ b/game/src/core/autoloads/input_manager.gd @@ -45,7 +45,9 @@ func release_mouse() -> void: func is_keyboard_or_mouse_event(event: InputEvent) -> bool: return ( - event is InputEventMouseButton or event is InputEventMouseMotion or event is InputEventKey + event is InputEventMouseButton + or event is InputEventMouseMotion + or event is InputEventKey ) diff --git a/game/src/core/autoloads/settings_handler.gd b/game/src/core/autoloads/settings_handler.gd index af6894c..fcf735f 100644 --- a/game/src/core/autoloads/settings_handler.gd +++ b/game/src/core/autoloads/settings_handler.gd @@ -69,8 +69,8 @@ func apply_settings() -> void: if not Engine.is_embedded_in_editor(): DisplayServer.window_set_mode(get_setting("video", "window_mode", DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN)) # Controls - VibrationComponent.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_weak_magnitude_threshold = get_setting("controls", "vibration_min_weak_threshold", 0.0) + RumbleConductor.min_strong_magnitude_threshold = get_setting("controls", "vibration_min_strong_threshold", 0.0) func commit_settings() -> void: diff --git a/game/src/core/autoloads/shader_globals.gd b/game/src/core/autoloads/shader_globals.gd index 423a0f0..678d26c 100644 --- a/game/src/core/autoloads/shader_globals.gd +++ b/game/src/core/autoloads/shader_globals.gd @@ -12,9 +12,16 @@ var user_offset_ms: float = 0.0 func _notification(what: int) -> void: + # Just in case. if what == NOTIFICATION_EDITOR_PRE_SAVE: - # Just in case. 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: diff --git a/game/src/core/vibration_component.gd b/game/src/core/controller_rumble/rumble_component.gd similarity index 80% rename from game/src/core/vibration_component.gd rename to game/src/core/controller_rumble/rumble_component.gd index 62dfb95..81bd976 100644 --- a/game/src/core/vibration_component.gd +++ b/game/src/core/controller_rumble/rumble_component.gd @@ -1,5 +1,5 @@ @tool -class_name VibrationComponent +class_name RumbleComponent extends Node ## 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_strong_vibration: float = 0.0 #static var _vibration_handled: bool = false -static var min_weak_magnitude_threshold: float = 0.0 -static var min_strong_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_duration_threshold: float = 0.0 + +@export var data: RumbleConfig + ## If [code]false[/code], calling [method vibrate] will not start a controller vibration. @export var enabled: bool = true: get = is_enabled, @@ -28,8 +31,7 @@ static var min_strong_magnitude_threshold: float = 0.0 @export var device: int = 0: get = get_device, set = set_device -## Delay the vibration by this amount. -@export var delay: float = 0.0 + @export var sync_to_audio: bool = false #region Editor tooling @@ -65,18 +67,26 @@ var animated_strong_magnitude: float = 0.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 ( - can_vibrate() - and not is_zero_approx(animated_weak_magnitude + animated_strong_magnitude) + can_vibrate() + and not is_zero_approx(animated_weak_magnitude + animated_strong_magnitude) ): - var _weak_magnitude: float = animated_weak_magnitude - var _strong_magnitude: float = animated_strong_magnitude + var config := RumbleConfig.new() + 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) - _strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_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) - 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_strong_magnitude = 0.0 @@ -87,20 +97,20 @@ func vibrate() -> void: if not can_vibrate(): return - if delay > 0.0: - await get_tree().create_timer(delay).timeout - if sync_to_audio: var last_mix_time: float = AudioServer.get_time_since_last_mix() var output_latency: float = AudioServer.get_output_latency() #SPrint.print_msg("Audio Delay: %s" % (last_mix_time + output_latency)) 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 _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) - _strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_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) #duration = maxf(duration, min_duration_threshold) diff --git a/game/src/core/vibration_component.gd.uid b/game/src/core/controller_rumble/rumble_component.gd.uid similarity index 100% rename from game/src/core/vibration_component.gd.uid rename to game/src/core/controller_rumble/rumble_component.gd.uid diff --git a/game/src/core/controller_rumble/rumble_conductor.gd b/game/src/core/controller_rumble/rumble_conductor.gd new file mode 100644 index 0000000..5227161 --- /dev/null +++ b/game/src/core/controller_rumble/rumble_conductor.gd @@ -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 diff --git a/game/src/core/controller_rumble/rumble_conductor.gd.uid b/game/src/core/controller_rumble/rumble_conductor.gd.uid new file mode 100644 index 0000000..70e2669 --- /dev/null +++ b/game/src/core/controller_rumble/rumble_conductor.gd.uid @@ -0,0 +1 @@ +uid://by4n7qykdds0o diff --git a/game/src/core/controller_rumble/rumble_config.gd b/game/src/core/controller_rumble/rumble_config.gd new file mode 100644 index 0000000..453c0ef --- /dev/null +++ b/game/src/core/controller_rumble/rumble_config.gd @@ -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) diff --git a/game/src/core/controller_rumble/rumble_config.gd.uid b/game/src/core/controller_rumble/rumble_config.gd.uid new file mode 100644 index 0000000..5cdbfa7 --- /dev/null +++ b/game/src/core/controller_rumble/rumble_config.gd.uid @@ -0,0 +1 @@ +uid://dwwingj0dsxdg diff --git a/game/src/core/instance_persister.gd b/game/src/core/instance_persister.gd index 2b71267..8e9292c 100644 --- a/game/src/core/instance_persister.gd +++ b/game/src/core/instance_persister.gd @@ -6,7 +6,8 @@ var _node_hash: String = "" 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_hash = str(world.get_path_to(node).hash()) _world = world diff --git a/game/src/core/interactive_loader.gd b/game/src/core/interactive_loader.gd index f3a1b5d..7bfeab2 100644 --- a/game/src/core/interactive_loader.gd +++ b/game/src/core/interactive_loader.gd @@ -26,7 +26,7 @@ func load_threaded(path: String) -> Resource: while ResourceLoader.load_threaded_get_status(path) != ResourceLoader.THREAD_LOAD_LOADED: 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) return null diff --git a/game/src/core/rhythm/rhythm_listener.gd b/game/src/core/rhythm/rhythm_listener.gd index 119220a..0c558a0 100644 --- a/game/src/core/rhythm/rhythm_listener.gd +++ b/game/src/core/rhythm/rhythm_listener.gd @@ -2,8 +2,8 @@ class_name RhythmListener extends Node -signal beat_tick(beat_index: int) -signal bar_tick(bar_index: int) +signal beat_ticked(beat_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_multiplier: float = 1.0 @@ -29,12 +29,12 @@ func _process(_delta: float) -> void: var bar_index: int = floori(bar) if _last_beat_index != beat_index: - on_beat_tick(beat_index) + on_beat_ticked(beat_index) _last_beat_index = beat_index _last_beat_time = RhythmPlayer.song_time + phase_shift if _last_bar_index != bar_index: - on_bar_tick(bar_index) + on_bar_ticked(bar_index) _last_bar_index = bar_index _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) - 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) - bar_tick.emit(bar_index) + bar_ticked.emit(bar_index) diff --git a/game/src/core/rhythm/rhythm_player.gd b/game/src/core/rhythm/rhythm_player.gd index bcfc12d..73fb734 100644 --- a/game/src/core/rhythm/rhythm_player.gd +++ b/game/src/core/rhythm/rhythm_player.gd @@ -1,8 +1,8 @@ class_name RhythmPlayer extends Node -signal beat_tick(beat_index: int) -signal bar_tick(bar_index: int) +signal beat_ticked(beat_index: int) +signal bar_ticked(bar_index: int) signal finished 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]) if _last_beat != beat_index: - beat_tick.emit(beat_index) + beat_ticked.emit(beat_index) _last_beat = beat_index if _last_bar != bar_index: - bar_tick.emit(bar_index) + bar_ticked.emit(bar_index) _last_bar = bar_index diff --git a/game/src/core/rhythm/tempo_map.gd b/game/src/core/rhythm/tempo_map.gd index 6473262..a6bf244 100644 --- a/game/src/core/rhythm/tempo_map.gd +++ b/game/src/core/rhythm/tempo_map.gd @@ -136,7 +136,8 @@ func recalculate_times() -> void: var seconds: float = beat_delta * 60.0 / previous.bpm 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: diff --git a/game/src/core/signal_group.gd b/game/src/core/signal_group.gd index f3a59b3..d7b4bc9 100644 --- a/game/src/core/signal_group.gd +++ b/game/src/core/signal_group.gd @@ -2,7 +2,7 @@ class_name SignalGroup ## @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 _watched_signals: Array[Signal] = [] @@ -36,7 +36,7 @@ func all(signals: Array[Signal], custom_count: int = -1) -> Array[Signal]: _on_signal_complete(sig) ), CONNECT_ONE_SHOT) - await _all_complete + await _all_completed return _signals_in_order @@ -52,7 +52,7 @@ func _on_signal_complete(sig: Signal) -> void: _signals_in_order.append(sig) if _counter == 0: - _all_complete.emit() + _all_completed.emit() #class Probe extends RefCounted: diff --git a/game/src/core/utils.gd b/game/src/core/utils.gd index 4e20b8f..fb3e229 100644 --- a/game/src/core/utils.gd +++ b/game/src/core/utils.gd @@ -99,8 +99,7 @@ static func full_rotation_float( static func get_uid(path: String) -> int: - var uid: int = ResourceUID.text_to_id(ResourceUID.path_to_uid(path)) - return uid + return ResourceUID.text_to_id(ResourceUID.path_to_uid(path)) ## Converts any given uid (as an [int] or [code]uid://[/code] path) diff --git a/game/src/core/world/level/debug_aabb_ingame_material.tres b/game/src/core/world/level/debug_aabb_ingame_material.tres index afa73bb..f08b482 100644 --- a/game/src/core/world/level/debug_aabb_ingame_material.tres +++ b/game/src/core/world/level/debug_aabb_ingame_material.tres @@ -8,5 +8,3 @@ shader = ExtResource("1_1ceck") 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_power = 3.0 -shader_parameter/box_center = Vector3(0, 0, 0) -shader_parameter/box_extents = Vector3(0, 0, 0) diff --git a/game/src/core/world/level/level_streamer.gd b/game/src/core/world/level/level_streamer.gd index 3d6e984..ce0d3fe 100644 --- a/game/src/core/world/level/level_streamer.gd +++ b/game/src/core/world/level/level_streamer.gd @@ -9,18 +9,28 @@ signal loading_finished @export var enabled: bool = true: set = set_enabled @export var threaded_loading: bool = true @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 = &"": set(value): level_id = value update_configuration_warnings() + @export_file("*.tscn", "*.scn") var scene_path: String: set(value): scene_path = value 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("Unload Level", "Clear") var editor_unload_level: Callable = _editor_unload_level + +@export_tool_button("Edit Level", "Edit") +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] = []: set(value): load_aabbs = value @@ -33,11 +43,16 @@ signal loading_finished #clear_gizmos() #for aabb: AABB in load_aabbs: #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 var editor_visualize_aabbs_with_box: bool = true: set = _editor_set_visualize_aabbs_with_box +@export_tool_button("Auto Generate AABB", "CSGBox3D") +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 is_loading_level: bool = false +var editor_force_draw_debug: bool = false + var _unload_time_left: float = 0.0 var _precomputed_aabb: AABB # Big bounding box covering all [_precomputed_aabbs] for faster checking. 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 _transform: Transform3D - # 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 are going down the child chain, + # we want the aabb of each subsequent node to be on the same axis as the parent. if bounds_transform.is_equal_approx(Transform3D()): _transform = node.global_transform else: @@ -128,7 +143,10 @@ func _process(delta: float) -> void: elif not prevent_unloading and is_level_loaded(): _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() @@ -152,14 +170,20 @@ func get_precomputed_aabbs() -> Array[AABB]: func load_level() -> void: _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 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) loaded_level = scene.instantiate() - add_child(loaded_level) + add_child(loaded_level, true) is_loading_level = false loading_finished.emit() @@ -175,7 +199,7 @@ func is_level_loaded() -> bool: 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) return resource as PackedScene @@ -183,6 +207,7 @@ func load_scene_threaded() -> PackedScene: func get_camera() -> Camera3D: 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": return _previous_camera @@ -259,7 +284,7 @@ func _editor_unload_level() -> void: func _editor_process_gizmo() -> void: 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() return diff --git a/game/src/core/world/level/world_proxy.gd b/game/src/core/world/level/world_proxy.gd index fb6cb2b..5ff116d 100644 --- a/game/src/core/world/level/world_proxy.gd +++ b/game/src/core/world/level/world_proxy.gd @@ -1,7 +1,7 @@ class_name WorldProxy extends Node -signal world_changed +signal world_changed(new_world: World) static var world_proxies: Array[WorldProxy] = [] diff --git a/game/src/core/world/world.gd b/game/src/core/world/world.gd index 0ef80d2..85337ed 100644 --- a/game/src/core/world/world.gd +++ b/game/src/core/world/world.gd @@ -22,6 +22,7 @@ signal world_unload_requested(do_save: bool) @export var debug_load_levels_based_on_player_position: bool = true var world_state: WorldState +var game: Game func _exit_tree() -> void: @@ -31,7 +32,7 @@ func _exit_tree() -> void: func _ready() -> void: if debug_enabled: if get_tree().current_scene == self: - _setup_world_debug() + _setup_debug_world_state() if is_instance_valid(world_state): await apply_world_state() @@ -83,8 +84,8 @@ func apply_world_state() -> void: if is_instance_valid(player): player.process_mode = Node.PROCESS_MODE_DISABLED - if is_instance_valid(player) and player.has_method(&"apply_orientation"): - player.apply_orientation(world_state.player_transform) + if player.has_method(&"apply_orientation"): + player.apply_orientation(world_state.player_transform) await _load_levels() @@ -210,7 +211,7 @@ func _load_levels() -> void: 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() if is_instance_valid(debug_player_position): diff --git a/game/src/game.gd b/game/src/game.gd index 9b1aaa1..6033554 100644 --- a/game/src/game.gd +++ b/game/src/game.gd @@ -4,8 +4,8 @@ extends Node ## TODO: sicherungskasten minigames (Kabel verbinden, oder draufhauen und geht wieder) - -const SAVE_DIR: String = "user://saves/%s/" +const USER_SETTINGS_PATH: String = "user://settings.ini" +const SAVE_DIR: String = "user://saves/%d/" # TODO "user://ch1/saves/%d/ # Chapter 1 const SAVE_PATH: String = SAVE_DIR + "%s" 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 main_menu_path: String = "uid://7v62dybcabgw" -var current_save_slot: int = 0 -var current_save_data: SaveData = INITIAL_SAVE_DATA.duplicate() +var save_slot: int = 0 +var save_data: SaveData = INITIAL_SAVE_DATA.duplicate() var world: World 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 @@ -28,39 +30,46 @@ static func _static_init() -> void: static func debug_draw(draw_shape: String, args: Array) -> void: - if is_debug and Engine.has_singleton(&"DebugDraw3D"): - Engine.get_singleton(&"DebugDraw3D").callv("draw_" + draw_shape, args) + const DD3D: StringName = &"DebugDraw3D" + 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: if not Engine.is_editor_hint(): - if needs_game_setup(): - load_initial_setup() - else: - load_main_menu() - + load_startup() register_commands() -func load_game(save_data: SaveData, save_slot: int) -> void: - current_save_data = save_data.duplicate() - current_save_slot = save_slot +func load_game(_save_data: SaveData, _save_slot: int) -> void: + if not await check_computer_hash(save_data.com_hash): + return + + save_data = _save_data.duplicate() + save_slot = _save_slot InputManager.capture_mouse() - load_world(current_save_data.current_world, false) + load_world(save_data.current_world, false) func save_game() -> Error: if not is_instance_valid(world): return ERR_DOES_NOT_EXIST - var save_slot: String = str(current_save_slot) var file_name: String = "save_data.tres" 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) - 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: @@ -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.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 loading_screen.fade_out() return OK @@ -110,8 +119,7 @@ func save_world_state() -> Error: world.update_world_state() var world_state: WorldState = world.world_state - var save_slot: String = str(current_save_slot) - var file_name: String = _get_world_save_file_name_from_world_path(world_state.world_path) + var file_name: String = convert_path_to_resource_filename(world_state.world_path) print("File name: ", file_name) print(SAVE_PATH % [save_slot, file_name]) 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: - var save_slot: String = str(current_save_slot) - var file_name: String = _get_world_save_file_name_from_world_path(world_path) + var file_name: String = convert_path_to_resource_filename(world_path) var file_path: String = SAVE_PATH % [save_slot, file_name] if not ResourceLoader.exists(file_path): return null @@ -136,29 +143,18 @@ func load_main_menu() -> void: _main_menu.load_game_request.connect(load_game) _main_menu.quit_request.connect(quit_game) main_menu = _main_menu - world_container.add_child(main_menu) + world_holder.add_child(main_menu) -func load_initial_setup() -> void: - var setup_menu: InitialSetupMenu = load(initial_setup_path).instantiate() - setup_menu.setup_finished.connect(func() -> void: - setup_menu.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) +func load_startup() -> void: + var startup: StartupScene = load(startup_scene).instantiate() + startup.startup_finished.connect(func() -> void: + startup.queue_free() await loading_screen.fade_in(0.0) load_main_menu() loading_screen.fade_out() ) - add_child(setup_menu) - - -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) + add_child(startup) func unload_main_menu() -> void: @@ -194,13 +190,3 @@ func _on_unload_world_request(do_save: bool) -> void: await load_main_menu() 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" diff --git a/game/src/game.tscn b/game/src/game.tscn index 2129739..b0294ad 100644 --- a/game/src/game.tscn +++ b/game/src/game.tscn @@ -7,7 +7,7 @@ script = ExtResource("1_7uq6d") 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 [node name="CanvasLayer" type="CanvasLayer" parent="." unique_id=919699501] diff --git a/game/src/gameplay/characters/player/head.gd b/game/src/gameplay/characters/player/head.gd index 8a372d7..8d1a340 100644 --- a/game/src/gameplay/characters/player/head.gd +++ b/game/src/gameplay/characters/player/head.gd @@ -103,7 +103,7 @@ func _process_input(delta: float) -> void: if not using_controller and mouse_acceleration: rotational_velocity = _lerp_rotational_velocity(get_sensitivity() / 10, mouse_friction, delta) 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) else: rotational_velocity = Vector2.ZERO diff --git a/game/src/gameplay/characters/player/hud.gd b/game/src/gameplay/characters/player/hud.gd index 0c51011..e0964ab 100644 --- a/game/src/gameplay/characters/player/hud.gd +++ b/game/src/gameplay/characters/player/hud.gd @@ -1,6 +1,9 @@ class_name Hud 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 @onready var interaction_prompt: RichTextLabel = $InteractionPrompt @@ -8,7 +11,7 @@ extends CanvasLayer 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: @@ -27,15 +30,15 @@ func _on_interaction_ray_focused(area: InteractionArea) -> void: if is_instance_valid(area): area.hint_prompts_changed.connect(_populate_focused_hints.bind(area)) _populate_focused_hints(area) - _play_crosshair_anim(&"to_hollow") + _play_crosshair_anim(_CROSSHAIR_ANIM_TO_HOLLOW) else: interaction_prompt.clear() - _play_crosshair_anim(&"to_dot") + _play_crosshair_anim(_CROSSHAIR_ANIM_TO_DOT) func _on_interaction_ray_focus_lost(area: InteractionArea) -> void: interaction_prompt.clear() - _play_crosshair_anim(&"to_dot") + _play_crosshair_anim(_CROSSHAIR_ANIM_TO_DOT) if is_instance_valid(area): area.hint_prompts_changed.disconnect(_populate_focused_hints) diff --git a/game/src/gameplay/characters/player/player_character.gd b/game/src/gameplay/characters/player/player_character.gd index 289702e..bd96d39 100644 --- a/game/src/gameplay/characters/player/player_character.gd +++ b/game/src/gameplay/characters/player/player_character.gd @@ -10,8 +10,6 @@ const ACTION_MOVE_FORWARD: StringName = &"move_forward" const ACTION_MOVE_BACKWARD: StringName = &"move_backward" const ACTION_INTERACT: StringName = &"interact" -static var player: PlayerCharacter - @export var flashlight_manager: FlashlightManager @export_group("Crouching") @@ -41,10 +39,6 @@ var _flying: bool = false @onready var flashlight: Flashlight = %Flashlight -func _init() -> void: - player = self - - func _exit_tree() -> void: LimboConsole.unregister_command("player_fly") LimboConsole.remove_alias("noclip") @@ -112,13 +106,11 @@ func _physics_process(delta: float) -> void: SPrint.print_msgf( "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], - true, - ) + % [is_on_floor(), was_on_floor, (velocity * Utils.VEC3_HOR).length(), velocity.y], true) 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) if reset_velocity: diff --git a/game/src/gameplay/objects/stagelight/stagelight.tscn b/game/src/gameplay/objects/stagelight/stagelight.tscn index 6e6ab38..4c5d4d9 100644 --- a/game/src/gameplay/objects/stagelight/stagelight.tscn +++ b/game/src/gameplay/objects/stagelight/stagelight.tscn @@ -61,7 +61,7 @@ light_surface_material = SubResource("ShaderMaterial_nqu5b") [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") [node name="StudioSpotLightFraming01" parent="." unique_id=2007355874 instance=ExtResource("3_otype")] diff --git a/game/src/gameplay/systems/flashlight/flashlight.gd b/game/src/gameplay/systems/flashlight/flashlight.gd index 4a8528f..55301da 100644 --- a/game/src/gameplay/systems/flashlight/flashlight.gd +++ b/game/src/gameplay/systems/flashlight/flashlight.gd @@ -30,9 +30,8 @@ func _unhandled_input(event: InputEvent) -> void: func try_turn_on() -> void: - if is_instance_valid(manager): - if manager.is_drained(): - return + if is_instance_valid(manager) and manager.is_drained(): + return turn_on() diff --git a/game/src/gameplay/systems/flashlight/flashlight_manager.gd b/game/src/gameplay/systems/flashlight/flashlight_manager.gd index 0e15148..54b5936 100644 --- a/game/src/gameplay/systems/flashlight/flashlight_manager.gd +++ b/game/src/gameplay/systems/flashlight/flashlight_manager.gd @@ -42,7 +42,7 @@ func _exit_tree() -> void: func _ready() -> void: 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) is_flashlight_active = _instance_persister.get_property(&"is_flashlight_active", is_flashlight_active) diff --git a/game/src/gameplay/systems/flashlight/flashlight_trigger.gd b/game/src/gameplay/systems/flashlight/flashlight_trigger.gd index 3342cbd..2a31ce9 100644 --- a/game/src/gameplay/systems/flashlight/flashlight_trigger.gd +++ b/game/src/gameplay/systems/flashlight/flashlight_trigger.gd @@ -10,14 +10,14 @@ enum DrainModes { @export var allowed_bodies: Array[Node3D] @export var trigger_once: bool = true -var world: World -var _world_proxy: WorldProxy - @export var drain_mode := DrainModes.SET @export var drain_value: float = 0.3 @export var change_manager_drain_mode: bool = false @export var manager_drain_mode := FlashlightManager.DrainModes.MANUAL +var world: World +var _world_proxy: WorldProxy + func _enter_tree() -> void: _world_proxy = WorldProxy.new() diff --git a/game/src/gameplay/systems/power_box/fuse_pickup.gd b/game/src/gameplay/systems/power_box/fuse_pickup.gd index 2acc872..b77febe 100644 --- a/game/src/gameplay/systems/power_box/fuse_pickup.gd +++ b/game/src/gameplay/systems/power_box/fuse_pickup.gd @@ -1,6 +1,7 @@ class_name FusePickup extends Node3D +signal picked_up @export var power_box: PowerBox @@ -23,4 +24,5 @@ func _ready() -> void: func _on_picked_up() -> void: power_box.on_fuse_picked_up() InstancePersister.new(self, world).set_property(&"is_collected", true) + picked_up.emit() queue_free() diff --git a/game/src/gameplay/systems/power_box/power_box.gd b/game/src/gameplay/systems/power_box/power_box.gd index cbb8cb8..779d5e8 100644 --- a/game/src/gameplay/systems/power_box/power_box.gd +++ b/game/src/gameplay/systems/power_box/power_box.gd @@ -80,10 +80,10 @@ func _on_interaction_interacted() -> void: #start_minigame() pass - if not is_active: - turn_on() - else: + if is_active: turn_off() + else: + turn_on() # TODO diff --git a/game/src/ui/main_menu/main_menu.gd b/game/src/ui/main_menu/main_menu.gd index 71d7821..03b13a6 100644 --- a/game/src/ui/main_menu/main_menu.gd +++ b/game/src/ui/main_menu/main_menu.gd @@ -4,9 +4,8 @@ extends Control signal load_game_request(save_data: SaveData, slot_index: int) signal quit_request -const USER_SETTINGS_PATH: String = "user://settings.ini" 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 load_button: Button = %LoadButton @@ -27,7 +26,7 @@ static func get_save_slots() -> PackedInt32Array: var index: int = 0 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("-")): index += 1 else: @@ -73,7 +72,7 @@ func _ready() -> void: 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") @@ -88,7 +87,7 @@ func populate_save_entries() -> 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 path: String = get_save_path(slot) @@ -100,7 +99,7 @@ func _on_continue_pressed() -> void: return config.set_value("save", "last_slot", slot) - config.save(USER_SETTINGS_PATH) + config.save(Game.USER_SETTINGS_PATH) var save_data: SaveData = load(path) 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() 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.save(USER_SETTINGS_PATH) + config.save(Game.USER_SETTINGS_PATH) func _on_options_pressed() -> void: @@ -130,9 +129,9 @@ func _on_quit_pressed() -> 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.save(USER_SETTINGS_PATH) + config.save(Game.USER_SETTINGS_PATH) var save_path: String = get_save_path(save_slot) diff --git a/game/src/ui/settings_menu/settings_menu.gd b/game/src/ui/settings_menu/settings_menu.gd index 41e7331..18c539b 100644 --- a/game/src/ui/settings_menu/settings_menu.gd +++ b/game/src/ui/settings_menu/settings_menu.gd @@ -60,7 +60,11 @@ func _ready() -> void: controller_setup_menu.confirmed.connect(_on_vibration_config_confirmed) 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) @@ -82,8 +86,10 @@ func close() -> void: func update_controls_from_settings() -> void: # Gameplay - headbobbing.set_pressed_no_signal(SettingsHandler.get_setting("gameplay", "headbobbing", true)) - headbobbing_slider.set_value_no_signal(SettingsHandler.get_setting("gameplay", "headbobbing_multiplier", 1.0)) + var enabled: bool = SettingsHandler.get_setting("gameplay", "headbobbing", true) + 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 match SettingsHandler.get_setting("localization", "language", "en_US"): diff --git a/game/src/ui/settings_menu/slider_label.gd b/game/src/ui/settings_menu/slider_label.gd index eb13a85..36cb014 100644 --- a/game/src/ui/settings_menu/slider_label.gd +++ b/game/src/ui/settings_menu/slider_label.gd @@ -41,7 +41,8 @@ func set_slider(new_slider: Slider) -> void: func _on_value_changed(new_value: float) -> void: var value: float = new_value 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) set_text(str(prefix, positive_prefix if value >= 0.0 else negative_prefix, new_text, suffix)) diff --git a/game/src/ui/setup/controller/controller_setup_menu.gd b/game/src/ui/setup/controller/controller_setup_menu.gd index bb22e4a..90c7ddf 100644 --- a/game/src/ui/setup/controller/controller_setup_menu.gd +++ b/game/src/ui/setup/controller/controller_setup_menu.gd @@ -10,7 +10,7 @@ var is_testing_weak: bool = false var is_testing_strong: bool = false 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 min_duration_slider: HSlider = %MinDurationSlider @@ -22,9 +22,6 @@ var _duration_idx: int = 0 func _ready() -> void: - _on_visibility_changed() - visibility_changed.connect(_on_visibility_changed) - repeat_vibration_timer.timeout.connect(_test_vibration) 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_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_strong_magnitude.value_changed.connect(_on_min_strong_magnitude_value_changed) confirm_button.pressed.connect(confirm) + visibility_changed.connect(_on_visibility_changed) + _on_visibility_changed() + func confirm() -> void: SettingsHandler.set_setting( "controls", "vibration_min_weak_threshold", - VibrationComponent.min_weak_magnitude_threshold, + RumbleConductor.min_weak_magnitude_threshold, false ) SettingsHandler.set_setting( "controls", "vibration_min_strong_threshold", - VibrationComponent.min_strong_magnitude_threshold, + RumbleConductor.min_strong_magnitude_threshold, true ) @@ -58,27 +55,30 @@ func confirm() -> void: func _test_vibration() -> void: - vibration_component.weak_magnitude = MIN_WEAK_MAGNITUDE * float(is_testing_weak) - vibration_component.strong_magnitude = MIN_STRONG_MAGNITUDE * float(is_testing_strong) + # If is_testing_weak and is_testing_strong are both false, vibrate both together. + 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: 5: - vibration_component.duration = 0.75 + rumble_component.data.duration = 0.75 _: - vibration_component.duration = 0.15 + rumble_component.data.duration = 0.15 #1: - #vibration_component.duration = 0.35 + #rumble_component.duration = 0.35 #2: - #vibration_component.duration = 0.75 + #rumble_component.duration = 0.75 _duration_idx = posmod(_duration_idx + 1, 6) - vibration_component.vibrate() + rumble_component.vibrate() _vibrate_texture() 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) await tween.finished @@ -91,17 +91,20 @@ func _on_visibility_changed() -> void: repeat_vibration_timer.start() process_mode = Node.PROCESS_MODE_INHERIT 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: repeat_vibration_timer.stop() process_mode = Node.PROCESS_MODE_DISABLED 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: - VibrationComponent.min_strong_magnitude_threshold = value + RumbleConductor.min_strong_magnitude_threshold = value func _on_weak_slider_focused() -> void: diff --git a/game/src/ui/setup/controller/controller_setup_menu.tscn b/game/src/ui/setup/controller/controller_setup_menu.tscn index 73c1a37..5487e87 100644 --- a/game/src/ui/setup/controller/controller_setup_menu.tscn +++ b/game/src/ui/setup/controller/controller_setup_menu.tscn @@ -1,8 +1,12 @@ [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://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="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] layout_mode = 3 @@ -14,9 +18,10 @@ grow_vertical = 2 script = ExtResource("1_ee61x") 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 script = ExtResource("2_6beo4") +data = SubResource("Resource_gtqq5") weak_magnitude = 0.05 strong_magnitude = 0.05 metadata/_custom_type_script = "uid://bbwtct3hoxwws" @@ -25,10 +30,13 @@ metadata/_custom_type_script = "uid://bbwtct3hoxwws" unique_name_in_owner = true [node name="Label" type="Label" parent="." unique_id=684594410] -layout_mode = 0 -offset_right = 384.0 -offset_bottom = 49.0 +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 23.0 +grow_horizontal = 2 text = "Adjust the sliders, until you can bearly feel the vibrations." +horizontal_alignment = 1 [node name="RichTextLabel" type="RichTextLabel" parent="." unique_id=1446444531] visible = false diff --git a/game/src/ui/setup/initial_setup_menu.gd b/game/src/ui/setup/initial_setup_menu.gd index 33f226d..0e8d03c 100644 --- a/game/src/ui/setup/initial_setup_menu.gd +++ b/game/src/ui/setup/initial_setup_menu.gd @@ -7,12 +7,25 @@ signal setup_finished @onready var controller_recommended: Control = %ControllerRecommended @onready var rhythm_setup_menu: RhythmSetupMenu = %RhythmSetupMenu @onready var controller_setup_menu: ControllerSetupMenu = %ControllerSetupMenu +@onready var configurable_notice: Label = %ConfigurableNotice +@onready var fade_control: Control = %FadeControl func _ready() -> void: rhythm_setup_menu.confirmed.connect(_on_rythm_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 controller_recommended.show() fade_out() @@ -21,23 +34,24 @@ func _ready() -> void: controller_recommended.hide() controller_setup_menu.show() + configurable_notice.show() fade_out() - InputManager.input_method_changed.connect(_on_input_method_changed) - func fade_in() -> void: get_viewport().gui_release_focus() foreground_color.mouse_filter = Control.MOUSE_FILTER_STOP 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 func fade_out() -> void: foreground_color.mouse_filter = Control.MOUSE_FILTER_IGNORE 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: diff --git a/game/src/ui/setup/initial_setup_menu.tscn b/game/src/ui/setup/initial_setup_menu.tscn index eed1f4a..6b9d7ac 100644 --- a/game/src/ui/setup/initial_setup_menu.tscn +++ b/game/src/ui/setup/initial_setup_menu.tscn @@ -24,7 +24,17 @@ grow_horizontal = 2 grow_vertical = 2 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 visible = false layout_mode = 1 @@ -34,7 +44,7 @@ anchor_bottom = 1.0 grow_horizontal = 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 anchors_preset = -1 anchor_left = 0.15 @@ -44,17 +54,17 @@ anchor_bottom = 0.97 grow_horizontal = 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 theme_override_font_sizes/font_size = 42 text = "Use of a controller recommended" 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 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 anchors_preset = 15 anchor_right = 1.0 @@ -68,20 +78,35 @@ texture = ExtResource("4_sr5uw") expand_mode = 1 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 process_mode = 4 visible = false 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 process_mode = 4 visible = false 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] unique_name_in_owner = true +visible = false layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 diff --git a/game/src/ui/setup/rhythm/rhythm_setup_menu.gd b/game/src/ui/setup/rhythm/rhythm_setup_menu.gd index af612e5..6d174e5 100644 --- a/game/src/ui/setup/rhythm/rhythm_setup_menu.gd +++ b/game/src/ui/setup/rhythm/rhythm_setup_menu.gd @@ -32,6 +32,13 @@ func close() -> void: 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: rhythm_player.stop() _restore_song_playback_data() @@ -55,7 +62,8 @@ func _on_visibility_changed() -> void: if not rhythm_beat.playing: _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() rhythm_player.play() diff --git a/game/src/ui/setup/rhythm/rhythm_setup_menu.tscn b/game/src/ui/setup/rhythm/rhythm_setup_menu.tscn index 15be745..20bdb63 100644 --- a/game/src/ui/setup/rhythm/rhythm_setup_menu.tscn +++ b/game/src/ui/setup/rhythm/rhythm_setup_menu.tscn @@ -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://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="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://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://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="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"] script = ExtResource("4_7a0hf") audio_stream = ExtResource("2_uupbh") @@ -27,14 +38,16 @@ mouse_filter = 2 script = ExtResource("1_la56f") 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") +data = SubResource("Resource_ej02t") weak_magnitude = 0.5 strong_magnitude = 0.0 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") +data = SubResource("Resource_sp7no") weak_magnitude = 0.9 strong_magnitude = 0.5 metadata/_custom_type_script = "uid://bbwtct3hoxwws" @@ -155,5 +168,14 @@ texture = ExtResource("6_cribi") layout_mode = 2 texture = ExtResource("6_cribi") -[connection signal="bar_tick" from="RhythmListener" to="BarVibration" method="vibrate" flags=3 unbinds=1] -[connection signal="beat_tick" from="RhythmListener" to="BeatVibration" method="vibrate" unbinds=1] +[node name="Label" type="Label" parent="." unique_id=1207688688] +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] diff --git a/game/src/ui/startup/fog_overlay.gdshader b/game/src/ui/startup/fog_overlay.gdshader new file mode 100644 index 0000000..0b3d391 --- /dev/null +++ b/game/src/ui/startup/fog_overlay.gdshader @@ -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. +//} diff --git a/game/src/ui/startup/fog_overlay.gdshader.uid b/game/src/ui/startup/fog_overlay.gdshader.uid new file mode 100644 index 0000000..4a0fef5 --- /dev/null +++ b/game/src/ui/startup/fog_overlay.gdshader.uid @@ -0,0 +1 @@ +uid://cx2yv2l8vbubl diff --git a/game/src/ui/startup/startup_scene.gd b/game/src/ui/startup/startup_scene.gd new file mode 100644 index 0000000..100b0b8 --- /dev/null +++ b/game/src/ui/startup/startup_scene.gd @@ -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() diff --git a/game/src/ui/startup/startup_scene.gd.uid b/game/src/ui/startup/startup_scene.gd.uid new file mode 100644 index 0000000..35da319 --- /dev/null +++ b/game/src/ui/startup/startup_scene.gd.uid @@ -0,0 +1 @@ +uid://o3b1qkysue8d diff --git a/game/src/ui/startup/startup_scene.tscn b/game/src/ui/startup/startup_scene.tscn new file mode 100644 index 0000000..fdca02b --- /dev/null +++ b/game/src/ui/startup/startup_scene.tscn @@ -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"] diff --git a/game/src/ui/startup/title.pixi b/game/src/ui/startup/title.pixi new file mode 100644 index 0000000..53bfd15 Binary files /dev/null and b/game/src/ui/startup/title.pixi differ diff --git a/game/src/ui/startup/title.png b/game/src/ui/startup/title.png new file mode 100644 index 0000000..33e6c5f Binary files /dev/null and b/game/src/ui/startup/title.png differ diff --git a/game/src/ui/startup/title.png.import b/game/src/ui/startup/title.png.import new file mode 100644 index 0000000..c4a1f32 --- /dev/null +++ b/game/src/ui/startup/title.png.import @@ -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 diff --git a/game/tools/animation_player_editor_calls.gd b/game/tools/animation_player_editor_calls.gd index a6297f2..31883bb 100644 --- a/game/tools/animation_player_editor_calls.gd +++ b/game/tools/animation_player_editor_calls.gd @@ -7,10 +7,9 @@ extends AnimationPlayer &"free", ] -var EDITOR_PATH: String: - get: return str(get_parent().get_path()) + "/" - var function_calls: Array[Dictionary] = [] +var _editor_path: String: + get: return str(get_parent().get_path()) + "/" func _ready() -> void: @@ -57,7 +56,7 @@ func animation_started_func(_dismiss: Variant) -> void: if animation.track_get_type(track_index) != Animation.TYPE_METHOD: 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) for key_index: int in track_key_count: var time: float = animation.track_get_key_time(track_index, key_index) diff --git a/game/tools/cutscene_animation_player.gd b/game/tools/cutscene_animation_player.gd index b627a0f..af17c73 100644 --- a/game/tools/cutscene_animation_player.gd +++ b/game/tools/cutscene_animation_player.gd @@ -22,7 +22,6 @@ func play_cutscene() -> void: disable_flashlight() - func align_player_with_end_alignment() -> void: if not is_instance_valid(player) or not is_instance_valid(end_alignment): return diff --git a/game/tools/debug_outline_material.tres b/game/tools/debug_outline_material.tres index 7b100b2..134b68a 100644 --- a/game/tools/debug_outline_material.tres +++ b/game/tools/debug_outline_material.tres @@ -8,5 +8,3 @@ shader = ExtResource("1_3pgam") 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_power = 3.0 -shader_parameter/box_center = Vector3(0, 0, 0) -shader_parameter/box_extents = Vector3(0, 0, 0) diff --git a/game/tools/time_music_animation_player.gd b/game/tools/time_music_animation_player.gd index 633169b..9824f50 100644 --- a/game/tools/time_music_animation_player.gd +++ b/game/tools/time_music_animation_player.gd @@ -19,12 +19,15 @@ extends AnimationPlayerEditorCalls ## is bigger than the [member max_catchup_threshold], ## or lower than the [method AudioServer.get_output_latency]. -enum CatchupBehaviour { - NONE, ## Don't catch up slowsly. +enum CatchUpBehaviour { + NONE, ## Don't catch up slowly. 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] ## or [AudioStreamPlayer3D] the music should be played on.[br] ## [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 # The offset from 0.0 seconds when the music starts in the animation. #@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, ## 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. -@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") -## 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 ## Should be set to a value above- or equal to [member max_error]. @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. @export var max_pitch_scale_difference: float = 1.01 -## Helper variable in case you want to modify the [member AudioStreamPlayer.pitch_scale] property. -@export var custom_pitch_scale: float = 1.0 -@export_subgroup("Speed Scale") +#@export_subgroup("Speed Scale") ## The maximum speed scale change the animation can be set to, when they are desynced. @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 _timer := Timer.new() +var _audio_playback_delay_timer := Timer.new() var _track_time: float = 0.0 @@ -66,8 +72,8 @@ func _ready() -> void: super() if not Engine.is_editor_hint(): - add_child(_timer) - _timer.one_shot = false + add_child(_audio_playback_delay_timer) + _audio_playback_delay_timer.one_shot = false set_audio_player(audio_player) animation_started.connect(_anim_started) @@ -85,6 +91,10 @@ func _process(delta: float) -> void: var latency: float = AudioServer.get_output_latency() var audio_position: float = _audio_player.get_playback_position() 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 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]) if ( - not catch_up_behaviour == CatchupBehaviour.NONE + not catch_up_behaviour == CatchUpBehaviour.NONE and abs_difference < max_catchup_threshold - #and abs_difference > latency #min_catchup_threshold: ): match catch_up_behaviour: - CatchupBehaviour.SPEED_SCALE_ANIMATION: + CatchUpBehaviour.SPEED_SCALE_ANIMATION: _sync_anim_speed_scale(difference, delta) - CatchupBehaviour.PITCH_SCALE_MUSIC: + CatchUpBehaviour.PITCH_SCALE_MUSIC: _sync_music_pitch_scale(latency, difference, delta) elif abs_difference > max_error: - #_audio_player.seek(difference) - _audio_player.play(anim_position + _track_time + latency) - SPrint.print_msg("Snapped Audio to: %s" % [_audio_player.get_playback_position()]) + _audio_player.play(anim_position - _track_time + latency) + SPrint.print_msg("Snapped Audio to: %s" % _audio_player.get_playback_position()) else: - _audio_player.pitch_scale = custom_pitch_scale - speed_scale = custom_speed_scale + _audio_player.pitch_scale = audio_pitch_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: @@ -120,6 +137,10 @@ func set_audio_player(path: NodePath) -> void: _audio_player = node if Utils.is_node_audioplayer(node) else null +func set_rhythm_player(player: RhythmPlayer) -> void: + rhythm_player = player + + #func play_timed( #anim_name: StringName, #from_marker: StringName = &"", @@ -129,13 +150,14 @@ func set_audio_player(path: NodePath) -> void: #from_end: bool = false #) -> void: #_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: var max_speed_scale: float = max_speed_scale_difference - var min_speed_scale: float = custom_speed_scale + (custom_speed_scale - max_speed_scale) - var speed: float = custom_speed_scale - difference + var min_speed_scale: float = anim_speed_scale + (anim_speed_scale - max_speed_scale) + var speed: float = anim_speed_scale - difference 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) #else: #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: 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 = 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: @@ -170,24 +193,30 @@ func _anim_started(anim_name: StringName) -> void: var duration: float = _track_time - current_animation_position if duration > 0.0: - _timer.start(duration) + _audio_playback_delay_timer.start(duration) 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 - #var latency: float = AudioServer.get_output_latency() - AudioServer.get_time_to_next_mix() - _audio_player.stream = animation.audio_track_get_key_stream(music_anim_track_index, 0) - _audio_player.play(current_animation_position - _track_time)# - latency) + var playback_position: float = current_animation_position - _track_time + + 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: if Engine.is_editor_hint(): 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.stream = null