diff --git a/.gitignore b/.gitignore index 89c55c6c..c9c54e56 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ .vs/ Properties/ +# Rider +.idea/ + # Git .git/ .gitattributes diff --git a/Rhythia.csproj b/Rhythia.csproj index 7deaa66c..94da00f6 100644 --- a/Rhythia.csproj +++ b/Rhythia.csproj @@ -1,4 +1,4 @@ - + net10.0 net10.0 diff --git a/Rhythia.csproj.old.2 b/Rhythia.csproj.old.2 new file mode 100644 index 00000000..fd915778 --- /dev/null +++ b/Rhythia.csproj.old.2 @@ -0,0 +1,19 @@ + + + net10.0 + net10.0 + true + preview + + + + + + + + + + + + + \ No newline at end of file diff --git a/Rhythia.csproj.old.3 b/Rhythia.csproj.old.3 new file mode 100644 index 00000000..041d012e --- /dev/null +++ b/Rhythia.csproj.old.3 @@ -0,0 +1,19 @@ + + + net10.0 + net10.0 + true + preview + + + + + + + + + + + + + \ No newline at end of file diff --git a/scenes/game.tscn b/scenes/game.tscn index 6439a47e..8928965d 100644 --- a/scenes/game.tscn +++ b/scenes/game.tscn @@ -1,15 +1,24 @@ [gd_scene format=3 uid="uid://vp1nmsiqt5yv"] -[ext_resource type="Script" uid="uid://8s0npsnw42ce" path="res://scripts/scenes/LegacyRunner.cs" id="1_1hgss"] +[ext_resource type="Script" uid="uid://dly10tvxguvhb" path="res://scripts/scenes/GameScene.cs" id="1_bhx41"] +[ext_resource type="ArrayMesh" uid="uid://ceww5w8t33lqy" path="res://user/meshes/squircle.obj" id="2_e1j6l"] +[ext_resource type="Script" uid="uid://bxncinhblsjgg" path="res://scripts/game/managers/CursorManager.cs" id="2_nepv3"] [ext_resource type="Texture2D" uid="uid://b3g4aw54n6bow" path="res://user/skins/default/game/grid.png" id="2_q78ea"] +[ext_resource type="Script" uid="uid://dp8nga2aot1n2" path="res://scripts/game/Runner.cs" id="2_qnb83"] +[ext_resource type="Script" uid="uid://bxwlotp5vcjux" path="res://scripts/game/managers/ReplayManager.cs" id="2_twgab"] +[ext_resource type="Script" uid="uid://c6nlc0umyu342" path="res://scripts/game/renderers/TrailRenderer.cs" id="3_7025r"] [ext_resource type="Script" uid="uid://okqdw5gj567n" path="res://scripts/game/LegacyRenderer.cs" id="3_ew8wr"] [ext_resource type="Texture2D" uid="uid://3e1tjlr4m0wt" path="res://user/skins/default/game/cursor.png" id="3_negii"] +[ext_resource type="Script" uid="uid://n6n303pw3qbd" path="res://scripts/game/managers/HudManager.cs" id="3_sww3w"] +[ext_resource type="Script" uid="uid://cqqaf7cwx5ty1" path="res://scripts/game/ui/HealthBar.cs" id="4_js0lu"] [ext_resource type="Theme" uid="uid://dhuhu3mmaew1a" path="res://themes/theme.tres" id="4_rvp5o"] [ext_resource type="FontFile" uid="uid://4bnnn01f4dqr" path="res://fonts/Ubuntu-Medium.ttf" id="5_pxgh4"] [ext_resource type="Texture2D" uid="uid://cxv6ey5xb6b0f" path="res://user/skins/default/game/health_background.png" id="6_bbly8"] +[ext_resource type="Script" uid="uid://dbyc4etv31aub" path="res://scripts/game/ui/ProgressBar.cs" id="7_gxtfn"] [ext_resource type="Texture2D" uid="uid://4x2vfpyjkri6" path="res://user/skins/default/game/health.png" id="7_wjg6i"] [ext_resource type="Texture2D" uid="uid://dmc5f6ajp8xv1" path="res://user/skins/default/game/progress_background.png" id="8_eu3ux"] [ext_resource type="Shader" uid="uid://dsysoy6w8ndnk" path="res://scripts/shaders/video_background.gdshader" id="8_ocxsu"] +[ext_resource type="Script" uid="uid://dv0gni2j4t686" path="res://scripts/game/ui/PanelLeft.cs" id="8_qnb83"] [ext_resource type="ArrayMesh" uid="uid://uqxu6tq6xhhg" path="res://user/meshes/squircle.obj" id="9_e1j6l"] [ext_resource type="Texture2D" uid="uid://d3s17ehomjscd" path="res://user/skins/default/game/progress.png" id="9_nde5v"] [ext_resource type="FontFile" uid="uid://cmjos3o0k2f6t" path="res://fonts/Comfortaa-Medium.ttf" id="11_o37lt"] @@ -17,12 +26,20 @@ [ext_resource type="FontFile" uid="uid://b6hgjef6s6oly" path="res://fonts/Comfortaa-Bold.ttf" id="12_axsek"] [ext_resource type="Texture2D" uid="uid://diba7c3s8as4s" path="res://user/skins/default/game/panel_right_background.png" id="12_whe6p"] [ext_resource type="Texture2D" uid="uid://dw2gxd3m24h4m" path="res://user/skins/default/game/hits.png" id="13_6rhgc"] +[ext_resource type="Script" uid="uid://c3gf0hgrqqqx5" path="res://scripts/game/ui/PanelRight.cs" id="13_sww3w"] [ext_resource type="Shader" uid="uid://bp2sv5ynsubi2" path="res://scripts/shaders/alpha_gradient.gdshader" id="14_3xchs"] [ext_resource type="Texture2D" uid="uid://c3x2cp3tx4vqt" path="res://user/skins/default/game/misses.png" id="14_bqf11"] [ext_resource type="Shader" uid="uid://bx4888tau78tk" path="res://scripts/shaders/combo_progress.gdshader" id="14_y51pe"] +[ext_resource type="Script" uid="uid://0boi2mqa8488" path="res://scripts/game/ui/Combo.cs" id="20_suyxu"] +[ext_resource type="Script" uid="uid://cmmf22opvqenp" path="res://scripts/game/ui/Progress.cs" id="21_twgab"] +[ext_resource type="Script" uid="uid://bjc8qp66nisos" path="res://scripts/game/ui/Title.cs" id="22_02fa5"] [ext_resource type="Texture2D" uid="uid://lfj8i1bi6kfs" path="res://textures/ui/pause.png" id="23_7silw"] [ext_resource type="FontFile" uid="uid://dotb3skhwouha" path="res://fonts/Roboto-Bold.ttf" id="23_wf21h"] [ext_resource type="FontFile" uid="uid://rs2mb33nd3b1" path="res://fonts/Roboto-Italic.ttf" id="24_j6p10"] +[ext_resource type="Script" uid="uid://dhrqgv44b65ls" path="res://scripts/game/ui/Skip.cs" id="25_nepv3"] +[ext_resource type="Script" uid="uid://ddtnij8m4eipl" path="res://scripts/game/ui/Grid.cs" id="27_js0lu"] +[ext_resource type="Script" uid="uid://b7jvxorkr4xfi" path="res://scripts/game/ui/Speed.cs" id="28_gxtfn"] +[ext_resource type="Script" uid="uid://c8d872aoiqglo" path="res://scripts/PlayerInputController.cs" id="37_02fa5"] [ext_resource type="Script" uid="uid://qjqjxykl30bm" path="res://scripts/scenes/PauseHud.cs" id="25_pausehud"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_sijr0"] @@ -144,6 +161,20 @@ texture_repeat = false material = SubResource("StandardMaterial3D_a6jk1") size = Vector2(4, 4) +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lvhmj"] +transparency = 1 +shading_mode = 0 +disable_ambient_light = true +disable_fog = true +albedo_texture = ExtResource("3_negii") +texture_filter = 5 +texture_repeat = false +distance_fade_min_distance = 1076.07 + +[sub_resource type="QuadMesh" id="QuadMesh_xij4d"] +material = SubResource("StandardMaterial3D_lvhmj") +size = Vector2(0.263, 0.263) + [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ssr8x"] transparency = 1 cull_mode = 2 @@ -160,22 +191,8 @@ transform_format = 1 use_colors = true mesh = SubResource("QuadMesh_5p2n8") -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lvhmj"] -transparency = 1 -shading_mode = 0 -disable_ambient_light = true -disable_fog = true -albedo_texture = ExtResource("3_negii") -texture_filter = 5 -texture_repeat = false -distance_fade_min_distance = 1076.07 - -[sub_resource type="QuadMesh" id="QuadMesh_xij4d"] -material = SubResource("StandardMaterial3D_lvhmj") -size = Vector2(0.263, 0.263) - [sub_resource type="ViewportTexture" id="ViewportTexture_pl11u"] -viewport_path = NodePath("Health/HealthViewport") +viewport_path = NodePath("Runner/HUD/Health/HealthViewport") [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4p8vu"] resource_local_to_scene = true @@ -190,44 +207,8 @@ resource_local_to_scene = true material = SubResource("StandardMaterial3D_4p8vu") size = Vector2(10.88, 0.8) -[sub_resource type="ViewportTexture" id="ViewportTexture_bhx41"] -viewport_path = NodePath("ProgressBar/ProgressBarViewport") - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p1h21"] -resource_local_to_scene = true -transparency = 1 -shading_mode = 0 -disable_fog = true -albedo_texture = SubResource("ViewportTexture_bhx41") -texture_repeat = false - -[sub_resource type="QuadMesh" id="QuadMesh_7mook"] -resource_local_to_scene = true -material = SubResource("StandardMaterial3D_p1h21") -size = Vector2(10.88, 0.8) - -[sub_resource type="ViewportTexture" id="ViewportTexture_qnb83"] -viewport_path = NodePath("Video/VideoViewport") - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_rj3ky"] -resource_local_to_scene = true -transparency = 1 -no_depth_test = true -shading_mode = 0 -disable_ambient_light = true -disable_fog = true -albedo_texture = SubResource("ViewportTexture_qnb83") -texture_filter = 1 -texture_repeat = false - -[sub_resource type="QuadMesh" id="QuadMesh_3p7x2"] -size = Vector2(258.3, 210) - -[sub_resource type="ShaderMaterial" id="ShaderMaterial_4qlgx"] -shader = ExtResource("8_ocxsu") - [sub_resource type="ViewportTexture" id="ViewportTexture_sww3w"] -viewport_path = NodePath("PanelLeft/PanelLeftViewport") +viewport_path = NodePath("Runner/HUD/PanelLeft/PanelLeftViewport") [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nody0"] resource_local_to_scene = true @@ -242,7 +223,7 @@ size = Vector2(4, 10) [sub_resource type="ShaderMaterial" id="ShaderMaterial_x2xye"] shader = ExtResource("14_y51pe") -shader_parameter/progress = 0.0 +shader_parameter/progress = 0.8870000421325 shader_parameter/colour = Color(1, 1, 1, 1) shader_parameter/background_colour = Color(0.155, 0.155, 0.155, 1) shader_parameter/sides = 8 @@ -265,7 +246,7 @@ font = ExtResource("12_axsek") font_size = 42 [sub_resource type="ViewportTexture" id="ViewportTexture_suyxu"] -viewport_path = NodePath("PanelRight/PanelRightViewport") +viewport_path = NodePath("Runner/HUD/PanelRight/PanelRightViewport") [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ro5ir"] resource_local_to_scene = true @@ -297,210 +278,375 @@ font_size = 50 [sub_resource type="ShaderMaterial" id="ShaderMaterial_gc8yu"] shader = ExtResource("14_3xchs") -[node name="SceneGame" type="Node" unique_id=909809067] -script = ExtResource("1_1hgss") +[sub_resource type="ViewportTexture" id="ViewportTexture_bhx41"] +viewport_path = NodePath("Runner/HUD/ProgressBar/ProgressBarViewport") + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p1h21"] +resource_local_to_scene = true +transparency = 1 +shading_mode = 0 +disable_fog = true +albedo_texture = SubResource("ViewportTexture_bhx41") +texture_repeat = false + +[sub_resource type="QuadMesh" id="QuadMesh_7mook"] +resource_local_to_scene = true +material = SubResource("StandardMaterial3D_p1h21") +size = Vector2(10.88, 0.8) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_73q55"] +transparency = 1 +cull_mode = 2 +shading_mode = 0 +disable_fog = true +vertex_color_use_as_albedo = true +vertex_color_is_srgb = true + +[sub_resource type="MultiMesh" id="MultiMesh_almyi"] +transform_format = 1 +use_colors = true +mesh = ExtResource("2_e1j6l") + +[sub_resource type="ViewportTexture" id="ViewportTexture_qnb83"] +viewport_path = NodePath("Runner/Video/VideoViewport") + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_rj3ky"] +resource_local_to_scene = true +transparency = 1 +no_depth_test = true +shading_mode = 0 +disable_ambient_light = true +disable_fog = true +albedo_texture = SubResource("ViewportTexture_qnb83") +texture_filter = 1 +texture_repeat = false + +[sub_resource type="QuadMesh" id="QuadMesh_3p7x2"] +size = Vector2(258.3, 210) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_4qlgx"] +shader = ExtResource("8_ocxsu") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_sijr0"] +bg_color = Color(0.1072, 0.0896, 0.16, 0.25098) + +[sub_resource type="LabelSettings" id="LabelSettings_a70tm"] +font_color = Color(1, 1, 1, 0.25098) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_d5aou"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_43mxh"] +bg_color = Color(0.0235294, 0.0196078, 0.0352941, 0.768627) + +[sub_resource type="LabelSettings" id="LabelSettings_pwlqp"] +font = ExtResource("23_wf21h") +font_size = 36 + +[sub_resource type="LabelSettings" id="LabelSettings_tjduq"] +font = ExtResource("24_j6p10") +font_size = 12 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kncke"] +bg_color = Color(0.0352941, 0.0313726, 0.0509804, 0.501961) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6kbgk"] +bg_color = Color(0.0352941, 0.0313726, 0.0509804, 0.501961) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_jlnvv"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y08lf"] +bg_color = Color(0.0745098, 0.0666667, 0.101961, 0.501961) + +[sub_resource type="Theme" id="Theme_e1j6l"] +Button/fonts/font = ExtResource("5_pxgh4") +Button/styles/focus = SubResource("StyleBoxFlat_kncke") +Button/styles/hover = SubResource("StyleBoxFlat_6kbgk") +Button/styles/normal = SubResource("StyleBoxEmpty_jlnvv") +Button/styles/pressed = SubResource("StyleBoxFlat_y08lf") + +[node name="SceneGame" type="Node" unique_id=909809067 node_paths=PackedStringArray("Runner", "Menu", "ReplayManager")] +script = ExtResource("1_bhx41") +Runner = NodePath("Runner") +Menu = NodePath("Menu") +ReplayManager = NodePath("ReplayManager") UseGameSpace = true AddSpaceAsChild = true -[node name="SubViewportContainer" type="SubViewportContainer" parent="." unique_id=1797381631] -visible = false -z_index = -1 +[node name="CursorManager" type="Node" parent="." unique_id=862696440 node_paths=PackedStringArray("runner", "playerInputController", "replayManager", "cursorMesh", "camera")] +script = ExtResource("2_nepv3") +runner = NodePath("../Runner") +playerInputController = NodePath("../PlayerInputController") +replayManager = NodePath("../ReplayManager") +cursorMesh = NodePath("../Runner/HUD/Grid/Cursor") +camera = NodePath("../Runner/Camera3D") + +[node name="TrailRenderer" type="Node" parent="CursorManager" unique_id=2058712869 node_paths=PackedStringArray("cursor", "cursorTrail", "runner")] +script = ExtResource("3_7025r") +cursor = NodePath("../../Runner/HUD/Grid/Cursor") +cursorTrail = NodePath("../../Runner/HUD/Grid/CursorTrail") +runner = NodePath("../../Runner") + +[node name="PlayerInputController" type="Node" parent="." unique_id=340777835] +script = ExtResource("37_02fa5") + +[node name="ReplayManager" type="Node" parent="." unique_id=40137833 node_paths=PackedStringArray("Runner", "ReplayViewer", "CursorManager")] +script = ExtResource("2_twgab") +Runner = NodePath("../Runner") +ReplayViewer = NodePath("../ReplayViewer") +CursorManager = NodePath("../CursorManager") + +[node name="Runner" type="Node3D" parent="." unique_id=580620214 node_paths=PackedStringArray("HudManager", "Camera", "Grid", "Cursor", "Notes", "VideoStreamPlayer")] +script = ExtResource("2_qnb83") +HudManager = NodePath("HUD") +Camera = NodePath("Camera3D") +Grid = NodePath("HUD/Grid") +Cursor = NodePath("HUD/Grid/Cursor") +Notes = NodePath("Notes") +VideoStreamPlayer = NodePath("Video/VideoViewport/VideoStreamPlayer") + +[node name="HUD" type="Node3D" parent="Runner" unique_id=1234434255 node_paths=PackedStringArray("Runner")] +script = ExtResource("3_sww3w") +Runner = NodePath("..") + +[node name="Grid" type="MeshInstance3D" parent="Runner/HUD" unique_id=730517176] +mesh = SubResource("QuadMesh_aih2q") +skeleton = NodePath("../../..") +script = ExtResource("27_js0lu") + +[node name="Cursor" type="MeshInstance3D" parent="Runner/HUD/Grid" unique_id=292067677] +mesh = SubResource("QuadMesh_xij4d") +skeleton = NodePath("../../../..") + +[node name="CursorTrail" type="MultiMeshInstance3D" parent="Runner/HUD/Grid" unique_id=913051903] +material_override = SubResource("StandardMaterial3D_ssr8x") +multimesh = SubResource("MultiMesh_e28va") + +[node name="Health" type="MeshInstance3D" parent="Runner/HUD" unique_id=449857227] +transform = Transform3D(1, 0, 0, 0, 0.965926, 0.258819, 0, -0.258819, 0.965926, 0, -6.3, -10) +cast_shadow = 0 +mesh = SubResource("QuadMesh_b1hbo") +skeleton = NodePath("../../..") +script = ExtResource("4_js0lu") + +[node name="HealthViewport" type="SubViewport" parent="Runner/HUD/Health" unique_id=1637011839] +disable_3d = true +transparent_bg = true +canvas_item_default_texture_filter = 2 +size = Vector2i(544, 40) +size_2d_override = Vector2i(1088, 80) +size_2d_override_stretch = true +render_target_update_mode = 4 + +[node name="Background" type="TextureRect" parent="Runner/HUD/Health/HealthViewport" unique_id=2016213185] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -focus_mode = 0 -mouse_filter = 2 -stretch = true +texture = ExtResource("6_bbly8") -[node name="SubViewport" type="SubViewport" parent="SubViewportContainer" unique_id=671564774] +[node name="Main" type="TextureRect" parent="Runner/HUD/Health/HealthViewport" unique_id=366630384] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("7_wjg6i") +expand_mode = 1 +stretch_mode = 1 + +[node name="PanelLeft" type="MeshInstance3D" parent="Runner/HUD" unique_id=2045187203] +transform = Transform3D(0.984808, 0, 0.173648, 0, 1, 0, -0.173648, 0, 0.984808, -7.5, 0, -9) +material_override = SubResource("StandardMaterial3D_nody0") +mesh = SubResource("QuadMesh_ssyve") +skeleton = NodePath("../../..") +script = ExtResource("8_qnb83") + +[node name="PanelLeftViewport" type="SubViewport" parent="Runner/HUD/PanelLeft" unique_id=944518522] +disable_3d = true transparent_bg = true -handle_input_locally = false -screen_space_aa = 1 -size = Vector2i(1280, 720) -render_target_update_mode = 0 +canvas_item_default_texture_filter = 2 +size = Vector2i(250, 625) -[node name="ReplayViewer" type="Panel" parent="." unique_id=1117476839] -visible = false -anchors_preset = 12 -anchor_top = 1.0 +[node name="Background" type="TextureRect" parent="Runner/HUD/PanelLeft/PanelLeftViewport" unique_id=1374853699] +anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 -offset_left = 32.0 -offset_top = -84.0 -offset_right = -32.0 -offset_bottom = -32.0 grow_horizontal = 2 grow_vertical = 2 -mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_sijr0") +texture = ExtResource("11_sdncv") -[node name="Label" type="Label" parent="ReplayViewer" unique_id=919285545] -layout_mode = 1 +[node name="MultiplierProgress" type="Panel" parent="Runner/HUD/PanelLeft/PanelLeftViewport" unique_id=1067613329] +clip_children = 2 +material = SubResource("ShaderMaterial_x2xye") anchors_preset = -1 -offset_right = 40.0 -grow_vertical = 0 -text = "Press F1 to toggle" -label_settings = SubResource("LabelSettings_a70tm") +anchor_left = 0.5 +anchor_top = 0.65 +anchor_right = 0.5 +anchor_bottom = 0.65 +offset_left = -128.0 +offset_top = -128.0 +offset_right = 128.0 +offset_bottom = 128.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_56f8v") -[node name="Time" type="Label" parent="ReplayViewer" unique_id=1277133470] -layout_mode = 1 +[node name="Score" type="Label" parent="Runner/HUD/PanelLeft/PanelLeftViewport" unique_id=1330190508] anchors_preset = -1 -anchor_bottom = 1.0 -offset_left = 64.0 -offset_top = 12.0 -offset_right = 164.0 -offset_bottom = -12.0 -text = "0:00 / 0:00" +anchor_left = 0.5 +anchor_top = 0.35 +anchor_right = 0.5 +anchor_bottom = 0.35 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("4_rvp5o") +text = "0" +label_settings = SubResource("LabelSettings_usmyc") horizontal_alignment = 1 -vertical_alignment = 1 -[node name="Pause" type="TextureButton" parent="ReplayViewer" unique_id=1677045606] -texture_repeat = 1 -layout_mode = 1 +[node name="Multiplier" type="Label" parent="Runner/HUD/PanelLeft/PanelLeftViewport" unique_id=1660490363] anchors_preset = -1 -anchor_top = 0.5 -anchor_bottom = 0.5 -offset_left = 12.0 -offset_right = 52.0 -grow_vertical = 2 -texture_normal = ExtResource("23_7silw") -stretch_mode = 3 - -[node name="Seek" type="HSlider" parent="ReplayViewer" unique_id=1706750343] -layout_mode = 1 -anchors_preset = -1 -anchor_top = 0.5 -anchor_right = 1.0 -anchor_bottom = 0.5 -offset_left = 192.0 -offset_right = -32.0 +anchor_left = 0.5 +anchor_top = 0.65 +anchor_right = 0.5 +anchor_bottom = 0.65 +grow_horizontal = 2 grow_vertical = 2 theme = ExtResource("4_rvp5o") -max_value = 1.0 -step = 0.0 +text = "1x" +label_settings = SubResource("LabelSettings_usmyc") +horizontal_alignment = 1 -[node name="NotificationHolder" type="VBoxContainer" parent="." unique_id=1425635507] -anchors_preset = 3 -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -210.0 -offset_top = -110.0 -offset_right = -10.0 -offset_bottom = -10.0 -grow_horizontal = 0 -grow_vertical = 0 -mouse_filter = 2 -alignment = 2 +[node name="PanelRight" type="MeshInstance3D" parent="Runner/HUD" unique_id=1993445313] +transform = Transform3D(0.984808, 0, -0.173648, 0, 1, 0, 0.173648, 0, 0.984808, 7.5, 0, -9) +material_override = SubResource("StandardMaterial3D_ro5ir") +mesh = SubResource("QuadMesh_ssyve") +skeleton = NodePath("../../..") +script = ExtResource("13_sww3w") -[node name="Menu" type="Panel" parent="." unique_id=856970091] -visible = false -modulate = Color(1, 1, 1, 0) +[node name="PanelRightViewport" type="SubViewport" parent="Runner/HUD/PanelRight" unique_id=623021244] +process_mode = 3 +disable_3d = true +transparent_bg = true +canvas_item_default_texture_filter = 2 +size = Vector2i(250, 625) + +[node name="Background" type="TextureRect" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=1856482217] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -mouse_filter = 2 -theme_override_styles/panel = SubResource("StyleBoxEmpty_d5aou") +texture = ExtResource("12_whe6p") -[node name="Background" type="ColorRect" parent="Menu" unique_id=2094618318] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 +[node name="Accuracy" type="Label" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=1669266042] +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 0.35 +anchor_right = 0.5 +anchor_bottom = 0.35 grow_horizontal = 2 grow_vertical = 2 -mouse_filter = 2 -color = Color(0, 0, 0, 0.25098) +theme = ExtResource("4_rvp5o") +text = "100.00%" +label_settings = SubResource("LabelSettings_usmyc") +horizontal_alignment = 1 -[node name="Button" type="Button" parent="Menu" unique_id=1826268412] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 +[node name="Hits" type="Label" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=2096630009] +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 0.625 +anchor_right = 0.5 +anchor_bottom = 0.625 +offset_right = -25.0 +grow_horizontal = 0 grow_vertical = 2 -button_mask = 3 -flat = true +size_flags_horizontal = 4 +theme = ExtResource("4_rvp5o") +text = "0" +label_settings = SubResource("LabelSettings_5bid2") +horizontal_alignment = 2 -[node name="Holder" type="Panel" parent="Menu" unique_id=1204474678] -layout_mode = 1 +[node name="Misses" type="Label" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=1379241442] anchors_preset = -1 anchor_left = 0.5 +anchor_top = 0.625 +anchor_right = 0.5 +anchor_bottom = 0.625 +offset_left = 25.0 +grow_vertical = 2 +size_flags_horizontal = 4 +theme = ExtResource("4_rvp5o") +text = "0" +label_settings = SubResource("LabelSettings_v63ri") + +[node name="SimpleMisses" type="Label" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=1871751782] +visible = false +anchors_preset = 8 +anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -offset_left = -220.0 -offset_top = -280.0 -offset_right = 220.0 -offset_bottom = 280.0 -mouse_filter = 1 -theme_override_styles/panel = SubResource("StyleBoxFlat_43mxh") - -[node name="Header" type="Label" parent="Menu/Holder" unique_id=544084605] -layout_mode = 1 -anchors_preset = -1 -anchor_right = 1.0 -anchor_bottom = 0.2 -offset_top = 16.0 -text = "PAUSED" -label_settings = SubResource("LabelSettings_pwlqp") -horizontal_alignment = 1 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +theme = ExtResource("4_rvp5o") +text = "0" +label_settings = SubResource("LabelSettings_g4ffu") -[node name="Disclaimer" type="Label" parent="Menu/Holder" unique_id=1874627546] -layout_mode = 1 +[node name="Sum" type="Label" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=150248329] anchors_preset = -1 -anchor_top = 0.8 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_bottom = -16.0 -text = "NOTE: Pausing disqualifies your pass" -label_settings = SubResource("LabelSettings_tjduq") +anchor_left = 0.5 +anchor_top = 0.7 +anchor_right = 0.5 +anchor_bottom = 0.7 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +theme = ExtResource("4_rvp5o") +text = "0" +label_settings = SubResource("LabelSettings_s1j5a") horizontal_alignment = 1 -vertical_alignment = 2 -[node name="Resume" type="Button" parent="Menu/Holder" unique_id=616429602] -layout_mode = 1 +[node name="HitsIcon" type="TextureRect" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=482771805] anchors_preset = -1 -anchor_top = 0.2 -anchor_right = 1.0 -anchor_bottom = 0.35 -offset_left = 32.0 -offset_right = -32.0 -mouse_default_cursor_shape = 2 -theme = SubResource("Theme_e1j6l") -theme_override_font_sizes/font_size = 28 -text = "RESUME" +anchor_left = 0.5 +anchor_top = 0.625 +anchor_right = 0.5 +anchor_bottom = 0.625 +offset_top = -12.0 +offset_right = -2.0 +offset_bottom = 7.0 +grow_horizontal = 0 +grow_vertical = 2 +theme = ExtResource("4_rvp5o") +texture = ExtResource("13_6rhgc") +expand_mode = 2 -[node name="Restart" type="Button" parent="Menu/Holder" unique_id=865082248] -layout_mode = 1 +[node name="MissesIcon" type="TextureRect" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=901292564] anchors_preset = -1 -anchor_top = 0.35 -anchor_right = 1.0 -anchor_bottom = 0.5 -offset_left = 32.0 -offset_right = -32.0 -mouse_default_cursor_shape = 2 -theme = SubResource("Theme_e1j6l") -theme_override_font_sizes/font_size = 28 -text = "RESTART" +anchor_left = 0.5 +anchor_top = 0.625 +anchor_right = 0.5 +anchor_bottom = 0.625 +offset_left = 2.0 +offset_top = -12.0 +offset_bottom = 7.0 +grow_vertical = 2 +theme = ExtResource("4_rvp5o") +texture = ExtResource("14_bqf11") +expand_mode = 2 -[node name="Settings" type="Button" parent="Menu/Holder" unique_id=730914507] -layout_mode = 1 +[node name="Line" type="ColorRect" parent="Runner/HUD/PanelRight/PanelRightViewport" unique_id=629344643] +material = SubResource("ShaderMaterial_gc8yu") anchors_preset = -1 -anchor_top = 0.5 +anchor_top = 0.65 anchor_right = 1.0 anchor_bottom = 0.65 -offset_left = 32.0 -offset_right = -32.0 -mouse_default_cursor_shape = 2 -theme = SubResource("Theme_e1j6l") -theme_override_font_sizes/font_size = 28 -text = "SETTINGS" +offset_left = 16.0 +offset_right = -16.0 +offset_bottom = 2.0 +grow_horizontal = 2 +grow_vertical = 2 [node name="Quit" type="Button" parent="Menu/Holder" unique_id=2118605072] layout_mode = 1 @@ -709,12 +855,14 @@ texture = ExtResource("7_wjg6i") expand_mode = 1 stretch_mode = 1 -[node name="ProgressBar" type="MeshInstance3D" parent="." unique_id=528447019] +[node name="ProgressBar" type="MeshInstance3D" parent="Runner/HUD" unique_id=528447019] transform = Transform3D(1, 0, 0, 0, 0.965926, -0.258819, 0, 0.258819, 0.965926, 0, 6.3, -10) cast_shadow = 0 mesh = SubResource("QuadMesh_7mook") +skeleton = NodePath("../../..") +script = ExtResource("7_gxtfn") -[node name="ProgressBarViewport" type="SubViewport" parent="ProgressBar" unique_id=658400463] +[node name="ProgressBarViewport" type="SubViewport" parent="Runner/HUD/ProgressBar" unique_id=658400463] disable_3d = true transparent_bg = true canvas_item_default_texture_filter = 2 @@ -723,7 +871,7 @@ size_2d_override = Vector2i(1088, 80) size_2d_override_stretch = true render_target_update_mode = 4 -[node name="Background" type="TextureRect" parent="ProgressBar/ProgressBarViewport" unique_id=1129572664] +[node name="Background" type="TextureRect" parent="Runner/HUD/ProgressBar/ProgressBarViewport" unique_id=1129572664] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -731,7 +879,7 @@ grow_horizontal = 2 grow_vertical = 2 texture = ExtResource("8_eu3ux") -[node name="Main" type="TextureRect" parent="ProgressBar/ProgressBarViewport" unique_id=2016165310] +[node name="Main" type="TextureRect" parent="Runner/HUD/ProgressBar/ProgressBarViewport" unique_id=2016165310] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -741,20 +889,94 @@ texture = ExtResource("9_nde5v") expand_mode = 1 stretch_mode = 1 -[node name="Video" type="MeshInstance3D" parent="." unique_id=1036410191] +[node name="Progress" type="Node3D" parent="Runner/HUD" unique_id=533288593] +script = ExtResource("21_twgab") + +[node name="Label" type="Label3D" parent="Runner/HUD/Progress" unique_id=496335082] +transform = Transform3D(3.5, 0, 0, 0, 3.38074, -0.905867, 0, 0.905867, 3.38074, 0, 7, -10) +pixel_size = 0.0025 +modulate = Color(1, 1, 1, 0.74509805) +outline_modulate = Color(0, 0, 0, 0) +text = "0:00 / 0:00" +font = ExtResource("12_axsek") +font_size = 64 + +[node name="Title" type="Node3D" parent="Runner/HUD" unique_id=1847012801] +script = ExtResource("22_02fa5") + +[node name="Label" type="Label3D" parent="Runner/HUD/Title" unique_id=841289409] +transform = Transform3D(2, 0, 0, 0, 1.93185, -0.517638, 0, 0.517638, 1.93185, 0, 8.25, -10) +outline_modulate = Color(0, 0, 0, 0) +text = "N/A - N/A" +font = ExtResource("12_axsek") +font_size = 64 +autowrap_mode = 3 +width = 2500.0 + +[node name="Combo" type="Node3D" parent="Runner/HUD" unique_id=2131933661] +script = ExtResource("20_suyxu") + +[node name="Label" type="Label3D" parent="Runner/HUD/Combo" unique_id=1330017059] +transform = Transform3D(7, 0, 0, 0, 6.89365, -1.21554, 0, 1.21554, 6.89365, 0, 3.5, -10) +pixel_size = 0.0025 +modulate = Color(1, 1, 1, 0) +outline_modulate = Color(1, 1, 1, 0.39215687) +text = "0" +font = ExtResource("5_pxgh4") +font_size = 64 +outline_size = 4 + +[node name="Speed" type="Node3D" parent="Runner/HUD" unique_id=267334765] +transform = Transform3D(7, 0, 0, 0, 6.76148, 1.81173, 0, -1.81173, 6.76148, 0, -7.5, -10) +script = ExtResource("28_gxtfn") + +[node name="Label" type="Label3D" parent="Runner/HUD/Speed" unique_id=1294863906] +transform = Transform3D(0.99999994, 0, 0, 0, 0.99999994, 0, 0, 0, 0.99999994, 0, 0, 0) +pixel_size = 0.0025 +modulate = Color(1, 1, 1, 0.39215687) +outline_modulate = Color(0, 0, 0, 0) +text = "1.00x" +font = ExtResource("5_pxgh4") + +[node name="Skip" type="Node3D" parent="Runner/HUD" unique_id=1658610942] +script = ExtResource("25_nepv3") + +[node name="Label" type="Label3D" parent="Runner/HUD/Skip" unique_id=1231864437] +transform = Transform3D(7, 0, 0, 0, 6.76148, 1.81173, 0, -1.81173, 6.76148, 0, -5, -10) +pixel_size = 0.0025 +modulate = Color(1, 1, 1, 0) +outline_modulate = Color(0, 0, 0, 0) +text = "Press Space to skip" +font = ExtResource("12_axsek") + +[node name="Camera3D" type="Camera3D" parent="Runner" unique_id=112686709] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3.75) +current = true +fov = 70.0 +size = 5.0 + +[node name="Notes" type="MultiMeshInstance3D" parent="Runner" unique_id=77746100 node_paths=PackedStringArray("Runner")] +material_override = SubResource("StandardMaterial3D_73q55") +cast_shadow = 0 +multimesh = SubResource("MultiMesh_almyi") +script = ExtResource("3_ew8wr") +Runner = NodePath("..") + +[node name="Video" type="MeshInstance3D" parent="Runner" unique_id=1036410191] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.479675, -0.459572, -99.9929) visible = false material_override = SubResource("StandardMaterial3D_rj3ky") transparency = 1.0 cast_shadow = 0 mesh = SubResource("QuadMesh_3p7x2") +skeleton = NodePath("../..") -[node name="VideoViewport" type="SubViewport" parent="Video" unique_id=866766269] +[node name="VideoViewport" type="SubViewport" parent="Runner/Video" unique_id=866766269] transparent_bg = true canvas_item_default_texture_filter = 2 size = Vector2i(1920, 1080) -[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="Video/VideoViewport" unique_id=61327167] +[node name="VideoStreamPlayer" type="VideoStreamPlayer" parent="Runner/Video/VideoViewport" unique_id=61327167] material = SubResource("ShaderMaterial_4qlgx") anchors_preset = 15 anchor_right = 1.0 @@ -764,65 +986,84 @@ grow_vertical = 2 volume_db = -80.0 expand = true -[node name="PanelLeft" type="MeshInstance3D" parent="." unique_id=2045187203] -transform = Transform3D(0.984808, 0, 0.173648, 0, 1, 0, -0.173648, 0, 0.984808, -7.5, 0, -9) -material_override = SubResource("StandardMaterial3D_nody0") -mesh = SubResource("QuadMesh_ssyve") - -[node name="PanelLeftViewport" type="SubViewport" parent="PanelLeft" unique_id=944518522] -disable_3d = true -transparent_bg = true -canvas_item_default_texture_filter = 2 -size = Vector2i(250, 625) - -[node name="Background" type="TextureRect" parent="PanelLeft/PanelLeftViewport" unique_id=1374853699] +[node name="SubViewportContainer" type="SubViewportContainer" parent="." unique_id=1797381631] +visible = false +z_index = -1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -texture = ExtResource("11_sdncv") +focus_mode = 0 +mouse_filter = 2 +stretch = true -[node name="MultiplierProgress" type="Panel" parent="PanelLeft/PanelLeftViewport" unique_id=1067613329] -clip_children = 2 -material = SubResource("ShaderMaterial_x2xye") -anchors_preset = -1 -anchor_left = 0.5 -anchor_top = 0.65 -anchor_right = 0.5 -anchor_bottom = 0.65 -offset_left = -128.0 -offset_top = -128.0 -offset_right = 128.0 -offset_bottom = 128.0 -theme_override_styles/panel = SubResource("StyleBoxFlat_56f8v") +[node name="SubViewport" type="SubViewport" parent="SubViewportContainer" unique_id=671564774] +transparent_bg = true +handle_input_locally = false +screen_space_aa = 1 +size = Vector2i(1280, 720) +render_target_update_mode = 0 -[node name="Score" type="Label" parent="PanelLeft/PanelLeftViewport" unique_id=1330190508] -anchors_preset = -1 -anchor_left = 0.5 -anchor_top = 0.35 -anchor_right = 0.5 -anchor_bottom = 0.35 +[node name="ReplayViewer" type="Panel" parent="." unique_id=1117476839] +visible = false +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 32.0 +offset_top = -84.0 +offset_right = -32.0 +offset_bottom = -32.0 grow_horizontal = 2 grow_vertical = 2 -theme = ExtResource("4_rvp5o") -text = "0" -label_settings = SubResource("LabelSettings_usmyc") +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_sijr0") + +[node name="Label" type="Label" parent="ReplayViewer" unique_id=919285545] +layout_mode = 1 +anchors_preset = -1 +offset_right = 40.0 +grow_vertical = 0 +text = "Press F1 to toggle" +label_settings = SubResource("LabelSettings_a70tm") + +[node name="Time" type="Label" parent="ReplayViewer" unique_id=1277133470] +layout_mode = 1 +anchors_preset = -1 +anchor_bottom = 1.0 +offset_left = 64.0 +offset_top = 12.0 +offset_right = 164.0 +offset_bottom = -12.0 +text = "0:00 / 0:00" horizontal_alignment = 1 +vertical_alignment = 1 -[node name="Multiplier" type="Label" parent="PanelLeft/PanelLeftViewport" unique_id=1660490363] +[node name="Pause" type="TextureButton" parent="ReplayViewer" unique_id=1677045606] +texture_repeat = 1 +layout_mode = 1 anchors_preset = -1 -anchor_left = 0.5 -anchor_top = 0.65 -anchor_right = 0.5 -anchor_bottom = 0.65 -grow_horizontal = 2 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 12.0 +offset_right = 52.0 grow_vertical = 2 -theme = ExtResource("4_rvp5o") -text = "1x" -label_settings = SubResource("LabelSettings_usmyc") -horizontal_alignment = 1 +texture_normal = ExtResource("23_7silw") +stretch_mode = 3 +[node name="Seek" type="HSlider" parent="ReplayViewer" unique_id=1706750343] +layout_mode = 1 +anchors_preset = -1 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = 192.0 +offset_right = -32.0 +grow_vertical = 2 +theme = ExtResource("4_rvp5o") +max_value = 1.0 +step = 0.0 [node name="PauseTitle" type="Label" parent="PanelLeft/PanelLeftViewport" unique_id=531787894 groups=["pause_text"]] anchors_preset = -1 anchor_left = 0.5 @@ -860,171 +1101,146 @@ transform = Transform3D(0.984808, 0, -0.173648, 0, 1, 0, 0.173648, 0, 0.984808, material_override = SubResource("StandardMaterial3D_ro5ir") mesh = SubResource("QuadMesh_ssyve") -[node name="PanelRightViewport" type="SubViewport" parent="PanelRight" unique_id=623021244] -process_mode = 3 -disable_3d = true -transparent_bg = true -canvas_item_default_texture_filter = 2 -size = Vector2i(250, 625) +[node name="NotificationHolder" type="VBoxContainer" parent="." unique_id=1425635507] +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -210.0 +offset_top = -110.0 +offset_right = -10.0 +offset_bottom = -10.0 +grow_horizontal = 0 +grow_vertical = 0 +mouse_filter = 2 +alignment = 2 -[node name="Background" type="TextureRect" parent="PanelRight/PanelRightViewport" unique_id=1856482217] +[node name="Menu" type="Panel" parent="." unique_id=856970091] +visible = false +modulate = Color(1, 1, 1, 0) anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -texture = ExtResource("12_whe6p") +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxEmpty_d5aou") -[node name="Accuracy" type="Label" parent="PanelRight/PanelRightViewport" unique_id=1669266042] -anchors_preset = -1 -anchor_left = 0.5 -anchor_top = 0.35 -anchor_right = 0.5 -anchor_bottom = 0.35 +[node name="Background" type="ColorRect" parent="Menu" unique_id=2094618318] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -theme = ExtResource("4_rvp5o") -text = "100.00%" -label_settings = SubResource("LabelSettings_usmyc") -horizontal_alignment = 1 +mouse_filter = 2 +color = Color(0, 0, 0, 0.25098) -[node name="Hits" type="Label" parent="PanelRight/PanelRightViewport" unique_id=2096630009] -anchors_preset = -1 -anchor_left = 0.5 -anchor_top = 0.625 -anchor_right = 0.5 -anchor_bottom = 0.625 -offset_right = -25.0 -grow_horizontal = 0 +[node name="Button" type="Button" parent="Menu" unique_id=1826268412] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 grow_vertical = 2 -size_flags_horizontal = 4 -theme = ExtResource("4_rvp5o") -text = "0" -label_settings = SubResource("LabelSettings_5bid2") -horizontal_alignment = 2 +button_mask = 3 +flat = true -[node name="Misses" type="Label" parent="PanelRight/PanelRightViewport" unique_id=1379241442] +[node name="Holder" type="Panel" parent="Menu" unique_id=1204474678] +layout_mode = 1 anchors_preset = -1 anchor_left = 0.5 -anchor_top = 0.625 -anchor_right = 0.5 -anchor_bottom = 0.625 -offset_left = 25.0 -grow_vertical = 2 -size_flags_horizontal = 4 -theme = ExtResource("4_rvp5o") -text = "0" -label_settings = SubResource("LabelSettings_v63ri") - -[node name="SimpleMisses" type="Label" parent="PanelRight/PanelRightViewport" unique_id=1871751782] -visible = false -anchors_preset = 8 -anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 4 -theme = ExtResource("4_rvp5o") -text = "0" -label_settings = SubResource("LabelSettings_g4ffu") +offset_left = -220.0 +offset_top = -280.0 +offset_right = 220.0 +offset_bottom = 280.0 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_43mxh") -[node name="Sum" type="Label" parent="PanelRight/PanelRightViewport" unique_id=150248329] +[node name="Header" type="Label" parent="Menu/Holder" unique_id=544084605] +layout_mode = 1 anchors_preset = -1 -anchor_left = 0.5 -anchor_top = 0.7 -anchor_right = 0.5 -anchor_bottom = 0.7 -grow_horizontal = 2 -grow_vertical = 2 -size_flags_horizontal = 4 -theme = ExtResource("4_rvp5o") -text = "0" -label_settings = SubResource("LabelSettings_s1j5a") +anchor_right = 1.0 +anchor_bottom = 0.2 +offset_top = 16.0 +text = "PAUSED" +label_settings = SubResource("LabelSettings_pwlqp") horizontal_alignment = 1 -[node name="HitsIcon" type="TextureRect" parent="PanelRight/PanelRightViewport" unique_id=482771805] +[node name="Disclaimer" type="Label" parent="Menu/Holder" unique_id=1874627546] +layout_mode = 1 anchors_preset = -1 -anchor_left = 0.5 -anchor_top = 0.625 -anchor_right = 0.5 -anchor_bottom = 0.625 -offset_top = -12.0 -offset_right = -2.0 -offset_bottom = 7.0 -grow_horizontal = 0 -grow_vertical = 2 -theme = ExtResource("4_rvp5o") -texture = ExtResource("13_6rhgc") -expand_mode = 2 +anchor_top = 0.8 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_bottom = -16.0 +text = "NOTE: Pausing disqualifies your pass" +label_settings = SubResource("LabelSettings_tjduq") +horizontal_alignment = 1 +vertical_alignment = 2 -[node name="MissesIcon" type="TextureRect" parent="PanelRight/PanelRightViewport" unique_id=901292564] +[node name="Resume" type="Button" parent="Menu/Holder" unique_id=616429602] +layout_mode = 1 anchors_preset = -1 -anchor_left = 0.5 -anchor_top = 0.625 -anchor_right = 0.5 -anchor_bottom = 0.625 -offset_left = 2.0 -offset_top = -12.0 -offset_bottom = 7.0 -grow_vertical = 2 -theme = ExtResource("4_rvp5o") -texture = ExtResource("14_bqf11") -expand_mode = 2 +anchor_top = 0.2 +anchor_right = 1.0 +anchor_bottom = 0.35 +offset_left = 32.0 +offset_right = -32.0 +mouse_default_cursor_shape = 2 +theme = SubResource("Theme_e1j6l") +theme_override_font_sizes/font_size = 28 +text = "RESUME" -[node name="Line" type="ColorRect" parent="PanelRight/PanelRightViewport" unique_id=629344643] -material = SubResource("ShaderMaterial_gc8yu") +[node name="Restart" type="Button" parent="Menu/Holder" unique_id=865082248] +layout_mode = 1 anchors_preset = -1 -anchor_top = 0.65 +anchor_top = 0.35 anchor_right = 1.0 -anchor_bottom = 0.65 -offset_left = 16.0 -offset_right = -16.0 -offset_bottom = 2.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="Progress" type="Label3D" parent="." unique_id=496335082] -transform = Transform3D(3.5, 0, 0, 0, 3.38074, -0.905867, 0, 0.905867, 3.38074, 0, 7, -10) -pixel_size = 0.0025 -modulate = Color(1, 1, 1, 0.74509805) -outline_modulate = Color(0, 0, 0, 0) -text = "0:00 / 0:00" -font = ExtResource("12_axsek") -font_size = 64 - -[node name="Title" type="Label3D" parent="." unique_id=841289409] -transform = Transform3D(2, 0, 0, 0, 1.93185, -0.517638, 0, 0.517638, 1.93185, 0, 8.25, -10) -outline_modulate = Color(0, 0, 0, 0) -text = "N/A - N/A" -font = ExtResource("12_axsek") -font_size = 64 -autowrap_mode = 3 -width = 2500.0 +anchor_bottom = 0.5 +offset_left = 32.0 +offset_right = -32.0 +mouse_default_cursor_shape = 2 +theme = SubResource("Theme_e1j6l") +theme_override_font_sizes/font_size = 28 +text = "RESTART" -[node name="Combo" type="Label3D" parent="." unique_id=1330017059] -transform = Transform3D(7, 0, 0, 0, 6.89365, -1.21554, 0, 1.21554, 6.89365, 0, 3.5, -10) -pixel_size = 0.0025 -modulate = Color(1, 1, 1, 0) -outline_modulate = Color(1, 1, 1, 0.39215687) -text = "0" -font = ExtResource("5_pxgh4") -font_size = 64 -outline_size = 4 +[node name="Settings" type="Button" parent="Menu/Holder" unique_id=730914507] +layout_mode = 1 +anchors_preset = -1 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.65 +offset_left = 32.0 +offset_right = -32.0 +mouse_default_cursor_shape = 2 +theme = SubResource("Theme_e1j6l") +theme_override_font_sizes/font_size = 28 +text = "SETTINGS" -[node name="Speed" type="Label3D" parent="." unique_id=1294863906] -transform = Transform3D(7, 0, 0, 0, 6.76148, 1.81173, 0, -1.81173, 6.76148, 0, -7.5, -10) -pixel_size = 0.0025 -modulate = Color(1, 1, 1, 0.39215687) -outline_modulate = Color(0, 0, 0, 0) -text = "1.00x" -font = ExtResource("5_pxgh4") +[node name="Quit" type="Button" parent="Menu/Holder" unique_id=2118605072] +layout_mode = 1 +anchors_preset = -1 +anchor_top = 0.65 +anchor_right = 1.0 +anchor_bottom = 0.8 +offset_left = 32.0 +offset_right = -32.0 +mouse_default_cursor_shape = 2 +theme = SubResource("Theme_e1j6l") +theme_override_font_sizes/font_size = 28 +text = "GIVE UP" -[node name="Skip" type="Label3D" parent="." unique_id=1231864437] -transform = Transform3D(7, 0, 0, 0, 6.76148, 1.81173, 0, -1.81173, 6.76148, 0, -5, -10) -pixel_size = 0.0025 -modulate = Color(1, 1, 1, 0) -outline_modulate = Color(0, 0, 0, 0) -text = "Press Space to skip" -font = ExtResource("12_axsek") +[node name="Transition" type="ColorRect" parent="." unique_id=784178581] +self_modulate = Color(1, 1, 1, 0) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0, 0, 0, 1) diff --git a/scenes/new_game.tscn b/scenes/new_game.tscn deleted file mode 100644 index 8437a94c..00000000 --- a/scenes/new_game.tscn +++ /dev/null @@ -1,52 +0,0 @@ -[gd_scene load_steps=10 format=3 uid="uid://v4d7lpllbx57"] - -[ext_resource type="Script" uid="uid://ucehkkcmc7xy" path="res://scripts/game/GameComponent.cs" id="1_l8kns"] -[ext_resource type="Texture2D" uid="uid://b3g4aw54n6bow" path="res://skin/game/grid.png" id="2_cwj52"] -[ext_resource type="Script" uid="uid://ddtnij8m4eipl" path="res://scripts/game/ui/Grid.cs" id="3_s1qwq"] -[ext_resource type="Texture2D" uid="uid://3e1tjlr4m0wt" path="res://skin/game/cursor.png" id="4_4op33"] -[ext_resource type="Script" uid="uid://cvaggwr3rrenl" path="res://scripts/game/renderers/NoteRenderer.cs" id="5_oee5v"] - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_a6jk1"] -resource_name = "Material" -transparency = 1 -shading_mode = 0 -albedo_texture = ExtResource("2_cwj52") -texture_repeat = false - -[sub_resource type="QuadMesh" id="QuadMesh_c2vyr"] -material = SubResource("StandardMaterial3D_a6jk1") -size = Vector2(4, 4) - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lvhmj"] -transparency = 1 -shading_mode = 0 -disable_ambient_light = true -disable_fog = true -albedo_texture = ExtResource("4_4op33") -texture_filter = 5 -texture_repeat = false -distance_fade_min_distance = 1076.07 - -[sub_resource type="QuadMesh" id="QuadMesh_ifke4"] -material = SubResource("StandardMaterial3D_lvhmj") -size = Vector2(0.263, 0.263) - -[node name="Game" type="Node3D" node_paths=PackedStringArray("Camera", "InterfaceComponents", "Renderers")] -script = ExtResource("1_l8kns") -Camera = NodePath("Camera") -InterfaceComponents = [NodePath("Grid")] -Renderers = [NodePath("Notes")] - -[node name="Camera" type="Camera3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3.75) - -[node name="Grid" type="MeshInstance3D" parent="." node_paths=PackedStringArray("Cursor")] -mesh = SubResource("QuadMesh_c2vyr") -script = ExtResource("3_s1qwq") -Cursor = NodePath("Cursor") - -[node name="Cursor" type="MeshInstance3D" parent="Grid"] -mesh = SubResource("QuadMesh_ifke4") - -[node name="Notes" type="Node3D" parent="."] -script = ExtResource("5_oee5v") diff --git a/scripts/PlayerInputController.cs b/scripts/PlayerInputController.cs new file mode 100644 index 00000000..ddd152d7 --- /dev/null +++ b/scripts/PlayerInputController.cs @@ -0,0 +1,96 @@ +using System; +using Godot; + +public partial class PlayerInputController : Node +{ + // I'm not sure if it's planned that the player can change keybindings + // So these bindings are hardcoded (for now) -thom + + /// + /// Fired when the mouse moves. + /// + /// First parameter is relative movement (delta), + /// second parameter is absolute screen position. + /// + /// + public event Action OnMouseMove; + + public event Action OnTogglePaused; + public event Action OnRestartPressed; + public event Action OnToggleReplayViewerVisibility; + public event Action OnPauseOrSkip; + public event Action OnToggleFade; + public event Action OnTogglePushback; + public event Action OnLeftMouseButton; + public bool IsEnabled { get; private set; } = true; + + public override void _Input(InputEvent @event) + { + if (!IsEnabled) return; + + handleInput(@event); + } + + private void handleInput(InputEvent @event) + { + switch (@event) + { + case InputEventMouseMotion: + case InputEventMouseButton { ButtonIndex: MouseButton.Left }: + handleMouseInput(@event); + break; + case InputEventKey {Keycode: Key.Escape}: + case InputEventKey {Keycode: Key.F1}: + case InputEventKey {Keycode: Key.Space}: + case InputEventKey {Keycode: Key.F}: + case InputEventKey {Keycode: Key.P}: + case InputEventKey {Keycode: Key.Quoteleft}: + handleKeyboardInput(@event); + break; + } + } + + private void handleMouseInput(InputEvent @event) + { + if (@event is InputEventMouseMotion motion) + OnMouseMove?.Invoke(motion.Relative, motion.Position); + + if (@event is InputEventMouseButton mouseButton) + { + if (!mouseButton.Pressed || mouseButton.DoubleClick) return; + + OnLeftMouseButton?.Invoke(mouseButton.Pressed); + } + } + + private void handleKeyboardInput(InputEvent @event) + { + InputEventKey key = (InputEventKey)@event; + + if (!key.Pressed || key.Echo) return; + + switch (key) + { + case {Keycode: Key.Escape}: + OnTogglePaused?.Invoke(); + break; + case {Keycode: Key.Quoteleft}: + OnRestartPressed?.Invoke(); + break; + case {Keycode: Key.F1}: + OnToggleReplayViewerVisibility?.Invoke(); + break; + case {Keycode: Key.Space}: + OnPauseOrSkip?.Invoke(); + break; + case {Keycode: Key.F}: + OnToggleFade?.Invoke(); + break; + case {Keycode: Key.P}: + OnTogglePushback?.Invoke(); + break; + } + } + + public void ToggleState() => IsEnabled = !IsEnabled; +} diff --git a/scripts/PlayerInputController.cs.uid b/scripts/PlayerInputController.cs.uid new file mode 100644 index 00000000..5ffcca2f --- /dev/null +++ b/scripts/PlayerInputController.cs.uid @@ -0,0 +1 @@ +uid://c8d872aoiqglo diff --git a/scripts/Rhythia.cs b/scripts/Rhythia.cs index 88bf6777..04dd6b80 100644 --- a/scripts/Rhythia.cs +++ b/scripts/Rhythia.cs @@ -124,7 +124,7 @@ public override async void _Ready() } } - LegacyRunner.Play(MapParser.Decode(matching[0].MapFilePath), matching[0].Speed, matching[0].StartFrom, matching[0].Modifiers, null, [.. matching]); + GameScene.Play(MapParser.Decode(matching[0].MapFilePath), matching[0].Speed, matching[0].StartFrom, matching[0].Modifiers, null, [.. matching]); } })); @@ -139,14 +139,14 @@ public static void Quit() } Quitting = true; + +// Logger.Log("Attempting to quit..."); - Logger.Log("Attempting to quit..."); +// var settings = SettingsManager.Instance.Settings; - var settings = SettingsManager.Instance.Settings; - - if (!LegacyRunner.CurrentAttempt.IsReplay) + if (GameScene.Attempt != null && !GameScene.Attempt.IsReplay) { - LegacyRunner.CurrentAttempt.Stop(); + GameScene.Instance.Runner.Stop(); } Stats.Instance.TotalPlaytime += (Time.GetTicksUsec() - Constants.STARTED) / 1000000; @@ -170,11 +170,8 @@ public override void _Notification(int what) { if (what == NotificationWMCloseRequest) { - if (SceneManager.Scene != null && SceneManager.Scene is LegacyRunner) - { - Stats.Instance.RageQuits++; - } - + if (SceneManager.Scene != null && SceneManager.Scene is GameScene) + Stats.RageQuits++; Quit(); } else if (what == NotificationApplicationFocusOut) diff --git a/scripts/SceneManager.cs b/scripts/SceneManager.cs index 5404a0b2..f96cad3c 100644 --- a/scripts/SceneManager.cs +++ b/scripts/SceneManager.cs @@ -120,7 +120,6 @@ private static void addSpace(BaseSpace space, bool addToScene = false) } backgroundContainer.Visible = !addToScene; - Space = space; } diff --git a/scripts/game/Attempt.cs b/scripts/game/Attempt.cs index 4e9585db..22837acf 100644 --- a/scripts/game/Attempt.cs +++ b/scripts/game/Attempt.cs @@ -4,35 +4,89 @@ public partial class Attempt : GodotObject { - public bool IsReplay { get; set; } - - public bool Paused { get; set; } - - public CameraMode CameraMode { get; set; } = new CameraLock(); - - public Map Map { get; set; } - - public double Progress { get; set; } - - public Vector3 CameraPosition { get; set; } = new Vector3(0, 0, 3.75f); - - public Vector3 CameraRotation { get; set; } = Vector3.Zero; - - public Vector2 CursorPosition { get; set; } = new(); - - public Vector2 RawCursorPosition { get; set; } = new(); - - public Vector3 CameraBasisZ { get; set; } = new(); - - public int Speed { get; set; } - - public List Mods { get; set; } = new(); - - public Dictionary> Objects { get; set; } = new(); - - public SettingsProfile Settings { get; set; } = new(); - - public double DistanceMM { get; set; } - - public Replay? Replay { get; set; } + public ulong TimeStarted; + public double DeathTime = -1; + public string ID; + public Map Map; + //public List Mods { get; set; } = new(); + public Dictionary Mods {get; set;} = new(); + public Dictionary> Objects { get; set; } = new(); + public SettingsProfile Settings; + public bool IsReplay = false; + public bool Stopped = false; + public bool Paused = false; + public bool Alive = true; + public bool CanSkip = false; + public bool Qualifies = false; + + public string[] Players = []; + + public CameraMode CameraMode { get; set; } = new CameraLock(); + public double Progress { get; set; } + public double Speed; + public double StartFrom; + public double MapLength; + public uint PassedNotes = 0; + + public double Accuracy = 100; + public double Health = 100; + public double HealthStep = 15; + + public uint Hits = 0; + public uint Misses = 0; + public uint Sum = 0; + public uint Score = 0; + public uint Combo = 0; + public uint ComboMultiplier = 1; + public uint ComboMultiplierProgress = 0; + public uint ComboMultiplierIncrement = 0; + public double ModsMultiplier = 1; + public float[] HitsInfo = []; + public Color LastHitColour = new Color(0.37254903f, 0.61960787f, 0.627451f, 1); + + public Vector3 CameraPosition { get; set; } = new Vector3(0, 0, 3.75f); + public Vector3 CameraRotation { get; set; } = Vector3.Zero; + public Vector3 CameraBasisZ { get; set; } = new(); + + public Vector2 CursorPosition = Vector2.Zero; + public Vector2 RawCursorPosition = Vector2.Zero; + public double DistanceMM = 0; + + public ulong FirstNote; + public string ReplayPath; + public Replay? Replay { get; set; } + public Replay[] Replays; + public List ReplayFrames = []; + public List ReplaySkips = []; + public ulong LastReplayFrame = 0; + public uint ReplayFrameCountOffset = 0; + public uint ReplayAttemptStatusOffset = 0; + + public Attempt(Map map, double speed, double startFrom, Dictionary mods, string[] players = null, Replay[] replays = null) + { + ID = $"{map.Name}_{OS.GetUniqueId()}_{Time.GetDatetimeStringFromUnixTime((long)Time.GetUnixTimeFromSystem())}".Replace(":", "_"); + Settings = SettingsManager.Instance.Settings; + Replays = replays; + IsReplay = Replays != null; + Map = map; + Speed = speed; + StartFrom = startFrom; + Players = players ?? []; + Progress = Speed * -1000 - Settings.ApproachTime.Value * 1000 + StartFrom; + ComboMultiplierIncrement = Math.Max(2, (uint)Map.Notes.Length / 200); + Mods = mods; + HitsInfo = IsReplay ? Replays[0].Notes : new float[Map.Notes.Length]; + + if (StartFrom > 0) + { + Qualifies = false; + foreach (Note note in Map.Notes) + { + if (note.Millisecond < StartFrom) + { + FirstNote = (ulong)note.Index + 1; + } + } + } + } } diff --git a/scripts/game/GameComponent.cs b/scripts/game/GameComponent.cs index c58bc073..bd5e0c12 100644 --- a/scripts/game/GameComponent.cs +++ b/scripts/game/GameComponent.cs @@ -1,4 +1,4 @@ -using System.Collections; +/*using System.Collections; using System.Collections.Generic; using Godot; using Godot.Collections; @@ -112,3 +112,4 @@ public override void _Input(InputEvent @event) } } } +*/ \ No newline at end of file diff --git a/scripts/game/LegacyRenderer.cs b/scripts/game/LegacyRenderer.cs index 2bd2f237..99d51ae0 100644 --- a/scripts/game/LegacyRenderer.cs +++ b/scripts/game/LegacyRenderer.cs @@ -1,77 +1,104 @@ using System; +using System.Linq; using Godot; public partial class LegacyRenderer : MultiMeshInstance3D { + [Export] public Runner Runner; private SettingsProfile settings; public override void _Ready() { + Runner ??= GetParent(); settings = SettingsManager.Instance.Settings; } public override void _Process(double delta) { - if (!LegacyRunner.Playing || LegacyRunner.CurrentAttempt.Stopped) +// if (!LegacyRunner.Playing || LegacyRunner.CurrentAttempt.Stopped) + if (!Runner.Playing) { return; } - Multimesh.InstanceCount = LegacyRunner.ToProcess; + Multimesh.InstanceCount = Runner.ToProcess; - if (LegacyRunner.ToProcess == 0) - { - return; - } - - float ar = (float)(LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].ApproachRate : settings.ApproachRate.Value); - float ad = (float)(LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].ApproachDistance : settings.ApproachDistance.Value); + float ar = (float)(Runner.Attempt.IsReplay ? Runner.Attempt.Replays[0].ApproachRate : settings.ApproachRate.Value); + float ad = (float)(Runner.Attempt.IsReplay ? Runner.Attempt.Replays[0].ApproachDistance : settings.ApproachDistance.Value); float at = ad / ar; - float fadeIn = (float)(LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].FadeIn : settings.FadeIn.Value) / 100; - float fadeOut = (float)(LegacyRunner.CurrentAttempt.IsReplay ? (LegacyRunner.CurrentAttempt.Replays[0].FadeOut ? 100 : 0) : settings.FadeOut.Value) / 100; - bool pushback = LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].Pushback : settings.Pushback.Value; + float fadeIn = (float)(Runner.Attempt.IsReplay ? Runner.Attempt.Replays[0].FadeIn : settings.FadeIn.Value); + bool fadeOut = Runner.Attempt.IsReplay ? Runner.Attempt.Replays[0].FadeOut : settings.FadeOut.Value; + bool pushback = Runner.Attempt.IsReplay ? Runner.Attempt.Replays[0].Pushback : settings.Pushback.Value; float hitWindowDepth = pushback ? (float)Constants.HIT_WINDOW * ar / 1000 : 0; - float noteOpacity = (float)settings.NoteOpacity; - float noteOpacityExponent = (float)settings.NoteOpacityExponent; + float noteSize = (float)(Runner.Attempt.IsReplay ? Runner.Attempt.Replays[0].NoteSize : settings.NoteSize.Value) / 4; + + // will fix this after everything works and is merged + +// if (LegacyRunner.ToProcess == 0) +// { +// return; +// } - if (noteOpacity > 1) - { - // Backward compatibility: older profiles may have stored opacity in a 0-100 range. - noteOpacity /= 100f; - } +// float ar = (float)(LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].ApproachRate : settings.ApproachRate.Value); +// float ad = (float)(LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].ApproachDistance : settings.ApproachDistance.Value); +// float at = ad / ar; +// float fadeIn = (float)(LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].FadeIn : settings.FadeIn.Value) / 100; +// float fadeOut = (float)(LegacyRunner.CurrentAttempt.IsReplay ? (LegacyRunner.CurrentAttempt.Replays[0].FadeOut ? 100 : 0) : settings.FadeOut.Value) / 100; +// bool pushback = LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].Pushback : settings.Pushback.Value; +// float hitWindowDepth = pushback ? (float)Constants.HIT_WINDOW * ar / 1000 : 0; +// float noteOpacity = (float)settings.NoteOpacity; +// float noteOpacityExponent = (float)settings.NoteOpacityExponent; + +// if (noteOpacity > 1) +// { +// // Backward compatibility: older profiles may have stored opacity in a 0-100 range. +// noteOpacity /= 100f; +// } - noteOpacity = Math.Clamp(noteOpacity, 0, 1); +// noteOpacity = Math.Clamp(noteOpacity, 0, 1); - float noteSize = (float)(LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].NoteSize : settings.NoteSize.Value) / 2; +// float noteSize = (float)(LegacyRunner.CurrentAttempt.IsReplay ? LegacyRunner.CurrentAttempt.Replays[0].NoteSize : settings.NoteSize.Value) / 2; Transform3D transform = new(Vector3.Right * noteSize, Vector3.Up * noteSize, Vector3.Back * noteSize, Vector3.Zero); - for (int i = 0; i < LegacyRunner.ToProcess; i++) + for (int i = 0; i < Runner.ToProcess; i++) { - Note note = LegacyRunner.ProcessNotes[i]; - float depth = (note.Millisecond - (float)LegacyRunner.CurrentAttempt.Progress) / (1000 * at) * ad / (float)LegacyRunner.CurrentAttempt.Speed; - float progress = 1 - Math.Max(0, (depth + hitWindowDepth) / (ad + hitWindowDepth)); - float alpha = 1; + Note note = Runner.ProcessNotes[i]; + float depth = (note.Millisecond - (float)Runner.Attempt.Progress) / (1000 * at) * ad / (float)Runner.Attempt.Speed; + float alpha = Math.Clamp((1 - (float)depth / ad) / (fadeIn / 100), 0, 1); + + // will also fix this later + +// Note note = LegacyRunner.ProcessNotes[i]; +// float depth = (note.Millisecond - (float)LegacyRunner.CurrentAttempt.Progress) / (1000 * at) * ad / (float)LegacyRunner.CurrentAttempt.Speed; +// float progress = 1 - Math.Max(0, (depth + hitWindowDepth) / (ad + hitWindowDepth)); +// float alpha = 1; - if (fadeIn > 0) - { - alpha = Math.Min(1, progress / fadeIn); - } +// if (fadeIn > 0) +// { +// alpha = Math.Min(1, progress / fadeIn); +// } - if (LegacyRunner.CurrentAttempt.Mods["Ghost"]) + if (Runner.Attempt.Mods["Ghost"]) { alpha -= (ad - depth) / (ad / 2); } else if (fadeOut > 0) { + // alpha -= (ad - depth) / (ad + (float)Constants.HIT_WINDOW * ar / 1000); + + // runner rewrite fork + // alpha *= Math.Min(1, (depth + hitWindowDepth) / (ad + hitWindowDepth)); + + // indev alpha -= 1 - Math.Min(1, (1 - progress) / fadeOut); } - if (!pushback && note.Millisecond - LegacyRunner.CurrentAttempt.Progress <= 0) + if (!pushback && note.Millisecond - Runner.Attempt.Progress <= 0) { alpha = 0; } - int j = LegacyRunner.ToProcess - i - 1; + int j = Runner.ToProcess - i - 1; Color color = SkinManager.Instance.Skin.NoteColors[note.Index % SkinManager.Instance.Skin.NoteColors.Length]; transform.Origin = new Vector3(note.X, note.Y, -depth); diff --git a/scripts/game/Runner.cs b/scripts/game/Runner.cs new file mode 100644 index 00000000..6b6d287c --- /dev/null +++ b/scripts/game/Runner.cs @@ -0,0 +1,415 @@ +using Godot; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Security.Cryptography; + +public partial class Runner : Node3D +{ + [Signal] public delegate void AttemptStatsUpdatedEventHandler(Attempt attempt); + [Signal] public delegate void SkipAvailableEventHandler(Attempt attempt); + [Signal] public delegate void HitResultChangedEventHandler(int noteIndex, HitResult hitResult); + + [Export] public HudManager HudManager; + + public Attempt Attempt; + + public Map Map; + private SettingsProfile settings; + public double Speed = 1; + public bool Paused = false; + public bool Playing = false; + public bool StopQueued = false; + + public int ToProcess = 0; + public List ProcessNotes = []; + private double lastFrame = Time.GetTicksUsec(); + + public bool SpinCamera = false; + + [ExportCategory("Settings")] + [Export] public bool NotesOnly = false; + + [ExportCategory("Nodes")] + [Export] public Camera3D Camera; + [Export] public MeshInstance3D Grid; + [Export] public MeshInstance3D Cursor; + [Export] public MultiMeshInstance3D Notes; + [Export] public VideoStreamPlayer VideoStreamPlayer; + + public override void _Ready() + { + base._Ready(); + + // if null, assign them to nodes under Runner + HudManager ??= GetNode("HUD"); + Camera ??= GetNode("Camera3D"); + Grid ??= HudManager.GetNode("Grid"); + Cursor ??= GetNode("Cursor"); + Notes ??= GetNode("Notes"); + VideoStreamPlayer ??= GetNode("Video").GetNode("VideoViewport").GetNode("VideoStreamPlayer"); + } + + public override void _Process(double delta) + { + ulong now = Time.GetTicksUsec(); + delta = (now - lastFrame) / 1000000; // more reliable + lastFrame = now; + + if (!Playing) return; + Attempt.Progress += delta * 1000 * Attempt.Speed; + + // if not paused & record replays on & not a temporary map & time from now and last replay frame was 60 frames apart + if (!Attempt.Stopped && settings.RecordReplays && !Attempt.Map.Ephemeral && now - Attempt.LastReplayFrame >= 1000000/60) + { + if (Attempt.ReplayFrames.Count == 0 || (Attempt.ReplayFrames[^1][1 .. 2] != new float[]{Attempt.CursorPosition.X, Attempt.CursorPosition.Y})) + { + Attempt.LastReplayFrame = now; + Attempt.ReplayFrames.Add([ + (float)Attempt.Progress, + Attempt.CursorPosition.X, + Attempt.CursorPosition.Y + ]); + } + } + + if (Attempt.Map.AudioBuffer != null) + { + if (Attempt.Progress >= Attempt.MapLength - Constants.HIT_WINDOW) + { + if (SoundManager.Song.Playing) + { + SoundManager.Song.Stop(); + } + } + else if (!SoundManager.Song.Playing && Attempt.Progress >= 0) + { + SoundManager.Song.Play(); + SoundManager.Song.Seek((float)Attempt.Progress / 1000); + } + } + + int nextNoteMillisecond = Attempt.PassedNotes >= Attempt.Map.Notes.Length ? (int)Attempt.MapLength : Attempt.Map.Notes[Attempt.PassedNotes].Millisecond; + if (nextNoteMillisecond - Attempt.Progress >= Constants.BREAK_TIME * Attempt.Speed) + { + int lastNoteMillisecond = Attempt.PassedNotes > 0 ? Attempt.Map.Notes[Attempt.PassedNotes - 1].Millisecond : 0; + int skipWindow = nextNoteMillisecond - Constants.BREAK_TIME - lastNoteMillisecond; + + if (skipWindow >= 1000 * Attempt.Speed) // only allow skipping if i'm gonna allow it for at least 1 second + { + if (!Attempt.CanSkip) + { + Attempt.CanSkip = true; + EmitSignal(SignalName.SkipAvailable, Attempt); + } + } + } + else + { + Attempt.CanSkip = false; + } + + ToProcess = 0; + ProcessNotes.Clear(); + + // note process check + double at = Attempt.IsReplay ? Attempt.Replays[0].ApproachTime : settings.ApproachTime; + + for (uint i = Attempt.PassedNotes; i < Attempt.Map.Notes.Length; i++) + { + Note note = Attempt.Map.Notes[i]; + + if (note.Millisecond < Attempt.StartFrom) + { + continue; + } + + if (note.Millisecond + Constants.HIT_WINDOW * Attempt.Speed < Attempt.Progress) // past hit window + { + if (i + 1 > Attempt.PassedNotes) + { + if (Attempt.IsReplay && Attempt.Replays.Length <= 1 && Attempt.Replays[0].Notes[note.Index] == -1 || !Attempt.IsReplay && note.LastResult != HitResult.Hit) + { + note.Miss(this); + } + Attempt.PassedNotes = i + 1; + } + + continue; + + // if (!Attempt.IsReplay) + // { + // continue; + // } + + } + else if (note.Millisecond > Attempt.Progress + at * 1000 * Attempt.Speed) // past approach distance + { + break; + } + else if (note.LastResult == HitResult.Hit) // no point + { + continue; + } + + if (settings.AlwaysPlayHitSound && !Attempt.Map.Notes[i].Hittable && note.Millisecond < Attempt.Progress) + { + Attempt.Map.Notes[i].Hittable = true; + + SoundManager.HitSound.Play(); + } + + ToProcess++; + ProcessNotes.Add(note); + } + + // hitreg check + for (int i = 0; i < ToProcess; i++) + { + Note note = ProcessNotes[i]; + if (note.LastResult == HitResult.Hit) continue; + + if (!Attempt.IsReplay) + { + if (note.Millisecond - Attempt.Progress > 0) continue; + + var result = note.CheckHitResult(Attempt); + if (result == HitResult.Hit) + { + note.Hit(this); + } + + } + else if (Attempt.Replays.Length > 1 && note.Millisecond - Attempt.Progress <= 0 || Attempt.Replays[0].Notes[note.Index] != -1 && note.Millisecond - Attempt.Progress + Attempt.Replays[0].Notes[note.Index] * Attempt.Speed <= 0) + { + note.Hit(this); + } + } + + if (Attempt.Progress >= Attempt.MapLength) + { + Stop(); + return; + } + + if (StopQueued) + { + StopQueued = false; + Stop(); + return; + } + } + + public void OnHitResultChanged(int noteIndex, HitResult hitResult) + { + float lateness = Attempt.IsReplay ? Attempt.HitsInfo[noteIndex] : (float)(((int)Attempt.Progress - Attempt.Map.Notes[noteIndex].Millisecond) / Attempt.Speed); + float factor = 1 - Math.Max(0, lateness - 25) / 150f; + uint hitScore = (uint)(100 * Attempt.ComboMultiplier * Attempt.ModsMultiplier * factor * ((Attempt.Speed - 1) / 2.5 + 1)); + + switch (hitResult) + { + case HitResult.Hit: + SoundManager.HitSound.Play(); + Attempt.Hits++; + Attempt.Sum++; + Attempt.Accuracy = Math.Floor((float)Attempt.Hits / Attempt.Sum * 10000) / 100; + Attempt.Combo++; + Attempt.ComboMultiplierProgress++; + Attempt.LastHitColour = SkinManager.Instance.Skin.NoteColors[noteIndex % SkinManager.Instance.Skin.NoteColors.Length]; + Attempt.Score += hitScore; + Attempt.HealthStep = Math.Max(Attempt.HealthStep / 1.45, 15); + Attempt.Health = Math.Min(100, Attempt.Health + Attempt.HealthStep / 1.75); + if (!Attempt.IsReplay) + { + Stats.NotesHit++; + if (Attempt.Combo > Stats.HighestCombo) Stats.HighestCombo = Attempt.Combo; + Attempt.HitsInfo[noteIndex] = lateness; + } + if (Attempt.ComboMultiplierProgress == Attempt.ComboMultiplierIncrement) + { + if (Attempt.ComboMultiplier < 8) + { + Attempt.ComboMultiplierProgress = Attempt.ComboMultiplier == 7 ? Attempt.ComboMultiplierIncrement : 0; + Attempt.ComboMultiplier++; + } + } + break; + case HitResult.Miss: + SoundManager.MissSound.Play(); + Attempt.Misses++; + Attempt.Sum++; + Attempt.Accuracy = Mathf.Floor((float)Attempt.Hits / Attempt.Sum * 10000) / 100; + Attempt.Combo = 0; + Attempt.ComboMultiplierProgress = 0; + Attempt.ComboMultiplier = Math.Max(1, Attempt.ComboMultiplier - 1); + Attempt.Health = Math.Max(0, Attempt.Health - Attempt.HealthStep); + Attempt.HealthStep = Math.Min(Attempt.HealthStep * 1.2, 100); + if (!Attempt.IsReplay) + { + Stats.NotesMissed++; + Attempt.HitsInfo[noteIndex] = -1; + } + if (!Attempt.IsReplay && Attempt.Health <= 0 && Attempt.Alive) + { + Attempt.Alive = false; + Attempt.Qualifies = false; + Attempt.DeathTime = Attempt.Progress; + SoundManager.FailSound.Play(); + + if (!Attempt.Mods["NoFail"]) QueueStop(); + } + break; + default: + break; + } + + EmitSignal(SignalName.AttemptStatsUpdated, Attempt); + } + + public void Play() + { + if (Attempt == null) return; + + if (!NotesOnly) + { + HudManager.Init(); + Attempt.TimeStarted = Time.GetTicksUsec(); + HitResultChanged += OnHitResultChanged; + } + + settings = SettingsManager.Instance.Settings; + //SpinCamera = Attempt.Mods.Any(mod => mod.Key == "Spin"); + SpinCamera = Attempt.Mods["Spin"]; + Camera.Fov = (float)(Attempt.IsReplay ? Attempt.Replays[0].FoV : settings.FoV.Value); + Notes.Multimesh.Mesh = SkinManager.Instance.Skin.NoteMesh; + Playing = true; + + if (Attempt.Map.AudioBuffer != null) + { + SoundManager.Song.Stream = Util.Audio.LoadStream(Attempt.Map.AudioBuffer); + SoundManager.Song.PitchScale = (float)Attempt.Speed; + Attempt.MapLength = (float)SoundManager.Song.Stream.GetLength() * 1000; + } + else + { + Attempt.MapLength = Attempt.Map.Length + 1000; + } + + Attempt.MapLength += Constants.HIT_WINDOW; + SoundManager.UpdateVolume(); + + } + + public void Pause() + { + + } + + public void Skip() + { + if (Attempt.CanSkip) + { + Attempt.ReplaySkips.Add((float)Attempt.Progress); + + if (Attempt.PassedNotes >= Attempt.Map.Notes.Length) + { + Attempt.Progress = SoundManager.Song.Stream.GetLength() * 1000; + } + else + { + Attempt.Progress = Attempt.Map.Notes[Attempt.PassedNotes].Millisecond - settings.ApproachTime * 1500 * Attempt.Speed; // turn AT to ms and multiply by 1.5x + + // Discord.Client.UpdateEndTime(DateTime.UtcNow.AddSeconds((Time.GetUnixTimeFromSystem() + (Attempt.Map.Length - Attempt.Progress) / 1000 / Attempt.Speed))); + + if (Attempt.Map.AudioBuffer != null) + { + if (!SoundManager.Song.Playing) + { + SoundManager.Song.Play(); + } + + SoundManager.Song.Seek((float)Attempt.Progress / 1000); + VideoStreamPlayer.StreamPosition = (float)Attempt.Progress / 1000; + } + } + } + } + + public void QueueStop() + { + if (!Playing) + { + return; + } + + Playing = false; + StopQueued = true; + } + + + public void Stop(bool results = true) + { + if (Attempt.Stopped) + { + return; + } + + Attempt.HitsInfo = Attempt.HitsInfo[0 .. (int)Attempt.PassedNotes]; + + // dont want an infinite dependency loop so im just going to do this -fog + if (!Attempt.IsReplay && GameScene.Instance.ReplayManager.CurrentMode == ReplayManager.Mode.RECORD) + { + GameScene.Instance.ReplayManager.SaveReplay(Attempt); + } + + + if (!Attempt.IsReplay) + { + Stats.GamePlaytime += (Time.GetTicksUsec() - Attempt.TimeStarted) / 1000000; + Stats.TotalDistance += (ulong)Attempt.DistanceMM; + + if (Attempt.StartFrom == 0) + { + if (!File.Exists($"{Constants.USER_FOLDER}/pbs/{Attempt.Map.Name}")) + { + List bytes = [0, 0, 0, 0]; + bytes.AddRange(SHA256.HashData([0, 0, 0, 0])); + File.WriteAllBytes($"{Constants.USER_FOLDER}/pbs/{Attempt.Map.Name}", [.. bytes]); + } + + Leaderboard leaderboard = new(Attempt.Map.Name, $"{Constants.USER_FOLDER}/pbs/{Attempt.Map.Name}"); + + leaderboard.Add(new(Attempt.ID, "You", Attempt.Qualifies, Attempt.Score, Attempt.Accuracy, Time.GetUnixTimeFromSystem(), Attempt.Progress, Attempt.Map.Length, Attempt.Speed, Attempt.Mods)); + leaderboard.Save(); + + if (Attempt.Qualifies) + { + Stats.Passes++; + Stats.TotalScore += Attempt.Score; + + if (Attempt.Accuracy == 100) + { + Stats.FullCombos++; + } + + if (Attempt.Score > Stats.HighestScore) + { + Stats.HighestScore = Attempt.Score; + } + + Stats.PassAccuracies.Add(Attempt.Accuracy); + } + } + } + + DisplayServer.WindowSetVsyncMode(DisplayServer.VSyncMode.Adaptive); + + if (results) + { + SceneManager.Load("res://scenes/results.tscn"); + } + } +} diff --git a/scripts/game/Runner.cs.uid b/scripts/game/Runner.cs.uid new file mode 100644 index 00000000..082bc758 --- /dev/null +++ b/scripts/game/Runner.cs.uid @@ -0,0 +1 @@ +uid://dp8nga2aot1n2 diff --git a/scripts/game/data/CursorTrailData.cs b/scripts/game/data/CursorTrailData.cs new file mode 100644 index 00000000..6940a370 --- /dev/null +++ b/scripts/game/data/CursorTrailData.cs @@ -0,0 +1,14 @@ +using Godot; + +public record struct CursorTrailData +{ + public readonly ulong Time; + public readonly float Rotation; + public readonly Vector2 Position; + public CursorTrailData(ulong time, Vector2 position, float rotation) + { + Time = time; + Position = position; + Rotation = rotation; + } +} diff --git a/scripts/game/data/CursorTrailData.cs.uid b/scripts/game/data/CursorTrailData.cs.uid new file mode 100644 index 00000000..77000c37 --- /dev/null +++ b/scripts/game/data/CursorTrailData.cs.uid @@ -0,0 +1 @@ +uid://jlx8lm1c5dpo diff --git a/scripts/game/judgments/HealthJudgment.cs b/scripts/game/judgments/HealthJudgment.cs index 5df71cd0..59dae2df 100644 --- a/scripts/game/judgments/HealthJudgment.cs +++ b/scripts/game/judgments/HealthJudgment.cs @@ -19,18 +19,18 @@ public class HealthJudgment public void ApplyAttempt(Attempt attempt) { - foreach (Mod mod in attempt.Mods) - { - if (mod is IHealthModifier healthMod) - { - healthModifiers.Add(healthMod); - } + // foreach (Mod mod in attempt.Mods) + // { + // if (mod is IHealthModifier healthMod) + // { + // healthModifiers.Add(healthMod); + // } - if (mod is IFailModifier failMod) - { - failModifiers.Add(failMod); - } - } + // if (mod is IFailModifier failMod) + // { + // failModifiers.Add(failMod); + // } + // } } public void ApplyHitObjectResult(bool hit) diff --git a/scripts/game/judgments/HitResult.cs b/scripts/game/judgments/HitResult.cs index 1723ee4b..1fd47534 100644 --- a/scripts/game/judgments/HitResult.cs +++ b/scripts/game/judgments/HitResult.cs @@ -1,5 +1,8 @@ using System; -public class HitResult +public enum HitResult { + None, + Hit, + Miss } diff --git a/scripts/game/managers/CursorManager.cs b/scripts/game/managers/CursorManager.cs new file mode 100644 index 00000000..d6bf0fa7 --- /dev/null +++ b/scripts/game/managers/CursorManager.cs @@ -0,0 +1,124 @@ +using System; +using Godot; + +/// +/// Class containing all gameplay related logic regarding the cursor. +/// +public partial class CursorManager : Node +{ + [Export] private Runner runner; + [Export] private PlayerInputController playerInputController; + [Export] private ReplayManager replayManager; + [Export] private MeshInstance3D cursorMesh; + [Export] private Camera3D camera; + + private float sensitivity; + + [Signal] + public delegate void OnCursorUpdatedEventHandler( + Vector2 position + ); + + public override void _Ready() + { + base._Ready(); + + cursorMesh ??= GetNode("Cursor"); + playerInputController ??= GetNode("/PlayerInputController"); + replayManager ??= GetNode("ReplayManager"); + } + + public override void _Process(double delta) + { + if (!runner.Playing) return; + + updateCursorRotation(delta); + } + + public void UpdateCursor(Vector2 inputDelta) + { + EmitSignalOnCursorUpdated(inputDelta); + + sensitivity = (float)(runner.Attempt.IsReplay ? runner.Attempt.Replays[0].Sensitivity : runner.Attempt.Settings.Sensitivity); + sensitivity *= runner.Attempt.Settings.FoV.Value / 70f; + + if (runner.Attempt.Settings.AbsoluteInput || runner.Attempt.IsReplay) + repositionAbsolute(); + + if (runner.SpinCamera) + updateSpinState(inputDelta); + else + updateLockedState(inputDelta); + } + + private void updateSpinState(Vector2 inputDelta) + { + Attempt attempt = runner.Attempt; + + if (!attempt.IsReplay) + { + camera.Rotation += new Vector3(-inputDelta.Y / 120 * sensitivity / (float)Math.PI, -inputDelta.X / 120 * sensitivity / (float)Math.PI, 0); + } + else + { + camera.Rotation += new Vector3(inputDelta.Y / (float)Math.PI, -inputDelta.X / (float)Math.PI, 0); + } + camera.Rotation = new Vector3((float)Math.Clamp(camera.Rotation.X, Mathf.DegToRad(-90), Mathf.DegToRad(90)), camera.Rotation.Y, camera.Rotation.Z); + + Vector3 Origin = new Vector3(0, 0, 3.5f); + Vector3 CursorLock = new Vector3(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0); + // The pivot is to mimic ROBLOX's orbital camera + Vector3 Pivot = camera.Basis.Z / 4f; + + // Proper Parallax Support + camera.Position = Origin + CursorLock * (float)attempt.Settings.CameraParallax + Pivot; + + Vector3 LookVector = camera.Basis.Z; + Vector2 CameraVector2 = new Vector2(camera.Position.X, camera.Position.Y); + Vector2 LookVector2 = new Vector2(LookVector.X, LookVector.Y); + + // Project Cursor from Camera's "ray cast" + attempt.RawCursorPosition = CameraVector2 - LookVector2 * Mathf.Abs(camera.Position.Z / LookVector.Z); + attempt.CursorPosition = attempt.RawCursorPosition.Clamp(-Constants.BOUNDS, Constants.BOUNDS); + + cursorMesh.Position = new Vector3(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0); + } + + private void updateLockedState(Vector2 inputDelta) + { + Attempt attempt = runner.Attempt; + Vector2 delta = new Vector2(1, -1) * (inputDelta * sensitivity / 120f); + + if (runner.Attempt.Settings.CursorDrift) + { + attempt.CursorPosition = attempt.IsReplay + ? replayManager.CursorPosition.Clamp(-Constants.BOUNDS, Constants.BOUNDS) + : (attempt.CursorPosition + delta).Clamp(-Constants.BOUNDS, Constants.BOUNDS); + } + else + { + attempt.RawCursorPosition = attempt.IsReplay + ? replayManager.CursorPosition + : attempt.RawCursorPosition + delta; + attempt.CursorPosition = attempt.RawCursorPosition.Clamp(-Constants.BOUNDS, Constants.BOUNDS); + } + + // Update visual cursor's position + cursorMesh.Position = new Vector3(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0); + + Vector3 Origin = new Vector3(0, 0, 3.75f); + float Parallax = (float)(attempt.IsReplay ? attempt.Replays[0].Parallax : attempt.Settings.CameraParallax); + + camera.Position = Origin + new Vector3(attempt.CursorPosition.X, attempt.CursorPosition.Y, 0) * Parallax; + camera.Rotation = Vector3.Zero; + } + + // Reset everything to zero so it doesn't have infinite sensitivity + private void repositionAbsolute() + { + camera.Rotation = Vector3.Zero; + runner.Attempt.RawCursorPosition = Vector2.Zero; + runner.Attempt.CursorPosition = Vector2.Zero; + } + private void updateCursorRotation(double delta) => cursorMesh.RotationDegrees += Vector3.Back * SettingsManager.Instance.Settings.CursorRotation * (float)delta; +} diff --git a/scripts/game/managers/CursorManager.cs.uid b/scripts/game/managers/CursorManager.cs.uid new file mode 100644 index 00000000..566a7af3 --- /dev/null +++ b/scripts/game/managers/CursorManager.cs.uid @@ -0,0 +1 @@ +uid://bxncinhblsjgg diff --git a/scripts/game/managers/HudManager.cs b/scripts/game/managers/HudManager.cs new file mode 100644 index 00000000..8c7b9913 --- /dev/null +++ b/scripts/game/managers/HudManager.cs @@ -0,0 +1,49 @@ +using Godot; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +public partial class HudManager : Node +{ + [Export] public Runner Runner; + + private List components = []; + + private List FindAllComponents(Node root) + { + List comps = new(); + + foreach (Node child in root.GetChildren()) + { + if (child is IUIComponent component) + comps.Add(component); + + comps.AddRange(FindAllComponents(child)); + } + + return comps; + } + + public void Init() + { + Runner ??= GetParent(); + components = FindAllComponents(this); + + foreach (var component in components) + { + component.Runner = Runner; + component.Init(); + } + } + + public override void _Process(double delta) + { + if (Runner?.Attempt == null) return; + + foreach (var component in components) + { + component.Process(delta, Runner.Attempt); + } + } +} \ No newline at end of file diff --git a/scripts/game/managers/HudManager.cs.uid b/scripts/game/managers/HudManager.cs.uid new file mode 100644 index 00000000..4e232c2d --- /dev/null +++ b/scripts/game/managers/HudManager.cs.uid @@ -0,0 +1 @@ +uid://n6n303pw3qbd diff --git a/scripts/game/managers/ReplayManager.cs b/scripts/game/managers/ReplayManager.cs new file mode 100644 index 00000000..8dd20642 --- /dev/null +++ b/scripts/game/managers/ReplayManager.cs @@ -0,0 +1,282 @@ +using Godot; +using System; +using System.Linq; +using System.Security.Cryptography; + +public partial class ReplayManager : Node +{ + public enum Mode + { + NONE, + RECORD, + PLAYBACK + } + + [Export] public Runner Runner { get; set; } + [Export] public Mode CurrentMode { get; set; } + [Export] public Panel ReplayViewer { get; set; } + [Export] public CursorManager CursorManager { get; private set; } + + public bool ViewerVisible; + + // only public variable because of GameScene + public static TextureButton SeekerPause; + private static Label seekerTime; + private static HSlider seekerTimeline; + private static bool seekerHovered; + public static bool LMB; // fml + public float ReplayLength; + public string ReplayPath; + public Vector2 CursorPosition { get; private set; } + + private FileAccess _file; + private ulong statusOffset, frameCountOffset; + + public void NewReplay(Attempt attempt) + { + var settings = attempt.Settings; + if (!settings.RecordReplays) return; + ReplayPath = $"{Constants.USER_FOLDER}/replays/{attempt.ID}.phxr"; + _file = FileAccess.Open(ReplayPath, FileAccess.ModeFlags.Write); + + _file.StoreString("phxr"); // sig + _file.Store8(1); // replay file version + + _file.StoreDouble(attempt.Speed); + _file.StoreDouble(attempt.StartFrom); + _file.StoreDouble(settings.ApproachRate); + _file.StoreDouble(settings.ApproachDistance); + _file.StoreDouble(settings.FadeIn); + _file.Store8((byte)(settings.FadeOut ? 1 : 0)); + _file.Store8((byte)(settings.Pushback ? 1 : 0)); + _file.StoreDouble(settings.CameraParallax); + _file.StoreDouble(settings.FoV.Value); + _file.StoreDouble(settings.NoteSize); + _file.StoreDouble(settings.Sensitivity); + + statusOffset = (uint)_file.GetPosition(); + _file.Store8(0); + + string mods = string.Join("_", Runner.Attempt.Mods.Where(mod => mod.Value).Select(mod => mod.Key)); + string mapName = attempt.Map.FilePath.GetFile().GetBaseName(); + string player = "You"; + + void storeSizedString(string data) + { + _file.Store32((uint)data.Length); + _file.StoreString(data); + } + + storeSizedString(mods); + storeSizedString(mapName); + _file.Store64((ulong)attempt.Map.Notes.Length); + storeSizedString(player); + + frameCountOffset = (uint)_file.GetPosition(); + _file.Store64(0); // reserve frame count + } + + public void SaveReplay(Attempt attempt) + { + _file.Seek(statusOffset); + _file.Store8((byte)(attempt.Alive ? (attempt.Qualifies ? 0 : 1) : 2)); + _file.Seek(frameCountOffset); + _file.Store64((ulong)attempt.ReplayFrames.Count); + + foreach (float[] frame in attempt.ReplayFrames) + { + _file.StoreFloat(frame[0]); + _file.StoreFloat(frame[1]); + _file.StoreFloat(frame[2]); + } + + _file.Seek(_file.GetLength()); + _file.Store64(attempt.FirstNote); + _file.Store64(attempt.Sum); + // GD.Print(string.Join(", ", attempt.HitsInfo)); + // GD.Print($"Sum: {attempt.FirstNote}+{attempt.Sum}={attempt.FirstNote + attempt.Sum}"); + // GD.Print($"HitsInfoCount: {attempt.HitsInfo.Count()}"); + + if (attempt.FirstNote + attempt.Sum != (uint)attempt.HitsInfo.Length) + { + + _file.Close(); + + if (FileAccess.FileExists(ReplayPath)) + { + + string mismatch = $"Sum: {attempt.FirstNote}+{attempt.Sum}={attempt.FirstNote + attempt.Sum}"; + string hitsInfoDebug = string.Join(", ", attempt.HitsInfo); + string passedNotesDebug = $"Passed Notes: {attempt.PassedNotes}"; + DirAccess.RemoveAbsolute(ReplayPath); + GD.PushWarning($"Corrupted De-synced replay deleted!\nPath: {ReplayPath}\n{mismatch}\nHits Info: {hitsInfoDebug}\n{passedNotesDebug}"); + } + + return; + } + + for (ulong i = attempt.FirstNote; i < attempt.FirstNote + attempt.Sum; i++) + { + _file.Store8((byte)(attempt.HitsInfo[i] == -1 ? 255 : Math.Min(254, attempt.HitsInfo[i] * (254 / 55)))); + } + + _file.Store64((ulong)attempt.ReplaySkips.Count); + foreach (float skip in attempt.ReplaySkips) + { + _file.StoreFloat(skip); + } + + _file.Close(); + + // open replay to store hash + _file = FileAccess.Open($"{Constants.USER_FOLDER}/replays/{attempt.ID}.phxr", FileAccess.ModeFlags.ReadWrite); + ulong length = _file.GetLength(); + byte[] hash = SHA256.HashData(_file.GetBuffer((long)length)); + _file.StoreBuffer(hash); + + _file.Close(); + + attempt.ReplayPath = ReplayPath; + } + + public void InitReplayLength() + { + if (Runner?.Attempt == null || !Runner.Attempt.IsReplay) return; + ReplayLength = Runner.Attempt.Replays[0].Length; + } + + public override void _Ready() + { + base._Ready(); + + // this entire code lowkey sucks, so i am just copy and pasting it because i am lazy -fog + SeekerPause = ReplayViewer.GetNode("Pause"); + seekerTime = ReplayViewer.GetNode