diff --git a/Audio/Midi/Song1.mid b/Audio/Midi/Song1.mid
deleted file mode 100644
index 8cabb595..00000000
Binary files a/Audio/Midi/Song1.mid and /dev/null differ
diff --git a/Audio/Midi/Song2.mid b/Audio/Midi/Song2.mid
deleted file mode 100644
index 49e3e6de..00000000
Binary files a/Audio/Midi/Song2.mid and /dev/null differ
diff --git a/Audio/Midi/Song3.mid b/Audio/Midi/Song3.mid
deleted file mode 100644
index 831fb61f..00000000
Binary files a/Audio/Midi/Song3.mid and /dev/null differ
diff --git a/Audio/Midi/florestan-subset.sf2 b/Audio/Midi/florestan-subset.sf2
deleted file mode 100644
index 56a930ac..00000000
Binary files a/Audio/Midi/florestan-subset.sf2 and /dev/null differ
diff --git a/Audio/songMaps/Song1.tres b/Audio/songMaps/Song1.tres
new file mode 100644
index 00000000..bf63d7af
--- /dev/null
+++ b/Audio/songMaps/Song1.tres
@@ -0,0 +1,216 @@
+[gd_resource type="Resource" load_steps=44 format=3 uid="uid://djm7g0svpexde"]
+
+[ext_resource type="Script" path="res://Classes/MidiMaestro/NoteInfo.cs" id="1_5ry1j"]
+[ext_resource type="Script" path="res://Classes/MidiMaestro/NoteChart.cs" id="2_gbqfw"]
+
+[sub_resource type="Resource" id="Resource_sycg1"]
+script = ExtResource("1_5ry1j")
+Beat = 16.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_vh0lg"]
+script = ExtResource("1_5ry1j")
+Beat = 24.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_4fym7"]
+script = ExtResource("1_5ry1j")
+Beat = 32.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_mf3c7"]
+script = ExtResource("1_5ry1j")
+Beat = 5.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_5kf2q"]
+script = ExtResource("1_5ry1j")
+Beat = 9.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_f15o4"]
+script = ExtResource("1_5ry1j")
+Beat = 13.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_oert5"]
+script = ExtResource("1_5ry1j")
+Beat = 17.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_kytyf"]
+script = ExtResource("1_5ry1j")
+Beat = 21.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_kmfoh"]
+script = ExtResource("1_5ry1j")
+Beat = 25.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_hsn40"]
+script = ExtResource("1_5ry1j")
+Beat = 29.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_fdk4c"]
+script = ExtResource("1_5ry1j")
+Beat = 33.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_1aoo3"]
+script = ExtResource("1_5ry1j")
+Beat = 37.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_wlcjv"]
+script = ExtResource("1_5ry1j")
+Beat = 41.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_i1n24"]
+script = ExtResource("1_5ry1j")
+Beat = 45.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_n2wrp"]
+script = ExtResource("1_5ry1j")
+Beat = 49.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_gmg26"]
+script = ExtResource("1_5ry1j")
+Beat = 53.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_2jtmb"]
+script = ExtResource("1_5ry1j")
+Beat = 57.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_l3uo1"]
+script = ExtResource("1_5ry1j")
+Beat = 32.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ayat2"]
+script = ExtResource("1_5ry1j")
+Beat = 35.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_5hrj0"]
+script = ExtResource("1_5ry1j")
+Beat = 38.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_sbk3e"]
+script = ExtResource("1_5ry1j")
+Beat = 41.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_erw8g"]
+script = ExtResource("1_5ry1j")
+Beat = 44.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ohcnr"]
+script = ExtResource("1_5ry1j")
+Beat = 47.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_1xng4"]
+script = ExtResource("1_5ry1j")
+Beat = 50.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ullhh"]
+script = ExtResource("1_5ry1j")
+Beat = 53.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_jf4ph"]
+script = ExtResource("1_5ry1j")
+Beat = 56.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_l0ykb"]
+script = ExtResource("1_5ry1j")
+Beat = 59.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_o3ga4"]
+script = ExtResource("1_5ry1j")
+Beat = 4.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_j4bu6"]
+script = ExtResource("1_5ry1j")
+Beat = 8.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_3pmwc"]
+script = ExtResource("1_5ry1j")
+Beat = 12.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_8a8dw"]
+script = ExtResource("1_5ry1j")
+Beat = 16.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_sen1l"]
+script = ExtResource("1_5ry1j")
+Beat = 20.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_i8i3p"]
+script = ExtResource("1_5ry1j")
+Beat = 24.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_v6ql4"]
+script = ExtResource("1_5ry1j")
+Beat = 28.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_v3ixx"]
+script = ExtResource("1_5ry1j")
+Beat = 32.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_fjllr"]
+script = ExtResource("1_5ry1j")
+Beat = 36.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_kfijo"]
+script = ExtResource("1_5ry1j")
+Beat = 40.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_j42ns"]
+script = ExtResource("1_5ry1j")
+Beat = 44.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_c3aa2"]
+script = ExtResource("1_5ry1j")
+Beat = 48.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_a36fj"]
+script = ExtResource("1_5ry1j")
+Beat = 52.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_gwuir"]
+script = ExtResource("1_5ry1j")
+Beat = 56.0
+Length = 0.0
+
+[resource]
+script = ExtResource("2_gbqfw")
+UpLaneData = [SubResource("Resource_o3ga4"), SubResource("Resource_j4bu6"), SubResource("Resource_3pmwc"), SubResource("Resource_8a8dw"), SubResource("Resource_sen1l"), SubResource("Resource_i8i3p"), SubResource("Resource_v6ql4"), SubResource("Resource_v3ixx"), SubResource("Resource_fjllr"), SubResource("Resource_kfijo"), SubResource("Resource_j42ns"), SubResource("Resource_c3aa2"), SubResource("Resource_a36fj"), SubResource("Resource_gwuir")]
+DownLaneData = [SubResource("Resource_sycg1"), SubResource("Resource_vh0lg"), SubResource("Resource_4fym7")]
+LeftLaneData = [SubResource("Resource_mf3c7"), SubResource("Resource_5kf2q"), SubResource("Resource_f15o4"), SubResource("Resource_oert5"), SubResource("Resource_kytyf"), SubResource("Resource_kmfoh"), SubResource("Resource_hsn40"), SubResource("Resource_fdk4c"), SubResource("Resource_1aoo3"), SubResource("Resource_wlcjv"), SubResource("Resource_i1n24"), SubResource("Resource_n2wrp"), SubResource("Resource_gmg26"), SubResource("Resource_2jtmb")]
+RightLaneData = [SubResource("Resource_l3uo1"), SubResource("Resource_ayat2"), SubResource("Resource_5hrj0"), SubResource("Resource_sbk3e"), SubResource("Resource_erw8g"), SubResource("Resource_ohcnr"), SubResource("Resource_1xng4"), SubResource("Resource_ullhh"), SubResource("Resource_jf4ph"), SubResource("Resource_l0ykb")]
diff --git a/Audio/songMaps/Song2.tres b/Audio/songMaps/Song2.tres
new file mode 100644
index 00000000..486b0873
--- /dev/null
+++ b/Audio/songMaps/Song2.tres
@@ -0,0 +1,156 @@
+[gd_resource type="Resource" load_steps=32 format=3]
+
+[ext_resource type="Script" path="res://Classes/MidiMaestro/NoteInfo.cs" id="1_7gfc4"]
+[ext_resource type="Script" path="res://Classes/MidiMaestro/NoteChart.cs" id="2_12jpe"]
+
+[sub_resource type="Resource" id="Resource_rxom0"]
+script = ExtResource("1_7gfc4")
+Beat = 1.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_asj20"]
+script = ExtResource("1_7gfc4")
+Beat = 6.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_hredl"]
+script = ExtResource("1_7gfc4")
+Beat = 10.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_iciyj"]
+script = ExtResource("1_7gfc4")
+Beat = 14.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_1w85u"]
+script = ExtResource("1_7gfc4")
+Beat = 17.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_daxqs"]
+script = ExtResource("1_7gfc4")
+Beat = 22.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_6qbd8"]
+script = ExtResource("1_7gfc4")
+Beat = 25.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_54mir"]
+script = ExtResource("1_7gfc4")
+Beat = 30.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ijh26"]
+script = ExtResource("1_7gfc4")
+Beat = 2.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_sstcx"]
+script = ExtResource("1_7gfc4")
+Beat = 4.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ldqq5"]
+script = ExtResource("1_7gfc4")
+Beat = 5.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_eltqr"]
+script = ExtResource("1_7gfc4")
+Beat = 10.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_fxmce"]
+script = ExtResource("1_7gfc4")
+Beat = 12.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_l7bpk"]
+script = ExtResource("1_7gfc4")
+Beat = 14.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_do8wm"]
+script = ExtResource("1_7gfc4")
+Beat = 15.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_qvcn2"]
+script = ExtResource("1_7gfc4")
+Beat = 18.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_siock"]
+script = ExtResource("1_7gfc4")
+Beat = 20.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_lec0e"]
+script = ExtResource("1_7gfc4")
+Beat = 22.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_6wstj"]
+script = ExtResource("1_7gfc4")
+Beat = 25.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_uxbx5"]
+script = ExtResource("1_7gfc4")
+Beat = 26.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_chdui"]
+script = ExtResource("1_7gfc4")
+Beat = 28.0
+Length = 1.0
+
+[sub_resource type="Resource" id="Resource_t7pyo"]
+script = ExtResource("1_7gfc4")
+Beat = 8.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_1obgv"]
+script = ExtResource("1_7gfc4")
+Beat = 15.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_3ah33"]
+script = ExtResource("1_7gfc4")
+Beat = 24.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_87dqu"]
+script = ExtResource("1_7gfc4")
+Beat = 1.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_81s21"]
+script = ExtResource("1_7gfc4")
+Beat = 2.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_pyy5l"]
+script = ExtResource("1_7gfc4")
+Beat = 9.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_7lmg0"]
+script = ExtResource("1_7gfc4")
+Beat = 17.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_hgf3i"]
+script = ExtResource("1_7gfc4")
+Beat = 25.0
+Length = 0.0
+
+[resource]
+script = ExtResource("2_12jpe")
+UpLaneData = Array[ExtResource("1_7gfc4")]([SubResource("Resource_87dqu"), SubResource("Resource_81s21"), SubResource("Resource_pyy5l"), SubResource("Resource_7lmg0"), SubResource("Resource_hgf3i")])
+DownLaneData = Array[ExtResource("1_7gfc4")]([SubResource("Resource_rxom0"), SubResource("Resource_asj20"), SubResource("Resource_hredl"), SubResource("Resource_iciyj"), SubResource("Resource_1w85u"), SubResource("Resource_daxqs"), SubResource("Resource_6qbd8"), SubResource("Resource_54mir")])
+LeftLaneData = Array[ExtResource("1_7gfc4")]([SubResource("Resource_ijh26"), SubResource("Resource_sstcx"), SubResource("Resource_ldqq5"), SubResource("Resource_eltqr"), SubResource("Resource_fxmce"), SubResource("Resource_l7bpk"), SubResource("Resource_do8wm"), SubResource("Resource_qvcn2"), SubResource("Resource_siock"), SubResource("Resource_lec0e"), SubResource("Resource_6wstj"), SubResource("Resource_uxbx5"), SubResource("Resource_chdui")])
+RightLaneData = Array[ExtResource("1_7gfc4")]([SubResource("Resource_t7pyo"), SubResource("Resource_1obgv"), SubResource("Resource_3ah33")])
diff --git a/Audio/songMaps/Song3.tres b/Audio/songMaps/Song3.tres
new file mode 100644
index 00000000..28d691de
--- /dev/null
+++ b/Audio/songMaps/Song3.tres
@@ -0,0 +1,281 @@
+[gd_resource type="Resource" load_steps=57 format=3 uid="uid://ekxscjn7ys6v"]
+
+[ext_resource type="Script" path="res://Classes/MidiMaestro/NoteInfo.cs" id="1_7kndb"]
+[ext_resource type="Script" path="res://Classes/MidiMaestro/NoteChart.cs" id="2_xg88o"]
+
+[sub_resource type="Resource" id="Resource_7kndb"]
+script = ExtResource("1_7kndb")
+Beat = 3.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_xg88o"]
+script = ExtResource("1_7kndb")
+Beat = 6.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_g8421"]
+script = ExtResource("1_7kndb")
+Beat = 7.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_11qbm"]
+script = ExtResource("1_7kndb")
+Beat = 17.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_hrrtc"]
+script = ExtResource("1_7kndb")
+Beat = 20.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_rmog3"]
+script = ExtResource("1_7kndb")
+Beat = 22.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_tnqsp"]
+script = ExtResource("1_7kndb")
+Beat = 25.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_6n6ig"]
+script = ExtResource("1_7kndb")
+Beat = 27.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ci2g3"]
+script = ExtResource("1_7kndb")
+Beat = 32.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_2c17a"]
+script = ExtResource("1_7kndb")
+Beat = 34.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_gugsw"]
+script = ExtResource("1_7kndb")
+Beat = 39.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_j4ixp"]
+script = ExtResource("1_7kndb")
+Beat = 44.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_i2oko"]
+script = ExtResource("1_7kndb")
+Beat = 48.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_6oq4l"]
+script = ExtResource("1_7kndb")
+Beat = 50.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_s8y6q"]
+script = ExtResource("1_7kndb")
+Beat = 53.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_pdfmt"]
+script = ExtResource("1_7kndb")
+Beat = 59.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_lkwp6"]
+script = ExtResource("1_7kndb")
+Beat = 9.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_sd1fo"]
+script = ExtResource("1_7kndb")
+Beat = 12.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_wkvcn"]
+script = ExtResource("1_7kndb")
+Beat = 18.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_woki6"]
+script = ExtResource("1_7kndb")
+Beat = 21.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_gm3ao"]
+script = ExtResource("1_7kndb")
+Beat = 24.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_8sl4t"]
+script = ExtResource("1_7kndb")
+Beat = 29.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_j8ilf"]
+script = ExtResource("1_7kndb")
+Beat = 45.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_14d3f"]
+script = ExtResource("1_7kndb")
+Beat = 53.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_1qfq5"]
+script = ExtResource("1_7kndb")
+Beat = 56.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_v0l44"]
+script = ExtResource("1_7kndb")
+Beat = 59.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ywfrj"]
+script = ExtResource("1_7kndb")
+Beat = 60.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_0m2gd"]
+script = ExtResource("1_7kndb")
+Beat = 5.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_yjy3y"]
+script = ExtResource("1_7kndb")
+Beat = 12.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_31o0y"]
+script = ExtResource("1_7kndb")
+Beat = 16.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ismkv"]
+script = ExtResource("1_7kndb")
+Beat = 23.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_jkqwb"]
+script = ExtResource("1_7kndb")
+Beat = 27.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_k1pbh"]
+script = ExtResource("1_7kndb")
+Beat = 35.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_0nv8p"]
+script = ExtResource("1_7kndb")
+Beat = 39.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_bskfl"]
+script = ExtResource("1_7kndb")
+Beat = 42.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_8cd7v"]
+script = ExtResource("1_7kndb")
+Beat = 47.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_4giuv"]
+script = ExtResource("1_7kndb")
+Beat = 52.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_2wwc7"]
+script = ExtResource("1_7kndb")
+Beat = 56.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_y6l23"]
+script = ExtResource("1_7kndb")
+Beat = 62.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_wc7xs"]
+script = ExtResource("1_7kndb")
+Beat = 5.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_k53ay"]
+script = ExtResource("1_7kndb")
+Beat = 11.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_blbm2"]
+script = ExtResource("1_7kndb")
+Beat = 13.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_3gjtl"]
+script = ExtResource("1_7kndb")
+Beat = 19.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_mi50b"]
+script = ExtResource("1_7kndb")
+Beat = 23.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_vdnsu"]
+script = ExtResource("1_7kndb")
+Beat = 25.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_1xwp0"]
+script = ExtResource("1_7kndb")
+Beat = 32.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_ybe5s"]
+script = ExtResource("1_7kndb")
+Beat = 36.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_wd1lo"]
+script = ExtResource("1_7kndb")
+Beat = 40.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_eotyn"]
+script = ExtResource("1_7kndb")
+Beat = 44.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_3wyug"]
+script = ExtResource("1_7kndb")
+Beat = 51.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_77c6u"]
+script = ExtResource("1_7kndb")
+Beat = 55.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_811fa"]
+script = ExtResource("1_7kndb")
+Beat = 57.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_6kji1"]
+script = ExtResource("1_7kndb")
+Beat = 60.0
+Length = 0.0
+
+[sub_resource type="Resource" id="Resource_tdwrl"]
+script = ExtResource("1_7kndb")
+Beat = 63.0
+Length = 0.0
+
+[resource]
+script = ExtResource("2_xg88o")
+UpLaneData = [SubResource("Resource_wc7xs"), SubResource("Resource_k53ay"), SubResource("Resource_blbm2"), SubResource("Resource_3gjtl"), SubResource("Resource_mi50b"), SubResource("Resource_vdnsu"), SubResource("Resource_1xwp0"), SubResource("Resource_ybe5s"), SubResource("Resource_wd1lo"), SubResource("Resource_eotyn"), SubResource("Resource_3wyug"), SubResource("Resource_77c6u"), SubResource("Resource_811fa"), SubResource("Resource_6kji1"), SubResource("Resource_tdwrl")]
+DownLaneData = [SubResource("Resource_7kndb"), SubResource("Resource_xg88o"), SubResource("Resource_g8421"), SubResource("Resource_11qbm"), SubResource("Resource_hrrtc"), SubResource("Resource_rmog3"), SubResource("Resource_tnqsp"), SubResource("Resource_6n6ig"), SubResource("Resource_ci2g3"), SubResource("Resource_2c17a"), SubResource("Resource_gugsw"), SubResource("Resource_j4ixp"), SubResource("Resource_i2oko"), SubResource("Resource_6oq4l"), SubResource("Resource_s8y6q"), SubResource("Resource_pdfmt")]
+LeftLaneData = [SubResource("Resource_lkwp6"), SubResource("Resource_sd1fo"), SubResource("Resource_wkvcn"), SubResource("Resource_woki6"), SubResource("Resource_gm3ao"), SubResource("Resource_8sl4t"), SubResource("Resource_j8ilf"), SubResource("Resource_14d3f"), SubResource("Resource_1qfq5"), SubResource("Resource_v0l44"), SubResource("Resource_ywfrj")]
+RightLaneData = [SubResource("Resource_0m2gd"), SubResource("Resource_yjy3y"), SubResource("Resource_31o0y"), SubResource("Resource_ismkv"), SubResource("Resource_jkqwb"), SubResource("Resource_k1pbh"), SubResource("Resource_0nv8p"), SubResource("Resource_bskfl"), SubResource("Resource_8cd7v"), SubResource("Resource_4giuv"), SubResource("Resource_2wwc7"), SubResource("Resource_y6l23")]
diff --git a/Classes/MidiMaestro/MidiMaestro.cs b/Classes/MidiMaestro/MidiMaestro.cs
index 57e6fca8..1bd1964f 100644
--- a/Classes/MidiMaestro/MidiMaestro.cs
+++ b/Classes/MidiMaestro/MidiMaestro.cs
@@ -2,30 +2,23 @@
using System.Linq;
using FunkEngine;
using Godot;
-using Melanchall.DryWetMidi.Core;
-using Melanchall.DryWetMidi.Interaction;
/**
- MidiMaestro: Manages reading midi file into lane note information.
+ MidiMaestro: Manages reading a song map file into lane note information.
*/
public partial class MidiMaestro : Resource
{
- private MidiFile _midiFile;
-
- public static TempoMap TempoMap { get; private set; }
- public static TimeSignature TimeSignature { get; private set; }
-
//The four note rows that we care about
- private readonly MidiNoteInfo[] _upNotes;
- private readonly MidiNoteInfo[] _downNotes;
- private readonly MidiNoteInfo[] _leftNotes;
- private readonly MidiNoteInfo[] _rightNotes;
+ private readonly NoteInfo[] _upNotes;
+ private readonly NoteInfo[] _downNotes;
+ private readonly NoteInfo[] _leftNotes;
+ private readonly NoteInfo[] _rightNotes;
//private MidiFile strippedSong;
/**
- * Constructor loads midi file and populates lane note arrays with midiNoteInfo
- * A string file path to a valid midi file
+ * Constructor loads resource file and populates lane note arrays with NoteInfo
+ * A string file path to a valid songMap .tres file
*/
public MidiMaestro(string filePath)
{
@@ -36,44 +29,32 @@ public MidiMaestro(string filePath)
if (!FileAccess.FileExists(filePath))
{
- GD.PushError("ERROR: Unable to load level Midi file: " + filePath);
+ GD.PushError("ERROR: Unable to load level songMap resource file: " + filePath);
}
- _midiFile = MidiFile.Read(filePath);
- TempoMap = _midiFile.GetTempoMap();
- TimeSignature = TempoMap.GetTimeSignatureAtTime(new MidiTimeSpan());
+ NoteChart savedChart = ResourceLoader.Load(filePath);
- //Strip out the notes from the midi file
- foreach (var track in _midiFile.GetTrackChunks())
+ if (savedChart != null)
{
- string trackName = track.Events.OfType().FirstOrDefault()?.Text;
- MidiNoteInfo[] noteEvents = track
- .GetNotes()
- .Select(note => new MidiNoteInfo(note))
- .ToArray();
-
- switch (trackName)
- {
- case "Up":
- _upNotes = noteEvents;
- break;
- case "Down":
- _downNotes = noteEvents;
- break;
- case "Left":
- _leftNotes = noteEvents;
- break;
- case "Right":
- _rightNotes = noteEvents;
- break;
- }
+ _upNotes = savedChart.GetLane(ArrowType.Up).ToArray();
+ _downNotes = savedChart.GetLane(ArrowType.Down).ToArray();
+ _leftNotes = savedChart.GetLane(ArrowType.Left).ToArray();
+ _rightNotes = savedChart.GetLane(ArrowType.Right).ToArray();
+ }
+ else
+ {
+ GD.PushError("ERROR: Unable to load songMap resource file: " + filePath);
+ _upNotes = [];
+ _downNotes = [];
+ _leftNotes = [];
+ _rightNotes = [];
}
}
/**
- * Gets midiNoteInfo by lane.
+ * Gets NoteInfo by lane.
*/
- public MidiNoteInfo[] GetNotes(ArrowType arrowType)
+ public NoteInfo[] GetNotes(ArrowType arrowType)
{
return arrowType switch
{
@@ -85,39 +66,3 @@ public MidiNoteInfo[] GetNotes(ArrowType arrowType)
};
}
}
-
-//A facade to wrap the midi notes. This is a simple class that wraps a Note object from the DryWetMidi library.
-public class MidiNoteInfo
-{
- private readonly Melanchall.DryWetMidi.Interaction.Note _note;
-
- public MidiNoteInfo(Melanchall.DryWetMidi.Interaction.Note note)
- {
- _note = note;
- }
-
- public long GetStartTimeBeat()
- {
- var beatsBar = _note.TimeAs(MidiMaestro.TempoMap);
- return beatsBar.Bars * MidiMaestro.TimeSignature.Numerator + beatsBar.Beats;
- }
-
- public long GetStartTimeTicks() => _note.Time;
-
- public float GetStartTimeSeconds() =>
- _note.TimeAs(MidiMaestro.TempoMap).Milliseconds / 1000f
- + _note.TimeAs(MidiMaestro.TempoMap).Seconds;
-
- public long GetEndTime() => _note.EndTime; //ticks
-
- public long GetDuration() => _note.Length; //ticks
-
- public long GetDurationBeats()
- {
- var beatsBar = TimeConverter.ConvertTo(
- _note.Length,
- MidiMaestro.TempoMap
- );
- return beatsBar.Bars * MidiMaestro.TimeSignature.Numerator + beatsBar.Beats;
- }
-}
diff --git a/Classes/MidiMaestro/NoteChart.cs b/Classes/MidiMaestro/NoteChart.cs
new file mode 100644
index 00000000..5e239bdc
--- /dev/null
+++ b/Classes/MidiMaestro/NoteChart.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Linq;
+using FunkEngine;
+using Godot;
+using Godot.Collections;
+
+public partial class NoteChart : Resource
+{ //Godot is unhappy with this sometimes.
+ const float Precision = 0.0001f;
+
+ [Export]
+ Array UpLaneData = [];
+
+ [Export]
+ Array DownLaneData = [];
+
+ [Export]
+ Array LeftLaneData = [];
+
+ [Export]
+ Array RightLaneData = [];
+
+ public void Reset()
+ {
+ UpLaneData = [];
+ DownLaneData = [];
+ LeftLaneData = [];
+ RightLaneData = [];
+ }
+
+ public void SaveChart(string path)
+ {
+ ResourceSaver.Save(this, path);
+ }
+
+ public Array GetLane(ArrowType arrowType)
+ {
+ return arrowType switch
+ {
+ ArrowType.Up => UpLaneData,
+ ArrowType.Down => DownLaneData,
+ ArrowType.Left => LeftLaneData,
+ ArrowType.Right => RightLaneData,
+ _ => throw new ArgumentOutOfRangeException(nameof(arrowType), arrowType, null),
+ };
+ }
+
+ public void RemoveNote(ArrowType type, float beat)
+ {
+ if (beat == 0)
+ return; //All my homies hate beat 0
+ for (int i = 0; i < GetLane(type).Count; i++)
+ {
+ if (Math.Abs(GetLane(type)[i].Beat - beat) > Precision)
+ continue;
+ GetLane(type).RemoveAt(i);
+ return;
+ }
+ }
+
+ public void AddNote(ArrowType type, float beat, float len = 0)
+ {
+ if (beat == 0)
+ return; //All my homies hate beat 0
+ if (GetLane(type).Any(note => Math.Abs(note.Beat - beat) < Precision)) //Fuck it, traverse the whole array.
+ {
+ return;
+ }
+ GetLane(type).Add(new NoteInfo().Create(beat, len));
+ }
+}
diff --git a/Classes/MidiMaestro/NoteChart.cs.uid b/Classes/MidiMaestro/NoteChart.cs.uid
new file mode 100644
index 00000000..51ea474f
--- /dev/null
+++ b/Classes/MidiMaestro/NoteChart.cs.uid
@@ -0,0 +1 @@
+uid://bnpnavb5lwobj
diff --git a/Classes/MidiMaestro/NoteInfo.cs b/Classes/MidiMaestro/NoteInfo.cs
new file mode 100644
index 00000000..17aacd8d
--- /dev/null
+++ b/Classes/MidiMaestro/NoteInfo.cs
@@ -0,0 +1,18 @@
+using System;
+using Godot;
+
+public partial class NoteInfo : Resource
+{
+ [Export]
+ public float Beat;
+
+ [Export]
+ public float Length;
+
+ public NoteInfo Create(float beat = 0, float len = 0)
+ {
+ Beat = beat;
+ Length = len;
+ return this;
+ }
+}
diff --git a/Classes/MidiMaestro/NoteInfo.cs.uid b/Classes/MidiMaestro/NoteInfo.cs.uid
new file mode 100644
index 00000000..017bc56b
--- /dev/null
+++ b/Classes/MidiMaestro/NoteInfo.cs.uid
@@ -0,0 +1 @@
+uid://bhbpcmtr6e6pk
diff --git a/Classes/MidiMaestro/SongTemplate.cs b/Classes/MidiMaestro/SongTemplate.cs
index bb967a59..9cff9f5d 100644
--- a/Classes/MidiMaestro/SongTemplate.cs
+++ b/Classes/MidiMaestro/SongTemplate.cs
@@ -7,21 +7,21 @@ public struct SongTemplate
{
public string Name;
public readonly string AudioLocation;
- public string MIDILocation;
- public readonly string EnemyScenePath;
+ public string SongMapLocation;
+ public readonly string[] EnemyScenePath;
public SongData SongData;
public SongTemplate(
SongData songData,
string name = "",
string audioLocation = "",
- string midiLocation = "",
- string enemyScenePath = ""
+ string songMapLocation = "",
+ string[] enemyScenePath = null
)
{
Name = name;
AudioLocation = audioLocation;
- MIDILocation = midiLocation;
+ SongMapLocation = songMapLocation;
SongData = songData;
EnemyScenePath = enemyScenePath;
}
diff --git a/Classes/Notes/Assets/Note_PlayerBlock.png b/Classes/Notes/Assets/Note_PlayerBlock.png
new file mode 100644
index 00000000..4e2cf3b9
Binary files /dev/null and b/Classes/Notes/Assets/Note_PlayerBlock.png differ
diff --git a/Classes/Notes/Assets/Note_PlayerBlock.png.import b/Classes/Notes/Assets/Note_PlayerBlock.png.import
new file mode 100644
index 00000000..74152ff5
--- /dev/null
+++ b/Classes/Notes/Assets/Note_PlayerBlock.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dhfeoc3nd6t4a"
+path="res://.godot/imported/Note_PlayerBlock.png-9b2b822fe3c56ce932bb38c394998018.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Notes/Assets/Note_PlayerBlock.png"
+dest_files=["res://.godot/imported/Note_PlayerBlock.png-9b2b822fe3c56ce932bb38c394998018.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Notes/Assets/Note_PlayerEcho.png b/Classes/Notes/Assets/Note_PlayerEcho.png
new file mode 100644
index 00000000..41996f88
Binary files /dev/null and b/Classes/Notes/Assets/Note_PlayerEcho.png differ
diff --git a/Classes/Notes/Assets/Note_PlayerEcho.png.import b/Classes/Notes/Assets/Note_PlayerEcho.png.import
new file mode 100644
index 00000000..cae71efb
--- /dev/null
+++ b/Classes/Notes/Assets/Note_PlayerEcho.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c0bmoqj5tcu7s"
+path="res://.godot/imported/Note_PlayerEcho.png-354cc7c059464dfdec108f0ac5e70a23.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Notes/Assets/Note_PlayerEcho.png"
+dest_files=["res://.godot/imported/Note_PlayerEcho.png-354cc7c059464dfdec108f0ac5e70a23.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Notes/Assets/Note_PlayerExplosive.png b/Classes/Notes/Assets/Note_PlayerExplosive.png
new file mode 100644
index 00000000..f4c5cf4a
Binary files /dev/null and b/Classes/Notes/Assets/Note_PlayerExplosive.png differ
diff --git a/Classes/Notes/Assets/Note_PlayerExplosive.png.import b/Classes/Notes/Assets/Note_PlayerExplosive.png.import
new file mode 100644
index 00000000..b5057cae
--- /dev/null
+++ b/Classes/Notes/Assets/Note_PlayerExplosive.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dqjvpiccco782"
+path="res://.godot/imported/Note_PlayerExplosive.png-bd13543a7e0a0d375fad8d7b336bb4a4.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Notes/Assets/Note_PlayerExplosive.png"
+dest_files=["res://.godot/imported/Note_PlayerExplosive.png-bd13543a7e0a0d375fad8d7b336bb4a4.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Notes/Assets/Note_PlayerPoison.png b/Classes/Notes/Assets/Note_PlayerPoison.png
new file mode 100644
index 00000000..5ed8f2ac
Binary files /dev/null and b/Classes/Notes/Assets/Note_PlayerPoison.png differ
diff --git a/Classes/Notes/Assets/Note_PlayerPoison.png.import b/Classes/Notes/Assets/Note_PlayerPoison.png.import
new file mode 100644
index 00000000..f05e552a
--- /dev/null
+++ b/Classes/Notes/Assets/Note_PlayerPoison.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://byhspof1urah8"
+path="res://.godot/imported/Note_PlayerPoison.png-f7291f8fff7355d0a3d6fb23b9e61bba.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Notes/Assets/Note_PlayerPoison.png"
+dest_files=["res://.godot/imported/Note_PlayerPoison.png-f7291f8fff7355d0a3d6fb23b9e61bba.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Notes/Note.cs b/Classes/Notes/Note.cs
index ef8bf758..f6b47e3d 100644
--- a/Classes/Notes/Note.cs
+++ b/Classes/Notes/Note.cs
@@ -12,6 +12,7 @@ public partial class Note : Resource, IDisplayable
public string Name { get; set; }
private int _baseVal;
public float CostModifier { get; private set; }
+ public Targetting TargetType { get; private set; }
private Action NoteEffect;
public const double TimingMax = 0.5d; //The max range for a note to be timed is its beat +/- this const
@@ -27,24 +28,19 @@ public Note(
PuppetTemplate owner = null,
int baseVal = 1,
Action noteEffect = null,
- float costModifier = 1.0f
+ float costModifier = 1.0f,
+ Targetting targetType = Targetting.First
)
{
Id = id;
Name = name;
Owner = owner;
- NoteEffect =
- noteEffect
- ?? (
- (BD, source, timing) =>
- {
- BD.GetTarget(this).TakeDamage((int)timing * source._baseVal);
- }
- );
+ NoteEffect = noteEffect;
_baseVal = baseVal;
Texture = texture;
Tooltip = tooltip;
CostModifier = costModifier;
+ TargetType = targetType;
}
public void OnHit(BattleDirector BD, Timing timing)
@@ -64,7 +60,8 @@ public Note Clone()
Owner,
_baseVal,
NoteEffect,
- CostModifier
+ CostModifier,
+ TargetType
);
return newNote;
}
@@ -78,4 +75,9 @@ public int GetBaseVal()
{
return _baseVal;
}
+
+ public void SetBaseVal(int val)
+ {
+ _baseVal = val;
+ }
}
diff --git a/Classes/Relics/Assets/Relic_Auroboros.png b/Classes/Relics/Assets/Relic_Auroboros.png
index b264c935..c53f2c1a 100644
Binary files a/Classes/Relics/Assets/Relic_Auroboros.png and b/Classes/Relics/Assets/Relic_Auroboros.png differ
diff --git a/Classes/Relics/Assets/Relic_Bandage.png b/Classes/Relics/Assets/Relic_Bandage.png
new file mode 100644
index 00000000..d6985cdc
Binary files /dev/null and b/Classes/Relics/Assets/Relic_Bandage.png differ
diff --git a/Classes/Relics/Assets/Relic_Bandage.png.import b/Classes/Relics/Assets/Relic_Bandage.png.import
new file mode 100644
index 00000000..bf7f127b
--- /dev/null
+++ b/Classes/Relics/Assets/Relic_Bandage.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dliqeijojucd2"
+path="res://.godot/imported/Relic_Bandage.png-7332ff4bcec166f3ea2b8ef3f740e5ec.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Relics/Assets/Relic_Bandage.png"
+dest_files=["res://.godot/imported/Relic_Bandage.png-7332ff4bcec166f3ea2b8ef3f740e5ec.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Relics/Assets/Relic_Breakfast.png b/Classes/Relics/Assets/Relic_Breakfast.png
index 1c6bbe1d..37935831 100644
Binary files a/Classes/Relics/Assets/Relic_Breakfast.png and b/Classes/Relics/Assets/Relic_Breakfast.png differ
diff --git a/Classes/Relics/Assets/Relic_Chips.png b/Classes/Relics/Assets/Relic_Chips.png
new file mode 100644
index 00000000..1abfef1e
Binary files /dev/null and b/Classes/Relics/Assets/Relic_Chips.png differ
diff --git a/Classes/Relics/Assets/Relic_Chips.png.import b/Classes/Relics/Assets/Relic_Chips.png.import
new file mode 100644
index 00000000..ec17e1ea
--- /dev/null
+++ b/Classes/Relics/Assets/Relic_Chips.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://b8pluqlha2bdn"
+path="res://.godot/imported/Relic_Chips.png-07f9cdab7fe18d0afca88a4403c4bdc6.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Relics/Assets/Relic_Chips.png"
+dest_files=["res://.godot/imported/Relic_Chips.png-07f9cdab7fe18d0afca88a4403c4bdc6.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Relics/Assets/Relic_Colorboros.png b/Classes/Relics/Assets/Relic_Colorboros.png
index 2303779b..29e6ab3c 100644
Binary files a/Classes/Relics/Assets/Relic_Colorboros.png and b/Classes/Relics/Assets/Relic_Colorboros.png differ
diff --git a/Classes/Relics/Assets/Relic_EnergyDrink.png b/Classes/Relics/Assets/Relic_EnergyDrink.png
new file mode 100644
index 00000000..c9ef64d9
Binary files /dev/null and b/Classes/Relics/Assets/Relic_EnergyDrink.png differ
diff --git a/Classes/Relics/Assets/Relic_EnergyDrink.png.import b/Classes/Relics/Assets/Relic_EnergyDrink.png.import
new file mode 100644
index 00000000..b564e084
--- /dev/null
+++ b/Classes/Relics/Assets/Relic_EnergyDrink.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bi678rx26dxdd"
+path="res://.godot/imported/Relic_EnergyDrink.png-31545a8db92a3c6cae482d1f8ecceff0.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Relics/Assets/Relic_EnergyDrink.png"
+dest_files=["res://.godot/imported/Relic_EnergyDrink.png-31545a8db92a3c6cae482d1f8ecceff0.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Relics/Assets/Relic_GoodVibes.png b/Classes/Relics/Assets/Relic_GoodVibes.png
index 3ab27727..6ad51932 100644
Binary files a/Classes/Relics/Assets/Relic_GoodVibes.png and b/Classes/Relics/Assets/Relic_GoodVibes.png differ
diff --git a/Classes/Relics/Assets/Relic_Medkit.png b/Classes/Relics/Assets/Relic_Medkit.png
new file mode 100644
index 00000000..c6389197
Binary files /dev/null and b/Classes/Relics/Assets/Relic_Medkit.png differ
diff --git a/Classes/Relics/Assets/Relic_Medkit.png.import b/Classes/Relics/Assets/Relic_Medkit.png.import
new file mode 100644
index 00000000..518fd23e
--- /dev/null
+++ b/Classes/Relics/Assets/Relic_Medkit.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://wtddl5ssgrm3"
+path="res://.godot/imported/Relic_Medkit.png-44673854c4fdf8be51048f39e6d245d1.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Relics/Assets/Relic_Medkit.png"
+dest_files=["res://.godot/imported/Relic_Medkit.png-44673854c4fdf8be51048f39e6d245d1.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Relics/Assets/Relic_PaperCut.png b/Classes/Relics/Assets/Relic_PaperCut.png
new file mode 100644
index 00000000..327c5a56
Binary files /dev/null and b/Classes/Relics/Assets/Relic_PaperCut.png differ
diff --git a/Classes/Relics/Assets/Relic_PaperCut.png.import b/Classes/Relics/Assets/Relic_PaperCut.png.import
new file mode 100644
index 00000000..f91aa7f4
--- /dev/null
+++ b/Classes/Relics/Assets/Relic_PaperCut.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cmd8hjrxxk4ic"
+path="res://.godot/imported/Relic_PaperCut.png-c918160bd88b568b8e8ae4c716d26ae4.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Relics/Assets/Relic_PaperCut.png"
+dest_files=["res://.godot/imported/Relic_PaperCut.png-c918160bd88b568b8e8ae4c716d26ae4.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Relics/Assets/Relic_VinylRecord.png b/Classes/Relics/Assets/Relic_VinylRecord.png
new file mode 100644
index 00000000..b72ab653
Binary files /dev/null and b/Classes/Relics/Assets/Relic_VinylRecord.png differ
diff --git a/Classes/Relics/Assets/Relic_VinylRecord.png.import b/Classes/Relics/Assets/Relic_VinylRecord.png.import
new file mode 100644
index 00000000..e517ede6
--- /dev/null
+++ b/Classes/Relics/Assets/Relic_VinylRecord.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bk7k5dk7g1mih"
+path="res://.godot/imported/Relic_VinylRecord.png-bd107871c395e71c5f912f3df2f1bdd4.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/Relics/Assets/Relic_VinylRecord.png"
+dest_files=["res://.godot/imported/Relic_VinylRecord.png-bd107871c395e71c5f912f3df2f1bdd4.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/Relics/RelicEffect.cs b/Classes/Relics/RelicEffect.cs
index bb2cedf3..f71bf462 100644
--- a/Classes/Relics/RelicEffect.cs
+++ b/Classes/Relics/RelicEffect.cs
@@ -10,19 +10,21 @@ public partial class RelicEffect : IBattleEvent
private BattleEffectTrigger Trigger { get; set; }
private int _baseValue;
public int Value;
- private Action _onRelicEffect;
+ private Action _onRelicEffect;
private bool _effectPersists = false;
public RelicEffect(
BattleEffectTrigger trigger,
int val,
- Action onRelicEffect
+ Action onRelicEffect,
+ bool persists = false
)
{
_baseValue = val;
Value = _baseValue;
Trigger = trigger;
_onRelicEffect = onRelicEffect;
+ _effectPersists = persists;
}
public void OnBattleEnd()
@@ -31,9 +33,9 @@ public void OnBattleEnd()
Value = _baseValue;
}
- public void OnTrigger(BattleDirector battleDirector)
+ public void OnTrigger(BattleEventArgs e)
{
- _onRelicEffect(battleDirector, this, Value);
+ _onRelicEffect(e, this, Value);
}
public BattleEffectTrigger GetTrigger()
diff --git a/Classes/Relics/RelicTemplate.cs b/Classes/Relics/RelicTemplate.cs
index 3e51c656..5ae354ab 100644
--- a/Classes/Relics/RelicTemplate.cs
+++ b/Classes/Relics/RelicTemplate.cs
@@ -13,10 +13,13 @@ public partial class RelicTemplate : Resource, IDisplayable
public Texture2D Texture { get; set; }
public string Tooltip { get; set; }
+ public Rarity Rarity { get; set; }
+
public RelicTemplate(
int id,
string name = "",
string tooltip = "",
+ Rarity rarity = Rarity.Common,
Texture2D texture = null,
RelicEffect[] effectTags = null
)
@@ -26,11 +29,12 @@ public RelicTemplate(
Name = name;
Tooltip = tooltip;
Texture = texture;
+ Rarity = rarity;
}
public RelicTemplate Clone()
{
- RelicTemplate newRelic = new RelicTemplate(Id, Name, Tooltip, Texture, Effects);
+ RelicTemplate newRelic = new RelicTemplate(Id, Name, Tooltip, Rarity, Texture, Effects);
return newRelic;
}
}
diff --git a/Classes/StatusEffects/Assets/Status_Block.png b/Classes/StatusEffects/Assets/Status_Block.png
new file mode 100644
index 00000000..fe2d2f57
Binary files /dev/null and b/Classes/StatusEffects/Assets/Status_Block.png differ
diff --git a/Classes/StatusEffects/Assets/Status_Block.png.import b/Classes/StatusEffects/Assets/Status_Block.png.import
new file mode 100644
index 00000000..ebdcbce5
--- /dev/null
+++ b/Classes/StatusEffects/Assets/Status_Block.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cdlepg5fxfwn0"
+path="res://.godot/imported/Status_Block.png-e8facdf93b546460a44dfea789c90919.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/StatusEffects/Assets/Status_Block.png"
+dest_files=["res://.godot/imported/Status_Block.png-e8facdf93b546460a44dfea789c90919.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/StatusEffects/Assets/Status_Mulligan.png b/Classes/StatusEffects/Assets/Status_Mulligan.png
new file mode 100644
index 00000000..5d84a8ac
Binary files /dev/null and b/Classes/StatusEffects/Assets/Status_Mulligan.png differ
diff --git a/Classes/StatusEffects/Assets/Status_Mulligan.png.import b/Classes/StatusEffects/Assets/Status_Mulligan.png.import
new file mode 100644
index 00000000..712af8ad
--- /dev/null
+++ b/Classes/StatusEffects/Assets/Status_Mulligan.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bqhvrayjisv1"
+path="res://.godot/imported/Status_Mulligan.png-c4ed3ce3cd1b732c83e1cbf8c1373367.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/StatusEffects/Assets/Status_Mulligan.png"
+dest_files=["res://.godot/imported/Status_Mulligan.png-c4ed3ce3cd1b732c83e1cbf8c1373367.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/StatusEffects/Assets/Status_Poison.png b/Classes/StatusEffects/Assets/Status_Poison.png
new file mode 100644
index 00000000..fc9b2656
Binary files /dev/null and b/Classes/StatusEffects/Assets/Status_Poison.png differ
diff --git a/Classes/StatusEffects/Assets/Status_Poison.png.import b/Classes/StatusEffects/Assets/Status_Poison.png.import
new file mode 100644
index 00000000..07640f0f
--- /dev/null
+++ b/Classes/StatusEffects/Assets/Status_Poison.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dhoqrahjqxhpu"
+path="res://.godot/imported/Status_Poison.png-1942ffb14b97018a45894d80c7758af4.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Classes/StatusEffects/Assets/Status_Poison.png"
+dest_files=["res://.godot/imported/Status_Poison.png-1942ffb14b97018a45894d80c7758af4.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Classes/StatusEffects/StatusEffect.cs b/Classes/StatusEffects/StatusEffect.cs
new file mode 100644
index 00000000..5229e49f
--- /dev/null
+++ b/Classes/StatusEffects/StatusEffect.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Linq;
+using FunkEngine;
+using Godot;
+
+///
+/// Status Effect class.
+/// Preferably set up as a static default status, then apply status using GetInstance, but custom statuses can be defined elsewhere.
+/// Invoke StatusEnd to remove status.
+///
+public partial class StatusEffect : TextureRect, IBattleEvent
+{ //TODO: Status effects that are permanent, and status effects that don't take up a slot/are invisible
+ public static readonly string LoadPath = "res://Classes/StatusEffects/StatusIcon.tscn";
+ public PuppetTemplate Sufferer { get; private set; }
+ public int Count { get; private set; }
+
+ public string StatusName { get; private set; }
+
+ [Export]
+ private Label CountLabel { get; set; }
+
+ internal delegate void StatusEndHandler(StatusEffect status);
+ internal event StatusEndHandler StatusEnd;
+
+ #region DefaultStatuses
+ private static readonly Action BlockEffect = (e, self) =>
+ {
+ if (e is BattleDirector.Harbinger.OnDamageInstanceArgs dmgArgs)
+ {
+ if (dmgArgs.Dmg.Target != self.Sufferer || dmgArgs.Dmg.Damage <= 0)
+ return;
+ dmgArgs.Dmg.ModifyDamage(0, 0);
+ self.DecCount();
+ }
+ };
+
+ ///
+ /// On the owner receiving a damage instance, if valid (correct target and dmg > 0) sets damage to 0 and reduces count.
+ ///
+ public static readonly StatusEffect Block = new StatusEffect()
+ .InitStatus(
+ "Block",
+ BlockEffect,
+ BattleEffectTrigger.OnDamageInstance,
+ GD.Load("res://Classes/StatusEffects/Assets/Status_Block.png")
+ )
+ .SetTags(true);
+
+ private static readonly Action MulliganEffect = (e, self) =>
+ {
+ if (e is not BattleDirector.Harbinger.NoteHitArgs { Timing: Timing.Miss })
+ return;
+ e.BD.NPB.SetIgnoreMiss(true); //Intercept the miss
+ self.DecCount();
+ };
+
+ ///
+ /// If the player missed, take damage, but don't receive combo penalty.
+ ///
+ public static readonly StatusEffect Mulligan = new StatusEffect()
+ .InitStatus(
+ "Mulligan",
+ MulliganEffect,
+ BattleEffectTrigger.NoteHit,
+ GD.Load("res://Classes/StatusEffects/Assets/Status_Mulligan.png")
+ )
+ .SetTags(true);
+
+ private static readonly Action PoisonEffect = (e, self) =>
+ {
+ if (e is not BattleDirector.Harbinger.LoopEventArgs)
+ return;
+ if (self.Sufferer == null)
+ return;
+ self.Sufferer.TakeDamage(new DamageInstance(self.Count, null, null)); //TODO: More robust damage types
+ self.DecCount();
+ };
+
+ ///
+ /// On loop, the owner takes damage equal to number of stacks, then the count gets decremented.
+ ///
+ public static readonly StatusEffect Poison = new StatusEffect()
+ .InitStatus(
+ "Poison",
+ PoisonEffect,
+ BattleEffectTrigger.OnLoop,
+ GD.Load("res://Classes/StatusEffects/Assets/Status_Poison.png")
+ )
+ .SetTags(true);
+ #endregion
+
+ private BattleEffectTrigger _trigger;
+ private Action _effect;
+
+ public BattleEffectTrigger GetTrigger()
+ {
+ return _trigger;
+ }
+
+ public void OnTrigger(BattleEventArgs e)
+ {
+ _effect(e, this);
+ }
+
+ public StatusEffect InitStatus(
+ string name,
+ Action effect,
+ BattleEffectTrigger trigger,
+ Texture2D texture = null
+ )
+ {
+ _effect = effect;
+ _trigger = trigger;
+ StatusName = name;
+ Texture = texture;
+ return this;
+ }
+
+ public StatusEffect GetInstance(int count = 1)
+ {
+ StatusEffect result = GD.Load(LoadPath).Instantiate();
+ result.SetCount(count);
+ result.InitStatus(Name, _effect, _trigger, Texture);
+ result.SetTags(_stackable, _refreshes);
+ return result;
+ }
+
+ public void SetOwner(PuppetTemplate owner)
+ {
+ Sufferer = owner;
+ }
+
+ public void IncCount(int count = 1)
+ {
+ SetCount(Count + count);
+ }
+
+ public void DecCount(int count = 1)
+ {
+ SetCount(Count - count);
+ }
+
+ public void SetCount(int count)
+ {
+ Count = count;
+ CountLabel.Text = Count.ToString();
+ if (Count <= 0)
+ {
+ StatusEnd?.Invoke(this);
+ }
+ }
+
+ ///
+ /// Re-applying a status increases the count.
+ ///
+ private bool _stackable;
+
+ ///
+ /// Re-applying a status sets the count to the higher counte
+ ///
+ private bool _refreshes;
+
+ public StatusEffect SetTags(bool stackable = false, bool refreshes = false)
+ {
+ _stackable = stackable;
+ _refreshes = refreshes;
+ return this;
+ }
+
+ //Called if a puppet is receiving a duplicate effect.
+ public void StackEffect(StatusEffect incomingEffect)
+ {
+ if (incomingEffect.StatusName != StatusName)
+ return;
+ if (_stackable)
+ {
+ IncCount(incomingEffect.Count);
+ }
+
+ if (_refreshes && incomingEffect.Count >= Count)
+ {
+ SetCount(incomingEffect.Count);
+ }
+ }
+}
diff --git a/Classes/StatusEffects/StatusEffect.cs.uid b/Classes/StatusEffects/StatusEffect.cs.uid
new file mode 100644
index 00000000..ed242236
--- /dev/null
+++ b/Classes/StatusEffects/StatusEffect.cs.uid
@@ -0,0 +1 @@
+uid://wawjisy70w1v
diff --git a/Classes/StatusEffects/StatusIcon.tscn b/Classes/StatusEffects/StatusIcon.tscn
new file mode 100644
index 00000000..9ca0495f
--- /dev/null
+++ b/Classes/StatusEffects/StatusIcon.tscn
@@ -0,0 +1,36 @@
+[gd_scene load_steps=4 format=3 uid="uid://opqtl7khulko"]
+
+[ext_resource type="Script" uid="uid://wawjisy70w1v" path="res://Classes/StatusEffects/StatusEffect.cs" id="1_2adc3"]
+
+[sub_resource type="Gradient" id="Gradient_y1lef"]
+offsets = PackedFloat32Array(1)
+colors = PackedColorArray(1, 1, 1, 1)
+
+[sub_resource type="GradientTexture2D" id="GradientTexture2D_2adc3"]
+gradient = SubResource("Gradient_y1lef")
+width = 16
+height = 16
+
+[node name="StatusIcon" type="TextureRect" node_paths=PackedStringArray("CountLabel")]
+custom_minimum_size = Vector2(8, 8)
+offset_right = 8.0
+offset_bottom = 8.0
+texture = SubResource("GradientTexture2D_2adc3")
+script = ExtResource("1_2adc3")
+CountLabel = NodePath("Count")
+
+[node name="Count" type="Label" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_left = 2.0
+offset_top = 4.0
+offset_bottom = 2.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_colors/font_color = Color(0, 0, 0, 1)
+theme_override_font_sizes/font_size = 12
+text = "0"
+horizontal_alignment = 2
+vertical_alignment = 2
diff --git a/Funk Engine.csproj b/Funk Engine.csproj
index 2eddb4c7..ae20ca2b 100644
--- a/Funk Engine.csproj
+++ b/Funk Engine.csproj
@@ -4,9 +4,6 @@
true
FunkEngine
-
-
-
diff --git a/Globals/FunkEngineNameSpace.cs b/Globals/FunkEngineNameSpace.cs
index 34c03684..c73800f6 100644
--- a/Globals/FunkEngineNameSpace.cs
+++ b/Globals/FunkEngineNameSpace.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using FunkEngine.Classes.MidiMaestro;
using Godot;
@@ -34,7 +35,7 @@ public struct BattleConfig
{
public Stages RoomType;
public MapGrid.Room BattleRoom;
- public string EnemyScenePath;
+ public string[] EnemyScenePath;
public SongTemplate CurSong;
}
@@ -222,6 +223,13 @@ public enum Timing
Perfect = 4,
}
+public enum Targetting
+{
+ Player,
+ First,
+ All,
+}
+
public enum BattleEffectTrigger
{
NotePlaced,
@@ -229,6 +237,10 @@ public enum BattleEffectTrigger
SelfNoteHit,
OnPickup,
OnLoop,
+ OnBattleStart,
+ OnBattleEnd,
+ OnDamageInstance,
+ OnDamageTaken,
}
public enum Stages
@@ -240,6 +252,23 @@ public enum Stages
Quit,
Map,
Load,
+ Continue,
+}
+
+public enum Area
+{
+ Forest = 0,
+ City = 1,
+}
+
+public enum Rarity
+{
+ Breakfast = 5,
+ Common = 4,
+ Uncommon = 3,
+ Rare = 2,
+ Epic = 1,
+ Legendary = 0,
}
#endregion
@@ -262,7 +291,7 @@ public Room[] GetRooms()
public class Room
{
public int Idx { get; private set; }
- public int[] Children { get; private set; } = Array.Empty();
+ public int[] Children { get; private set; } = [];
public int X { get; private set; }
public int Y { get; private set; }
public Stages Type { get; private set; }
@@ -287,59 +316,111 @@ public void AddChild(int newIdx)
}
}
+ //TODO: Make odds for rooms based on y-level, e.g. elites only spawn on y > 3
+ public struct MapConfig
+ {
+ public int Width { get; private set; }
+ public int Height { get; private set; }
+ public int Paths { get; private set; }
+
+ ///
+ /// Rooms that exist at set levels, only one room can be set per y-level.
+ ///
+ public Dictionary SetRooms { get; private set; } =
+ new()
+ {
+ { 0, Stages.Battle }, //The first, e.g. y = 0 room, should always be a battle.
+ };
+
+ public const int NumStages = 2;
+
+ public static readonly Stages[] StagsToRoll = new[] { Stages.Battle, Stages.Chest };
+
+ ///
+ /// The odds for each stage to appear in a non-set room position.
+ ///
+ public float[] StageOdds = new float[2];
+
+ public MapConfig(int width, int height, int paths, float[] odds)
+ {
+ Width = width;
+ Height = height;
+ Paths = paths;
+ for (int i = 0; i < NumStages; i++)
+ {
+ StageOdds[i] = odds[i];
+ }
+ }
+
+ ///
+ /// Adds a set room type to be generated guaranteed. Additional entries in the same y-level are ignored.
+ ///
+ /// The y-level of the rooms
+ /// The room type to be set.
+ public MapConfig AddSetRoom(int height, Stages roomType)
+ {
+ SetRooms.TryAdd(height, roomType);
+ return this;
+ }
+ }
+
/**
* Initializes the map with max width, max height, and with number of paths.
*/
- public void InitMapGrid(int width, int height, int paths)
+ public void InitMapGrid(MapConfig curConfig)
{
_curIdx = 0;
- _rooms = Array.Empty();
- _map = new int[width, height]; //x,y
+ _rooms = [];
+ _map = new int[curConfig.Width, curConfig.Height]; //x,y
- int startX = (width / 2);
+ int startX = (curConfig.Width / 2);
_rooms = _rooms.Append(new Room(_curIdx, startX, 0)).ToArray();
_rooms[0].SetType(Stages.Battle);
_map[startX, 0] = _curIdx++;
- for (int i = 0; i < paths; i++)
+ for (int i = 0; i < curConfig.Paths; i++)
{
- GeneratePath_r(startX, 0, width, height);
+ GeneratePath_r(startX, 0, curConfig);
}
- CreateCommonChildren(width, height);
- AddBossRoom(width, height);
+ CreateCommonChildren(curConfig.Width, curConfig.Height);
+ AddBossRoom(curConfig.Width, curConfig.Height);
}
/**Start at x, y, assume prev room exists. Picks new x pos within +/- 1, attaches recursively*/
- private void GeneratePath_r(int x, int y, int width, int height)
+ private void GeneratePath_r(int x, int y, MapConfig curConfig)
{
int nextX = StageProducer.GlobalRng.RandiRange(
Math.Max(x - 1, 0),
- Math.Min(x + 1, width - 1)
+ Math.Min(x + 1, curConfig.Width - 1)
);
if (_map[nextX, y + 1] == 0)
{
_rooms = _rooms.Append(new Room(_curIdx, nextX, y + 1)).ToArray();
_map[nextX, y + 1] = _curIdx;
_rooms[_map[x, y]].AddChild(_curIdx++);
- _rooms[^1].SetType(PickRoomType(x, y));
+ _rooms[^1].SetType(PickRoomType(x, y, curConfig));
}
else
{
_rooms[_map[x, y]].AddChild(_map[nextX, y + 1]);
}
- if (y < height - 2)
+ if (y < curConfig.Height - 2)
{
- GeneratePath_r(nextX, y + 1, width, height);
+ GeneratePath_r(nextX, y + 1, curConfig);
}
}
- private Stages PickRoomType(int x, int y)
+ private Stages PickRoomType(int x, int y, MapConfig curConfig)
{
- if (y % 3 == 0)
- return Stages.Chest;
- if (StageProducer.GlobalRng.Randf() < .08)
- return Stages.Chest;
- return Stages.Battle;
+ //If the y has a set room return it.
+ if (curConfig.SetRooms.TryGetValue(y, out Stages result))
+ {
+ return result;
+ }
+
+ //Random roll for the room type.
+ int idx = (int)StageProducer.GlobalRng.RandWeighted(curConfig.StageOdds);
+ return MapConfig.StagsToRoll[idx];
}
//Asserts that if there is a room at the same x, but y+1 they are connected
@@ -372,12 +453,18 @@ private void AddBossRoom(int width, int height)
}
#region Interfaces
+
+public class BattleEventArgs(BattleDirector director) : EventArgs
+{
+ public BattleDirector BD = director;
+}
+
/**
* A BattleDirector driven battle event. Needs an enum defined trigger.
*/
public interface IBattleEvent
{
- void OnTrigger(BattleDirector BD);
+ void OnTrigger(BattleEventArgs e);
BattleEffectTrigger GetTrigger();
}
diff --git a/Globals/SaveSystem.cs b/Globals/SaveSystem.cs
index de754cfb..cb522c32 100644
--- a/Globals/SaveSystem.cs
+++ b/Globals/SaveSystem.cs
@@ -1,3 +1,4 @@
+using System;
using System.Linq;
using System.Text.Json;
using Godot;
@@ -13,14 +14,38 @@ public static class SaveSystem
private static ConfigFile _curConfigData;
private const float DefaultVolume = 1f;
- private const string DefaultInput = "WASD";
+ private const string DefaultInputType = "WASD";
+ private const int DefaultInputKeyboardUp = 87; //W
+ private const int DefaultInputKeyboardLeft = 65; //A
+ private const int DefaultInputKeyboardDown = 83; //S
+ private const int DefaultInputKeyboardRight = 68; //D
+ private const int DefaultInputKeyboardSecondary = 4194325; //Shift
+ private const int DefaultInputKeyboardInventory = 73; //I
+ private const int DefaultInputControllerUp = 3; //Y
+ private const int DefaultInputControllerLeft = 2; //X
+ private const int DefaultInputControllerDown = 0; //A
+ private const int DefaultInputControllerRight = 1; //B
+ private const int DefaultInputControllerSecondary = 10; //right bumper
+ private const int DefaultInputControllerInventory = 4; //back button
private const string DefaultLanguage = "en";
private const bool DefaultHighCon = false;
public enum ConfigSettings
{
Volume,
- InputKey,
+ InputType,
+ InputKeyboardUp,
+ InputKeyboardLeft,
+ InputKeyboardDown,
+ InputKeyboardRight,
+ InputKeyboardSecondary,
+ InputKeyboardInventory,
+ InputControllerUp,
+ InputControllerLeft,
+ InputControllerDown,
+ InputControllerRight,
+ InputControllerSecondary,
+ InputControllerInventory,
LanguageKey,
HighContrast,
}
@@ -32,7 +57,19 @@ private static void InitConfig()
{
_curConfigData = new ConfigFile();
UpdateConfig(ConfigSettings.Volume, DefaultVolume);
- UpdateConfig(ConfigSettings.InputKey, DefaultInput);
+ UpdateConfig(ConfigSettings.InputType, DefaultInputType);
+ UpdateConfig(ConfigSettings.InputKeyboardUp, DefaultInputKeyboardUp);
+ UpdateConfig(ConfigSettings.InputKeyboardLeft, DefaultInputKeyboardLeft);
+ UpdateConfig(ConfigSettings.InputKeyboardDown, DefaultInputKeyboardDown);
+ UpdateConfig(ConfigSettings.InputKeyboardRight, DefaultInputKeyboardRight);
+ UpdateConfig(ConfigSettings.InputKeyboardSecondary, DefaultInputKeyboardSecondary);
+ UpdateConfig(ConfigSettings.InputKeyboardInventory, DefaultInputKeyboardInventory);
+ UpdateConfig(ConfigSettings.InputControllerUp, DefaultInputControllerUp);
+ UpdateConfig(ConfigSettings.InputControllerLeft, DefaultInputControllerLeft);
+ UpdateConfig(ConfigSettings.InputControllerDown, DefaultInputControllerDown);
+ UpdateConfig(ConfigSettings.InputControllerRight, DefaultInputControllerRight);
+ UpdateConfig(ConfigSettings.InputControllerSecondary, DefaultInputControllerSecondary);
+ UpdateConfig(ConfigSettings.InputControllerInventory, DefaultInputControllerInventory);
UpdateConfig(ConfigSettings.LanguageKey, DefaultLanguage);
UpdateConfig(ConfigSettings.HighContrast, DefaultHighCon);
}
@@ -51,9 +88,45 @@ public static void UpdateConfig(ConfigSettings setting, Variant value)
case ConfigSettings.Volume:
_curConfigData.SetValue("Options", "Volume", value);
break;
- case ConfigSettings.InputKey:
+ case ConfigSettings.InputType:
_curConfigData.SetValue("Options", "InputKey", value);
break;
+ case ConfigSettings.InputKeyboardUp:
+ _curConfigData.SetValue("Options", "InputKeyboardUp", value);
+ break;
+ case ConfigSettings.InputKeyboardLeft:
+ _curConfigData.SetValue("Options", "InputKeyboardLeft", value);
+ break;
+ case ConfigSettings.InputKeyboardDown:
+ _curConfigData.SetValue("Options", "InputKeyboardDown", value);
+ break;
+ case ConfigSettings.InputKeyboardRight:
+ _curConfigData.SetValue("Options", "InputKeyboardRight", value);
+ break;
+ case ConfigSettings.InputKeyboardSecondary:
+ _curConfigData.SetValue("Options", "InputKeyboardSecondary", value);
+ break;
+ case ConfigSettings.InputKeyboardInventory:
+ _curConfigData.SetValue("Options", "InputKeyboardInventory", value);
+ break;
+ case ConfigSettings.InputControllerUp:
+ _curConfigData.SetValue("Options", "InputControllerUp", value);
+ break;
+ case ConfigSettings.InputControllerLeft:
+ _curConfigData.SetValue("Options", "InputControllerLeft", value);
+ break;
+ case ConfigSettings.InputControllerDown:
+ _curConfigData.SetValue("Options", "InputControllerDown", value);
+ break;
+ case ConfigSettings.InputControllerRight:
+ _curConfigData.SetValue("Options", "InputControllerRight", value);
+ break;
+ case ConfigSettings.InputControllerSecondary:
+ _curConfigData.SetValue("Options", "InputControllerSecondary", value);
+ break;
+ case ConfigSettings.InputControllerInventory:
+ _curConfigData.SetValue("Options", "InputControllerInventory", value);
+ break;
case ConfigSettings.LanguageKey:
_curConfigData.SetValue("Options", "LanguageKey", value);
break;
@@ -74,6 +147,7 @@ private static void AssertConfigFile()
if (_curConfigData == null)
{
LoadConfigData();
+ ApplySavedInputBindings();
}
}
@@ -118,8 +192,80 @@ public static Variant GetConfigValue(ConfigSettings setting)
{
case ConfigSettings.Volume:
return _curConfigData.GetValue("Options", "Volume", DefaultVolume);
- case ConfigSettings.InputKey:
- return _curConfigData.GetValue("Options", "InputKey", DefaultInput);
+ case ConfigSettings.InputType:
+ return _curConfigData.GetValue("Options", "InputKey", DefaultInputType);
+ case ConfigSettings.InputKeyboardUp:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputKeyboardUp",
+ DefaultInputKeyboardUp
+ );
+ case ConfigSettings.InputKeyboardLeft:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputKeyboardLeft",
+ DefaultInputKeyboardLeft
+ );
+ case ConfigSettings.InputKeyboardDown:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputKeyboardDown",
+ DefaultInputKeyboardDown
+ );
+ case ConfigSettings.InputKeyboardRight:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputKeyboardRight",
+ DefaultInputKeyboardRight
+ );
+ case ConfigSettings.InputKeyboardSecondary:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputKeyboardSecondary",
+ DefaultInputKeyboardSecondary
+ );
+ case ConfigSettings.InputKeyboardInventory:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputKeyboardInventory",
+ DefaultInputKeyboardInventory
+ );
+ case ConfigSettings.InputControllerUp:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputControllerUp",
+ DefaultInputControllerUp
+ );
+ case ConfigSettings.InputControllerLeft:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputControllerLeft",
+ DefaultInputControllerLeft
+ );
+ case ConfigSettings.InputControllerDown:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputControllerDown",
+ DefaultInputControllerDown
+ );
+ case ConfigSettings.InputControllerRight:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputControllerRight",
+ DefaultInputControllerRight
+ );
+ case ConfigSettings.InputControllerSecondary:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputControllerSecondary",
+ DefaultInputControllerSecondary
+ );
+ case ConfigSettings.InputControllerInventory:
+ return _curConfigData.GetValue(
+ "Options",
+ "InputControllerInventory",
+ DefaultInputControllerInventory
+ );
case ConfigSettings.LanguageKey:
return _curConfigData.GetValue("Options", "LanguageKey", DefaultLanguage);
case ConfigSettings.HighContrast:
@@ -129,6 +275,91 @@ public static Variant GetConfigValue(ConfigSettings setting)
return float.MinValue;
}
}
+
+ public static void ApplySavedInputBindings()
+ {
+ InputMap.ActionEraseEvents("WASD_arrowUp");
+ InputMap.ActionEraseEvents("WASD_arrowDown");
+ InputMap.ActionEraseEvents("WASD_arrowRight");
+ InputMap.ActionEraseEvents("WASD_arrowLeft");
+ InputMap.ActionEraseEvents("WASD_secondaryPlacement");
+ InputMap.ActionEraseEvents("WASD_inventory");
+ InputMap.ActionEraseEvents("CONTROLLER_arrowUp");
+ InputMap.ActionEraseEvents("CONTROLLER_arrowDown");
+ InputMap.ActionEraseEvents("CONTROLLER_arrowLeft");
+ InputMap.ActionEraseEvents("CONTROLLER_arrowRight");
+ InputMap.ActionEraseEvents("CONTROLLER_secondaryPlacement");
+ InputMap.ActionEraseEvents("CONTROLLER_inventory");
+
+ // Keyboard bindings
+ AddKeyBinding("WASD_arrowUp", GetConfigValue(ConfigSettings.InputKeyboardUp).ToString());
+ AddKeyBinding(
+ "WASD_arrowDown",
+ GetConfigValue(ConfigSettings.InputKeyboardDown).ToString()
+ );
+ AddKeyBinding(
+ "WASD_arrowLeft",
+ GetConfigValue(ConfigSettings.InputKeyboardLeft).ToString()
+ );
+ AddKeyBinding(
+ "WASD_arrowRight",
+ GetConfigValue(ConfigSettings.InputKeyboardRight).ToString()
+ );
+ AddKeyBinding(
+ "WASD_secondaryPlacement",
+ GetConfigValue(ConfigSettings.InputKeyboardSecondary).ToString()
+ );
+ AddKeyBinding(
+ "WASD_inventory",
+ GetConfigValue(ConfigSettings.InputKeyboardInventory).ToString()
+ );
+
+ // Controller bindings
+ AddJoypadBinding(
+ "CONTROLLER_arrowUp",
+ GetConfigValue(ConfigSettings.InputControllerUp).ToString()
+ );
+ AddJoypadBinding(
+ "CONTROLLER_arrowDown",
+ GetConfigValue(ConfigSettings.InputControllerDown).ToString()
+ );
+ AddJoypadBinding(
+ "CONTROLLER_arrowLeft",
+ GetConfigValue(ConfigSettings.InputControllerLeft).ToString()
+ );
+ AddJoypadBinding(
+ "CONTROLLER_arrowRight",
+ GetConfigValue(ConfigSettings.InputControllerRight).ToString()
+ );
+ AddJoypadBinding(
+ "CONTROLLER_secondaryPlacement",
+ GetConfigValue(ConfigSettings.InputControllerSecondary).ToString()
+ );
+ AddJoypadBinding(
+ "CONTROLLER_inventory",
+ GetConfigValue(ConfigSettings.InputControllerInventory).ToString()
+ );
+ }
+
+ private static void AddKeyBinding(string action, string keyString)
+ {
+ Key key = (Key)Enum.Parse(typeof(Key), keyString, ignoreCase: true);
+ InputEventKey inputEvent = new InputEventKey { PhysicalKeycode = key };
+ InputMap.ActionAddEvent(action, inputEvent);
+ }
+
+ private static void AddJoypadBinding(string action, string buttonString)
+ {
+ if (Enum.TryParse(buttonString, true, out JoyButton button))
+ {
+ InputEventJoypadButton inputEvent = new InputEventJoypadButton { ButtonIndex = button };
+ InputMap.ActionAddEvent(action, inputEvent);
+ }
+ else
+ {
+ GD.PushWarning($"Could not parse joypad button: {buttonString}");
+ }
+ }
#endregion
#region Save
@@ -140,6 +371,7 @@ public class SaveFile
public ulong RngSeed { get; init; }
public ulong RngState { get; init; }
public int LastRoomIdx { get; init; }
+ public int Area { get; init; }
public int[] NoteIds { get; init; }
public int[] RelicIds { get; init; }
@@ -151,7 +383,8 @@ public SaveFile(
int lastRoomIdx,
int[] noteIds,
int[] relicIds,
- int playerHealth
+ int playerHealth,
+ int area
)
{
RngSeed = rngSeed;
@@ -160,6 +393,7 @@ int playerHealth
NoteIds = noteIds;
RelicIds = relicIds;
PlayerHealth = playerHealth;
+ Area = area;
}
}
@@ -173,7 +407,8 @@ public static void SaveGame()
StageProducer.CurRoom,
noteIds,
relicIds,
- StageProducer.PlayerStats.CurrentHealth
+ StageProducer.PlayerStats.CurrentHealth,
+ (int)StageProducer.CurArea
);
string json = JsonSerializer.Serialize(sv);
@@ -211,6 +446,5 @@ public static void ClearSave()
{
DirAccess.RemoveAbsolute(UserSavePath);
}
-
#endregion
}
diff --git a/Globals/Scribe.cs b/Globals/Scribe.cs
index 526a60a0..e15a8b97 100644
--- a/Globals/Scribe.cs
+++ b/Globals/Scribe.cs
@@ -1,8 +1,11 @@
+using System.Collections.Generic;
using System.Linq;
using FunkEngine;
using FunkEngine.Classes.MidiMaestro;
using Godot;
+// ReSharper disable UnusedParameter.Local
+
/**
* Catch all for storing defined data. Catch all as single source of truth for items and battles.
*/
@@ -19,7 +22,8 @@ public partial class Scribe : Node
1,
(director, note, timing) =>
{
- director.Player.TakeDamage((3 - (int)timing) * note.GetBaseVal());
+ int dmg = (3 - (int)timing) * note.GetBaseVal();
+ director.Player.TakeDamage(new DamageInstance(dmg, null, director.Player));
}
),
new Note(
@@ -33,7 +37,7 @@ public partial class Scribe : Node
{
if (timing == Timing.Miss)
return;
- director.Enemy.TakeDamage((int)timing * note.GetBaseVal());
+ director.DealDamage(note, (int)timing * note.GetBaseVal(), director.Player);
}
),
new Note(
@@ -47,7 +51,7 @@ public partial class Scribe : Node
{
if (timing == Timing.Miss)
return;
- director.Enemy.TakeDamage(note.GetBaseVal() * (int)timing);
+ director.DealDamage(note, (int)timing * note.GetBaseVal(), director.Player);
}
),
new Note(
@@ -75,8 +79,9 @@ public partial class Scribe : Node
{
if (timing == Timing.Miss)
return;
- director.Player.Heal((int)timing * note.GetBaseVal());
- director.Enemy.TakeDamage((int)timing * note.GetBaseVal());
+ int dmg = (int)timing * note.GetBaseVal();
+ director.Player.Heal(dmg);
+ director.DealDamage(note, dmg, director.Player);
}
),
new Note(
@@ -90,10 +95,69 @@ public partial class Scribe : Node
{
if (timing == Timing.Miss)
return;
- director.Enemy.TakeDamage((int)timing + note.GetBaseVal());
+ director.DealDamage(note, (int)timing * note.GetBaseVal(), director.Player);
},
0.25f
),
+ new Note(
+ 6,
+ "PlayerBlock",
+ "Gives player one charge of block.",
+ GD.Load("res://Classes/Notes/Assets/Note_PlayerBlock.png"),
+ null,
+ 1,
+ (director, note, timing) =>
+ {
+ if (timing == Timing.Miss)
+ return;
+ director.AddStatus(Targetting.Player, StatusEffect.Block.GetInstance()); //todo: should scale with timing????
+ }
+ ),
+ new Note(
+ 7,
+ "PlayerExplosive",
+ "Deals damage to all enemies.",
+ GD.Load("res://Classes/Notes/Assets/Note_PlayerExplosive.png"),
+ null,
+ 1,
+ (director, note, timing) =>
+ {
+ if (timing == Timing.Miss)
+ return;
+ director.DealDamage(note, (int)timing * note.GetBaseVal(), director.Player);
+ },
+ 1f,
+ Targetting.All
+ ),
+ new Note(
+ 8,
+ "PlayerEcho",
+ "Deals more damage with each loop.",
+ GD.Load("res://Classes/Notes/Assets/Note_PlayerEcho.png"),
+ null,
+ 1,
+ (director, note, timing) =>
+ {
+ if (timing == Timing.Miss)
+ return;
+ director.DealDamage(note, (int)timing * note.GetBaseVal(), director.Player);
+ note.SetBaseVal(note.GetBaseVal() + 1);
+ }
+ ),
+ new Note(
+ 9,
+ "PlayerPoison",
+ "Applies stacks of poison based on timing.",
+ GD.Load("res://Classes/Notes/Assets/Note_PlayerPoison.png"),
+ null,
+ 1,
+ (director, note, timing) =>
+ {
+ if (timing == Timing.Miss)
+ return;
+ director.AddStatus(Targetting.First, StatusEffect.Poison.GetInstance((int)timing));
+ }
+ ),
};
public static readonly RelicTemplate[] RelicDictionary = new[]
@@ -102,13 +166,14 @@ public partial class Scribe : Node
0,
"Breakfast", //Reference ha ha, Item to give when relic pool is empty.
"Increases max hp.", //TODO: Description can include the relics values?
+ Rarity.Breakfast,
GD.Load("res://Classes/Relics/Assets/Relic_Breakfast.png"),
new RelicEffect[]
{
new RelicEffect(
BattleEffectTrigger.OnPickup,
10,
- (director, self, val) =>
+ (e, self, val) =>
{
StageProducer.PlayerStats.MaxHealth += val;
StageProducer.PlayerStats.CurrentHealth += val;
@@ -120,15 +185,16 @@ public partial class Scribe : Node
1,
"Good Vibes",
"Heals the player whenever they place a note.",
+ Rarity.Common,
GD.Load("res://Classes/Relics/Assets/Relic_GoodVibes.png"),
new RelicEffect[]
{
new RelicEffect(
BattleEffectTrigger.NotePlaced,
2,
- (director, self, val) =>
+ (e, self, val) =>
{
- director.Player.Heal(val);
+ e.BD.Player.Heal(val);
}
),
}
@@ -137,15 +203,16 @@ public partial class Scribe : Node
2,
"Auroboros",
"Bigger number, better person. Increases combo multiplier every riff.",
+ Rarity.Common,
GD.Load("res://Classes/Relics/Assets/Relic_Auroboros.png"),
new RelicEffect[]
{
new RelicEffect(
BattleEffectTrigger.OnLoop,
1,
- (director, self, val) =>
+ (e, self, val) =>
{
- director.NPB.IncreaseBonusMult(val);
+ e.BD.NPB.IncreaseBonusMult(val);
self.Value++;
}
),
@@ -155,23 +222,139 @@ public partial class Scribe : Node
3,
"Colorboros",
"Taste the rainbow. Charges the freestyle bar every riff.",
+ Rarity.Common,
GD.Load("res://Classes/Relics/Assets/Relic_Colorboros.png"),
new RelicEffect[]
{
new RelicEffect(
BattleEffectTrigger.OnLoop,
10,
- (director, self, val) =>
+ (e, self, val) =>
{
- director.NPB.IncreaseCharge(val);
+ e.BD.NPB.IncreaseCharge(val);
self.Value += 5;
}
),
}
),
+ new RelicTemplate(
+ 4,
+ "Chips",
+ "Hitting a note deals a bit of damage.",
+ Rarity.Rare, //This thing is really good imo.
+ GD.Load("res://Classes/Relics/Assets/Relic_Chips.png"),
+ new RelicEffect[]
+ {
+ new RelicEffect(
+ BattleEffectTrigger.NoteHit,
+ 1,
+ (e, self, val) =>
+ {
+ if (e is not BattleDirector.Harbinger.NoteHitArgs noteHitArgs)
+ return;
+ if (noteHitArgs.Timing != Timing.Miss)
+ e.BD.DealDamage(Targetting.First, val, null);
+ }
+ ),
+ }
+ ),
+ new RelicTemplate(
+ 5,
+ "Paper Cut",
+ "Deals damage each loop.",
+ Rarity.Common,
+ GD.Load("res://Classes/Relics/Assets/Relic_PaperCut.png"),
+ new RelicEffect[]
+ {
+ new RelicEffect(
+ BattleEffectTrigger.OnLoop,
+ 5,
+ (e, self, val) =>
+ {
+ e.BD.DealDamage(Targetting.First, val, null);
+ }
+ ),
+ }
+ ),
+ new RelicTemplate(
+ 6,
+ "Energy Drink",
+ "Take a chance to cool down and sip an energy drink to increase your max energy bar.",
+ Rarity.Common,
+ GD.Load("res://Classes/Relics/Assets/Relic_EnergyDrink.png"),
+ new RelicEffect[]
+ {
+ new RelicEffect(
+ BattleEffectTrigger.OnPickup,
+ 10,
+ (e, self, val) =>
+ {
+ StageProducer.PlayerStats.MaxComboBar -= val;
+ }
+ ),
+ }
+ ),
+ new RelicTemplate(
+ 7,
+ "Bandage",
+ "A clean strip of cloth. Use it after a fight to patch up and feel better.",
+ Rarity.Common,
+ GD.Load("res://Classes/Relics/Assets/Relic_Bandage.png"),
+ new RelicEffect[]
+ {
+ new RelicEffect(
+ BattleEffectTrigger.OnBattleEnd,
+ 10,
+ (e, self, val) =>
+ {
+ StageProducer.PlayerStats.CurrentHealth += val;
+ }
+ ),
+ }
+ ),
+ new RelicTemplate(
+ 8,
+ "Medkit",
+ "A small kit with medical supplies. Heals you a bit after each loop.",
+ Rarity.Common,
+ GD.Load("res://Classes/Relics/Assets/Relic_Medkit.png"),
+ new RelicEffect[]
+ {
+ new RelicEffect(
+ BattleEffectTrigger.OnLoop,
+ 5,
+ (e, self, val) =>
+ {
+ e.BD.Player.Heal(val);
+ }
+ ),
+ }
+ ),
+ new RelicTemplate(
+ 9,
+ "Vinyl Record",
+ "Right round, right round. All loop effects trigger twice.",
+ Rarity.Legendary,
+ GD.Load("res://Classes/Relics/Assets/Relic_VinylRecord.png"),
+ new RelicEffect[]
+ {
+ new RelicEffect(
+ BattleEffectTrigger.OnLoop,
+ 0,
+ (e, self, val) =>
+ {
+ if (
+ (e is BattleDirector.Harbinger.LoopEventArgs eLoop)
+ && !eLoop.ArtificialLoop
+ )
+ BattleDirector.Harbinger.Instance.InvokeChartLoop(eLoop.Loop);
+ }
+ ),
+ }
+ ),
};
- public static readonly SongTemplate[] SongDictionary = new[]
+ public static readonly SongTemplate[] SongDictionary = new[] //Generalize and make pools for areas/room types
{
new SongTemplate(
new SongData
@@ -182,7 +365,8 @@ public partial class Scribe : Node
},
"Song1",
"Audio/Song1.ogg",
- "Audio/Midi/Song1.mid"
+ "Audio/songMaps/Song1.tres",
+ [P_BossBlood.LoadPath]
),
new SongTemplate(
new SongData
@@ -193,8 +377,20 @@ public partial class Scribe : Node
},
"Song2",
"Audio/Song2.ogg",
- "Audio/Midi/Song2.mid",
- P_Parasifly.LoadPath
+ "Audio/songMaps/Song2.tres",
+ [P_Parasifly.LoadPath]
+ ),
+ new SongTemplate(
+ new SongData
+ {
+ Bpm = 60,
+ SongLength = -1,
+ NumLoops = 1,
+ },
+ "Song2",
+ "Audio/Song2.ogg",
+ "Audio/songMaps/Song2.tres",
+ [P_Parasifly.LoadPath, P_Parasifly.LoadPath]
),
new SongTemplate(
new SongData
@@ -205,43 +401,136 @@ public partial class Scribe : Node
},
"Song3",
"Audio/Song3.ogg",
- "Audio/Midi/Song3.mid",
- P_TheGWS.LoadPath
+ "Audio/songMaps/Song3.tres",
+ [P_TheGWS.LoadPath]
),
};
- //TODO: Item pool(s)
+ //Needs to be strictly maintained based on what the player has obtained.
+ private static List[] _relicRarityPools = null;
- public static RelicTemplate[] GetRandomRelics(RelicTemplate[] excludedRelics, int count)
+ public static void InitRelicPools()
{
- var availableRelics = Scribe
- .RelicDictionary.Where(r => excludedRelics.All(o => o.Name != r.Name))
- .ToArray();
+ _relicRarityPools = new List[(int)Rarity.Breakfast + 1];
+ for (int i = 0; i <= (int)Rarity.Breakfast; i++)
+ {
+ _relicRarityPools[i] = new List();
+ }
+
+ foreach (RelicTemplate relic in RelicDictionary)
+ {
+ _relicRarityPools[(int)relic.Rarity].Add(relic.Id);
+ }
+ }
+
+ //TODO: Keep sorted by Id for faster binary search.
+ private static void AddRelicToPool(RelicTemplate relic)
+ {
+ if (relic.Rarity == Rarity.Breakfast)
+ return;
+ int indexRelic = _relicRarityPools[(int)relic.Rarity].IndexOf(relic.Id);
+ if (indexRelic == -1)
+ {
+ _relicRarityPools[(int)relic.Rarity].Add(relic.Id);
+ }
+ }
+
+ public static void RemoveRelicFromPool(RelicTemplate relic)
+ {
+ if (relic.Rarity == Rarity.Breakfast)
+ return;
+ int indexRelic = _relicRarityPools[(int)relic.Rarity].IndexOf(relic.Id);
+ if (indexRelic == -1)
+ {
+ GD.PushWarning(
+ "Attempting to remove relic " + relic.Id + " from the Relic Pool, not found!"
+ );
+ return;
+ }
+ _relicRarityPools[(int)relic.Rarity].RemoveAt(indexRelic);
+ }
+
+ ///
+ /// Return an array of relics for reward selection.
+ /// Intended usage of rarity. Player Stats has the rarity distribution. Do rolls in descending order of rarity.
+ /// Get a relic for the rolled rarity, continue for count.
+ /// If the relic pool is out of the rolled rarity, be nice to player and give them a relic of higher rarity.
+ /// Continue through ascending rarities until no new relics are acquirable, then give Breakfast.
+ ///
+ /// Number of relics to generate.
+ /// An offset for the loot rng seed.
+ /// An array of the int odds out of 100 for each typical rarity (Common through Legendary).
+ ///
+ public static RelicTemplate[] GetRandomRelics(int count, int lootOffset, int[] odds)
+ {
+ RelicTemplate[] result = new RelicTemplate[count];
RandomNumberGenerator lootRng = new RandomNumberGenerator();
- lootRng.SetSeed(StageProducer.GlobalRng.Seed + (ulong)StageProducer.CurRoom);
+ lootRng.SetSeed(StageProducer.GlobalRng.Seed + (ulong)lootOffset);
- availableRelics = availableRelics
- .OrderBy(_ => lootRng.Randi())
- .Take(count)
- .Select(r => r.Clone())
- .ToArray();
+ for (int i = 0; i < count; i++)
+ {
+ Rarity rarity = RollRarities(odds, lootRng);
+ RelicTemplate relic = H_GetRandomRelic(rarity, lootRng);
+ result[i] = relic;
+ }
+ //Re-add relics back to pools.
+ foreach (RelicTemplate relic in result)
+ {
+ _relicRarityPools[(int)relic.Rarity].Add(relic.Id);
+ }
+
+ return result;
+ }
- for (int i = availableRelics.Length; i < count; i++)
+ private static Rarity RollRarities(int[] rarityOdds, RandomNumberGenerator rng)
+ {
+ int rarityRoll = rng.RandiRange(1, 100);
+ for (int i = 0; i < rarityOdds.Length; i++)
{
- availableRelics = availableRelics.Append(RelicDictionary[0].Clone()).ToArray();
+ if (rarityRoll < rarityOdds[i])
+ {
+ return (Rarity)i;
+ }
+ }
+
+ return Rarity.Common;
+ }
+
+ private static RelicTemplate H_GetRandomRelic(Rarity startingRarity, RandomNumberGenerator rng)
+ {
+ int countOfRarity = 0;
+ Rarity currentRarity = startingRarity;
+
+ while (countOfRarity <= 0) //While there are no options of current rarity selected
+ {
+ countOfRarity = _relicRarityPools[(int)currentRarity].Count;
+
+ if (countOfRarity > 0) //There are relics of a rarity
+ { //Select a random relic of rarity.
+ int relicIndex = rng.RandiRange(0, countOfRarity - 1);
+ int selectedRelicId = _relicRarityPools[(int)currentRarity][relicIndex];
+ RelicTemplate result = RelicDictionary[selectedRelicId].Clone();
+ RemoveRelicFromPool(result); //Prevent same relic being selected in same selection process.
+ return result;
+ }
+
+ //Rotate through, in increasing rarity. Technically right now it will go Legendary -> Common before Uncommon, this is ok for now, but should be noted.
+ currentRarity = (Rarity)Mathf.PosMod((int)(currentRarity - 1), (int)Rarity.Breakfast);
+ if (currentRarity == startingRarity)
+ countOfRarity = 1; //Gone through all rarities, found no valid relic, exit loop to throw Breakfast.
}
- return availableRelics;
+ return RelicDictionary[0].Clone();
}
- public static Note[] GetRandomRewardNotes(int count)
+ public static Note[] GetRandomRewardNotes(int count, int lootOffset)
{
var availableNotes = Scribe
.NoteDictionary.Where(r => r.Name.Contains("Player")) //TODO: Classifications/pools
.ToArray();
RandomNumberGenerator lootRng = new RandomNumberGenerator();
- lootRng.SetSeed(StageProducer.GlobalRng.Seed + (ulong)StageProducer.CurRoom);
+ lootRng.SetSeed(StageProducer.GlobalRng.Seed + (ulong)lootOffset);
availableNotes = availableNotes
.OrderBy(_ => lootRng.Randi())
diff --git a/Globals/StageProducer.cs b/Globals/StageProducer.cs
index 64de9178..3c435ee2 100644
--- a/Globals/StageProducer.cs
+++ b/Globals/StageProducer.cs
@@ -13,10 +13,28 @@ public partial class StageProducer : Node
public static readonly RandomNumberGenerator GlobalRng = new();
- public static Vector2I MapSize { get; } = new(7, 6); //For now, make width an odd number
- public static MapGrid Map { get; } = new();
+ private static readonly MapGrid.MapConfig FirstMapConfig = new MapGrid.MapConfig(
+ 7,
+ 6,
+ 3,
+ [10, 1]
+ ).AddSetRoom(3, Stages.Chest);
+
+ private static readonly MapGrid.MapConfig TestMapConfig = new MapGrid.MapConfig(
+ 10,
+ 10,
+ 5,
+ [10, 2]
+ )
+ .AddSetRoom(3, Stages.Chest)
+ .AddSetRoom(6, Stages.Chest);
+
+ private static readonly MapGrid.MapConfig[] MapConfigs = new[] { FirstMapConfig };
+
+ public static MapGrid Map { get; private set; } = new();
private Stages _curStage = Stages.Title;
public static int CurRoom { get; private set; }
+ public static Area CurArea { get; private set; } = Area.Forest;
private Node _curScene;
private Node _preloadStage;
@@ -53,17 +71,19 @@ private void InitFromCfg()
private void GenerateMapConsistent()
{
GlobalRng.State = GlobalRng.Seed << 5 / 2; //Fudge seed state, to get consistent maps across new/loaded games
- Map.InitMapGrid(MapSize.X, MapSize.Y, 3);
+ Map.InitMapGrid(MapConfigs[(int)CurArea]);
}
private void StartNewGame()
{
GlobalRng.Randomize();
+ CurArea = Area.Forest;
GenerateMapConsistent();
PlayerStats = new PlayerStats();
CurRoom = Map.GetRooms()[0].Idx;
+ Scribe.InitRelicPools();
IsInitialized = true;
}
@@ -76,12 +96,15 @@ private bool LoadGame()
return false;
}
GlobalRng.Seed = sv.RngSeed;
+ CurArea = (Area)sv.Area;
GenerateMapConsistent();
GlobalRng.State = sv.RngState;
CurRoom = sv.LastRoomIdx;
+ Scribe.InitRelicPools();
+
PlayerStats = new PlayerStats();
- PlayerStats.CurNotes = Array.Empty();
+ PlayerStats.CurNotes = [];
foreach (int noteId in sv.NoteIds)
{
PlayerStats.AddNote(Scribe.NoteDictionary[noteId]);
@@ -109,10 +132,10 @@ public void TransitionFromRoom(int nextRoomIdx)
private Task _loadTask;
- /**
- * To be used from Cartographer. Preloads the scene during transition animation.
- * This removes the occasionally noticeable load time for the scene change.
- */
+ ///
+ /// To be used from Cartographer. Preloads the scene during transition animation. This removes the occasionally noticeable load time for the scene change.
+ ///
+ /// Index of the next room in the map to get the stage from.
public void PreloadScene(int nextRoomIdx)
{
Stages nextStage = Map.GetRooms()[nextRoomIdx].Type;
@@ -171,6 +194,10 @@ public void TransitionStage(Stages nextStage, int nextRoomIdx = -1)
case Stages.Quit:
GetTree().Quit();
return;
+ case Stages.Continue:
+ ProgressAreas();
+ GetTree().ChangeSceneToFile("res://Scenes/Maps/InBetween.tscn");
+ break;
default:
GD.PushError($"Error Scene Transition is {nextStage}");
break;
@@ -195,12 +222,12 @@ private BattleConfig MakeBattleConfig(Stages nextRoom, int nextRoomIdx)
switch (nextRoom)
{
case Stages.Battle:
- int songIdx = stageRng.RandiRange(1, 2);
+ int songIdx = stageRng.RandiRange(1, 3);
result.CurSong = Scribe.SongDictionary[songIdx];
result.EnemyScenePath = Scribe.SongDictionary[songIdx].EnemyScenePath;
break;
case Stages.Boss:
- result.EnemyScenePath = P_BossBlood.LoadPath;
+ result.EnemyScenePath = Scribe.SongDictionary[0].EnemyScenePath;
result.CurSong = Scribe.SongDictionary[0];
break;
case Stages.Chest:
@@ -212,4 +239,38 @@ private BattleConfig MakeBattleConfig(Stages nextRoom, int nextRoomIdx)
CurRoom = nextRoomIdx;
return result;
}
+
+ //Putting this here in an autoload.
+ public override void _Input(InputEvent @event)
+ {
+ //Consume controller input, if window out of focus.
+ //This handles ui_input, other scenes need to consume their own.
+ if (!GetWindow().HasFocus())
+ {
+ GetViewport().SetInputAsHandled();
+ return;
+ }
+ }
+
+ #region Area Management
+
+ ///
+ /// There should always be a mapconfig for each area. It's preferable to crash later if there isn't even a placeholder config.
+ ///
+ /// True if there is another area.
+ public static bool IsMoreAreas()
+ {
+ return (int)CurArea + 1 < MapConfigs.Length;
+ }
+
+ public void ProgressAreas()
+ {
+ CurArea += 1;
+
+ Map = new();
+ GenerateMapConsistent();
+ CurRoom = Map.GetRooms()[0].Idx;
+ }
+
+ #endregion
}
diff --git a/Globals/Translations/Translations.csv b/Globals/Translations/Translations.csv
index 36684621..6f9346d4 100644
--- a/Globals/Translations/Translations.csv
+++ b/Globals/Translations/Translations.csv
@@ -10,16 +10,21 @@ CONTROLS_TITLE_TYPE_QWER,QWERT,QWERT
CONTROLS_TITLE_TYPE_ARROW,Arrow,箭头键
CONTROLS_TITLE_SELECTED,Selected,已选择
CONTROLS_WASD_BUTTON,WASD,WASD
-CONTROLS_CONTROLLER_BUTTON,Controller,控制杆
+CONTROLS_CONTROLLER,Controller,控制杆
+CONTROLS_KEYBOARD,Keyboard,键盘
CONTROLS_QWER_BUTTON,QWER,QWER
CONTROLS_ARROW_BUTTON,Arrow Keys,箭头键
CONTROLS_RETURN_BUTTON,Return,返回
+CONTROLS_CHOOSE_SCHEME,Choose Control Scheme,选择控制方式
+CONTROLS_CHOOSE_BUTTON,Choose new button,选择输入按钮
ESCAPE_MENU_RESUME,Resume,继续
ESCAPE_MENU_QUIT,Quit,退出
ESCAPE_MENU_TITLE,Quit to Title,返回标题
-CHEST_ROOM_REWARDS,Rewards!,奖励!
+INBETWEEN_CONTINUE,Continue,继续
+CHEST_ROOM_REWARDS,Reward Selection!,奖励!
CHEST_ROOM_SKIP,Skip,跳过
CHEST_ROOM_ACCEPT,Accept,接受
+CHEST_ROOM_REROLL,Rerolls: ,重刷:
BATTLE_ROOM_BEGIN_BUTTON,"Begin Battle [Enter]","开始战斗 [Enter]"
BATTLE_ROOM_PERFECT,Perfect,精准
BATTLE_ROOM_GOOD,Good,良好
@@ -42,6 +47,14 @@ NOTE_PLAYERVAMPIRE_NAME,PlayerVampire,玩家吸血
NOTE_PLAYERVAMPIRE_TOOLTIP,"Steals health from enemy.","从敌人吸取生命值"
NOTE_PLAYERQUARTER_NAME,PlayerQuarter,玩家一分之一
NOTE_PLAYERQUARTER_TOOLTIP,"Basic note at a quarter of the cost.","以四分之一的耗费时间量发出基础音符"
+NOTE_PLAYERBLOCK_NAME,PlayerBlock,玩家格挡
+NOTE_PLAYERBLOCK_TOOLTIP,"Gives player one charge of block.","给予玩家一层格挡充能。"
+NOTE_PLAYEREXPLOSIVE_NAME,PlayerExplosive,玩家炸药
+NOTE_PLAYEREXPLOSIVE_TOOLTIP,"Deals damage to all enemies.","对所有敌人造成伤害"
+NOTE_PLAYERECHO_NAME,PlayerEcho,玩家回声
+NOTE_PLAYERECHO_TOOLTIP,"Deals more damage with each loop.","每次循环造成更多伤害"
+NOTE_PLAYERPOISON_NAME,PlayerPoison,玩家中毒
+NOTE_PLAYERPOISON_TOOLTIP,"Applies stacks of poison based on timing.","根据时机施加中毒层数"
RELIC_BREAKFAST_NAME,Breakfast,早餐
RELIC_BREAKFAST_TOOLTIP,"Increases max hp.",提高最大生命值
RELIC_GOODVIBES_NAME,Good Vibes,良好消息
@@ -50,6 +63,18 @@ RELIC_AUROBOROS_NAME,Auroboros,无尾蛇
RELIC_AUROBOROS_TOOLTIP,"Bigger number, better person. Increases combo multiplier every riff.","进一步增加综合倍数,每次现场提升"
RELIC_COLORBOROS_NAME,Colorboros,彩蛇轮回
RELIC_COLORBOROS_TOOLTIP,"Taste the rainbow. Charges the freestyle bar every riff.","品尝临岛,每次现场充值自由格条"
+RELIC_CHIPS_NAME,"Chips",薯片
+RELIC_CHIPS_TOOLTIP,"Hitting a note deals a bit of damage.","击中音符会造成少量伤害。"
+RELIC_PAPERCUT_NAME,Paper Cut,纸割伤
+RELIC_PAPERCUT_TOOLTIP,"Deals damage each riff.","每轮造成伤害"
+RELIC_ENERGYDRINK_NAME,Energy Drink,"能量饮料"
+RELIC_ENERGYDRINK_TOOLTIP,"Take a chance to cool down and sip an energy drink to increase your max energy bar.","碰碰运气,喝一口能量饮料来冷静下来并增加你的最大能量条。"
+RELIC_BANDAGE_NAME,Bandage,"绷带"
+RELIC_BANDAGE_TOOLTIP,"A clean strip of cloth. Use it after a fight to patch up and feel better.","一块干净的布条,战斗后使用来包扎并恢复一些健康。"
+RELIC_MEDKIT_NAME,Medkit,"急救包"
+RELIC_MEDKIT_TOOLTIP,"A small kit with medical supplies. Heals you a bit after each riff.","包含一些医疗用品的小包,每次循环后恢复少量生命。"
+RELIC_VINYLRECORD_NAME,Vinyl Record,"黑胶唱片"
+RELIC_VINYLRECORD_TOOLTIP,"Right round, right round. All riff effects trigger twice.","把我转起来,把我转起来。所有循环效果触发两次。"
INVENTORY_TAB_NOTES,Notes,乐谱
INVENTORY_TAB_RELICS,Relics,遗物
OPTIONS_VOLUME_LABEL,Master Volume,最终音量设置
diff --git a/README.md b/README.md
index 3ed2e824..1f0f4ecc 100644
--- a/README.md
+++ b/README.md
@@ -36,8 +36,9 @@ We now have a Steam page!
### Images
- **Input Buttons**: [inputKeys – Nicolae (Xelu) Berbece](https://thoseawesomeguys.com/prompts/)
-- **Title Screen Font**: [04B-30 – Yuji Oshimoto](http://04.jp.org/)
- **Light Map Texture**: [Godot Engine Docs – Godot Foundation](https://docs.godotengine.org/en/stable/tutorials/2d/2d_lights_and_shadows.html)
+- **Title Screen Font**: [04B-30 – Yuji Oshimoto](http://04.jp.org/)
+- **Main System Font**: [Fibberish - nathan scott](https://caffinate.itch.io/fibberish/)
---
@@ -46,3 +47,4 @@ We now have a Steam page!
The repository license **does not** apply to any music or image files in this repository, including but not limited to `.wav`, `.mp3`, `.ogg`, `.png`, `.jpg`, and other media formats. These assets are **not licensed** for use, private or public, without prior written consent from the team.
All source code and documentation files are licensed under the **GNU Affero General Public License v3.0 (AGPL-3.0)**.
+
diff --git a/Scenes/AreaBasedBackground.cs b/Scenes/AreaBasedBackground.cs
new file mode 100644
index 00000000..8a5275a2
--- /dev/null
+++ b/Scenes/AreaBasedBackground.cs
@@ -0,0 +1,16 @@
+using System;
+using FunkEngine;
+using Godot;
+
+public partial class AreaBasedBackground : TextureRect
+{
+ public override void _Ready()
+ {
+ Texture = StageProducer.CurArea switch
+ {
+ Area.Forest => GD.Load("res://SharedAssets/BackGround_Full.png"),
+ Area.City => GD.Load("res://icon.svg"),
+ _ => null,
+ };
+ }
+}
diff --git a/Scenes/AreaBasedBackground.cs.uid b/Scenes/AreaBasedBackground.cs.uid
new file mode 100644
index 00000000..6dd4683b
--- /dev/null
+++ b/Scenes/AreaBasedBackground.cs.uid
@@ -0,0 +1 @@
+uid://cp6t6haqyef7o
diff --git a/Scenes/BattleDirector/BattleScene.tscn b/Scenes/BattleDirector/BattleScene.tscn
index 6af6318e..647e287b 100644
--- a/Scenes/BattleDirector/BattleScene.tscn
+++ b/Scenes/BattleDirector/BattleScene.tscn
@@ -1,12 +1,14 @@
-[gd_scene load_steps=10 format=3 uid="uid://b0mrgr7h0ty1y"]
+[gd_scene load_steps=12 format=3 uid="uid://b0mrgr7h0ty1y"]
-[ext_resource type="Script" path="res://Scenes/BattleDirector/Scripts/BattleDirector.cs" id="1_jmdo1"]
-[ext_resource type="Script" path="res://Scenes/UI/Scripts/MenuModule.cs" id="2_ka0ws"]
-[ext_resource type="Script" path="res://Scenes/BattleDirector/Scripts/Conductor.cs" id="3_elcaj"]
+[ext_resource type="Script" uid="uid://bttu0wmy2fp64" path="res://Scenes/BattleDirector/Scripts/BattleDirector.cs" id="1_jmdo1"]
+[ext_resource type="Script" uid="uid://pl57giqyhckb" path="res://Scenes/UI/Scripts/MenuModule.cs" id="2_ka0ws"]
+[ext_resource type="Script" uid="uid://tg14hkh1n7iv" path="res://Scenes/BattleDirector/Scripts/Conductor.cs" id="3_elcaj"]
[ext_resource type="PackedScene" uid="uid://duhiilcv4tat3" path="res://Scenes/BattleDirector/NotePlacementBar.tscn" id="4_qk7om"]
[ext_resource type="PackedScene" uid="uid://dfevfib11kou1" path="res://Scenes/ChartViewport/ChartViewport.tscn" id="5_r2xh0"]
[ext_resource type="Texture2D" uid="uid://qhwve7fik4do" path="res://SharedAssets/BackGround_Full.png" id="6_0jtpx"]
+[ext_resource type="Script" uid="uid://cp6t6haqyef7o" path="res://Scenes/AreaBasedBackground.cs" id="7_6k2qj"]
[ext_resource type="Texture2D" uid="uid://dbjotl0v1ymia" path="res://SharedAssets/BattleFrame1.png" id="7_klvil"]
+[ext_resource type="Theme" uid="uid://d37e3tpsbxwak" path="res://Scenes/UI/Assets/GeneralTheme.tres" id="8_62qim"]
[sub_resource type="Gradient" id="Gradient_8uy3a"]
offsets = PackedFloat32Array(0, 0.766234, 1)
@@ -17,9 +19,10 @@ gradient = SubResource("Gradient_8uy3a")
fill_from = Vector2(1, 0)
fill_to = Vector2(0.738532, 1)
-[node name="ProtoBattleDirector" type="Node2D" node_paths=PackedStringArray("CD", "CM", "NPB", "Audio", "_focusedButton")]
+[node name="ProtoBattleDirector" type="Node2D" node_paths=PackedStringArray("PuppetMarkers", "CD", "CM", "NPB", "Audio", "_focusedButton")]
process_mode = 1
script = ExtResource("1_jmdo1")
+PuppetMarkers = [NodePath("PlayerMarker"), NodePath("Enemy1Marker"), NodePath("Enemy2Marker"), NodePath("Enemy3Marker")]
CD = NodePath("Conductor")
CM = NodePath("SubViewport")
NPB = NodePath("NotePlacementBar")
@@ -37,6 +40,18 @@ CurSceneNode = NodePath("..")
script = ExtResource("3_elcaj")
CM = NodePath("../SubViewport")
+[node name="PlayerMarker" type="Marker2D" parent="."]
+position = Vector2(158, 125)
+
+[node name="Enemy1Marker" type="Marker2D" parent="."]
+position = Vector2(325, 125)
+
+[node name="Enemy2Marker" type="Marker2D" parent="."]
+position = Vector2(450, 125)
+
+[node name="Enemy3Marker" type="Marker2D" parent="."]
+position = Vector2(575, 125)
+
[node name="NotePlacementBar" parent="." instance=ExtResource("4_qk7om")]
offset_top = 183.0
offset_bottom = 183.0
@@ -59,6 +74,7 @@ z_index = -1
offset_right = 640.0
offset_bottom = 178.0
texture = ExtResource("6_0jtpx")
+script = ExtResource("7_6k2qj")
[node name="BattleFrame" type="TextureRect" parent="."]
z_index = 1
@@ -68,9 +84,9 @@ offset_bottom = 360.0
texture = ExtResource("7_klvil")
[node name="StartButton" type="Button" parent="."]
-modulate = Color(5, 5, 5, 1)
offset_left = 241.0
-offset_top = 234.0
-offset_right = 399.0
-offset_bottom = 265.0
+offset_top = 230.0
+offset_right = 443.0
+offset_bottom = 267.0
+theme = ExtResource("8_62qim")
text = "BATTLE_ROOM_BEGIN_BUTTON"
diff --git a/Scenes/BattleDirector/NotePlacementBar.tscn b/Scenes/BattleDirector/NotePlacementBar.tscn
index 34f29a29..492ee90b 100644
--- a/Scenes/BattleDirector/NotePlacementBar.tscn
+++ b/Scenes/BattleDirector/NotePlacementBar.tscn
@@ -1,6 +1,6 @@
[gd_scene load_steps=12 format=3 uid="uid://duhiilcv4tat3"]
-[ext_resource type="Script" path="res://Scenes/BattleDirector/Scripts/NotePlacementBar.cs" id="1_456es"]
+[ext_resource type="Script" uid="uid://gj666xe815py" path="res://Scenes/BattleDirector/Scripts/NotePlacementBar.cs" id="1_456es"]
[ext_resource type="Texture2D" uid="uid://cnyr5usjdv0ni" path="res://Scenes/BattleDirector/Assets/NoteQueue_Frame.png" id="2_3tw16"]
[ext_resource type="Texture2D" uid="uid://c3chrsxrulapd" path="res://Classes/Notes/Assets/Note_PlayerBasic.png" id="3_6ylx6"]
[ext_resource type="Texture2D" uid="uid://caw70lr5e1yiq" path="res://Classes/Notes/Assets/Note_PlayerDouble.png" id="4_6w8ha"]
@@ -14,14 +14,23 @@ width = 34
height = 100
[sub_resource type="Gradient" id="Gradient_xvck1"]
-offsets = PackedFloat32Array(0, 0.373239, 0.690141, 1)
-colors = PackedColorArray(0, 1, 0, 1, 1, 0, 0.4, 1, 0, 1, 0.95, 1, 1, 0, 0, 1)
+offsets = PackedFloat32Array(0)
+colors = PackedColorArray(0, 0, 0, 1)
[sub_resource type="GradientTexture2D" id="GradientTexture2D_0bqho"]
gradient = SubResource("Gradient_xvck1")
width = 32
height = 98
-fill_to = Vector2(0, 1)
+fill_from = Vector2(0, 1)
+fill_to = Vector2(0, 0)
+
+[sub_resource type="Gradient" id="Gradient_2uknl"]
+offsets = PackedFloat32Array(0)
+colors = PackedColorArray(0.46, 0.2162, 0.39905, 1)
+
+[sub_resource type="GradientTexture1D" id="GradientTexture1D_d62c7"]
+gradient = SubResource("Gradient_2uknl")
+width = 1
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_fy2uu"]
lifetime_randomness = 0.32
@@ -41,14 +50,6 @@ turbulence_noise_scale = 5.0
turbulence_influence_min = 0.0
turbulence_influence_max = 0.018
-[sub_resource type="Gradient" id="Gradient_2uknl"]
-offsets = PackedFloat32Array(0)
-colors = PackedColorArray(0.46, 0.2162, 0.39905, 1)
-
-[sub_resource type="GradientTexture1D" id="GradientTexture1D_d62c7"]
-gradient = SubResource("Gradient_2uknl")
-width = 1
-
[node name="NotePlacementBar" type="Control" node_paths=PackedStringArray("_notePlacementBar", "_particles", "_fullBarParticles", "_currentComboMultText", "_currentNote", "_nextNote")]
layout_mode = 3
anchors_preset = 15
@@ -80,12 +81,12 @@ z_index = 1
position = Vector2(-1, -32)
emitting = false
amount = 22
-process_material = SubResource("ParticleProcessMaterial_fy2uu")
texture = SubResource("GradientTexture1D_d62c7")
lifetime = 2.0
preprocess = 0.1
explosiveness = 0.3
randomness = 0.05
+process_material = SubResource("ParticleProcessMaterial_fy2uu")
[node name="FullBarParticles" type="CPUParticles2D" parent="PlacementBar"]
position = Vector2(14, 98)
@@ -107,7 +108,8 @@ layout_mode = 0
offset_right = 80.0
offset_bottom = 35.0
mouse_filter = 2
-text = " x1"
+theme_override_font_sizes/font_size = 32
+text = " x1"
context_menu_enabled = false
shortcut_keys_enabled = false
selecting_enabled = false
diff --git a/Scenes/BattleDirector/NotePoofParticles.tscn b/Scenes/BattleDirector/NotePoofParticles.tscn
index 7f5970d9..d2384f7c 100644
--- a/Scenes/BattleDirector/NotePoofParticles.tscn
+++ b/Scenes/BattleDirector/NotePoofParticles.tscn
@@ -25,9 +25,9 @@ scale_curve = SubResource("CurveTexture_h7u0t")
[node name="NotePoof" type="GPUParticles2D"]
emitting = false
amount = 10
-process_material = SubResource("ParticleProcessMaterial_k0qar")
texture = ExtResource("1_hjrch")
lifetime = 0.75
one_shot = true
preprocess = 0.1
explosiveness = 1.0
+process_material = SubResource("ParticleProcessMaterial_k0qar")
diff --git a/Scenes/BattleDirector/Scripts/BattleDirector.cs b/Scenes/BattleDirector/Scripts/BattleDirector.cs
index 329f7d13..b776ca33 100644
--- a/Scenes/BattleDirector/Scripts/BattleDirector.cs
+++ b/Scenes/BattleDirector/Scripts/BattleDirector.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using FunkEngine;
using Godot;
@@ -11,7 +12,10 @@ public partial class BattleDirector : Node2D
public static readonly string LoadPath = "res://Scenes/BattleDirector/BattleScene.tscn";
public PlayerPuppet Player;
- public EnemyPuppet Enemy;
+ private EnemyPuppet[] _enemies;
+
+ [Export]
+ public Marker2D[] PuppetMarkers = new Marker2D[4]; //[0] is always player
[Export]
private Conductor CD;
@@ -34,30 +38,6 @@ public partial class BattleDirector : Node2D
#endregion
- #region Note Handling
- private bool PlayerAddNote(ArrowType type, Beat beat)
- {
- if (!NPB.CanPlaceNote())
- return false;
-
- Note noteToPlace = NPB.NotePlaced();
- noteToPlace.OnHit(this, Timing.Okay);
- CD.AddPlayerNote(noteToPlace, type, beat);
- NotePlaced?.Invoke(this);
- return true;
- }
-
- public PuppetTemplate GetTarget(Note note)
- {
- if (note.Owner == Player)
- {
- return Enemy;
- }
-
- return Player;
- }
- #endregion
-
#region Initialization
private void SyncStartWithMix()
{
@@ -84,6 +64,7 @@ public override void _Ready()
}
TimeKeeper.InitVals(curSong.Bpm);
+ Harbinger.Init(this);
InitPlayer();
InitEnemies();
CD.Initialize(curSong);
@@ -96,7 +77,7 @@ public override void _Ready()
private void InitPlayer()
{
Player = GD.Load(PlayerPuppet.LoadPath).Instantiate();
- AddChild(Player);
+ PuppetMarkers[0].AddChild(Player);
Player.Defeated += CheckBattleStatus;
EventizeRelics();
NPB.Setup(StageProducer.PlayerStats);
@@ -105,11 +86,19 @@ private void InitPlayer()
private void InitEnemies()
{
//TODO: Refine
- Enemy = GD.Load(StageProducer.Config.EnemyScenePath)
- .Instantiate();
- AddChild(Enemy);
- Enemy.Defeated += CheckBattleStatus;
- AddEnemyEffects();
+ _enemies = new EnemyPuppet[StageProducer.Config.EnemyScenePath.Length];
+ for (int i = 0; i < StageProducer.Config.EnemyScenePath.Length; i++)
+ {
+ EnemyPuppet enemy = GD.Load(StageProducer.Config.EnemyScenePath[0])
+ .Instantiate();
+ if (_enemies.Length == 1)
+ PuppetMarkers[2].AddChild(enemy);
+ else
+ PuppetMarkers[i + 1].AddChild(enemy);
+ enemy.Defeated += CheckBattleStatus;
+ _enemies[i] = enemy;
+ AddEnemyEffects(enemy);
+ }
}
public override void _Process(double delta)
@@ -128,7 +117,7 @@ private void UpdateBeat(Beat beat)
}
if (beat.Loop > TimeKeeper.LastBeat.Loop)
{
- ChartLooped?.Invoke(this);
+ Harbinger.Instance.InvokeChartLoop(beat.Loop, false);
}
TimeKeeper.LastBeat = beat;
}
@@ -143,9 +132,28 @@ public override void _UnhandledInput(InputEvent @event)
{
DebugKillEnemy();
}
+
+ if (eventKey.Keycode == Key.Key9)
+ {
+ DebugRefillEnergy();
+ }
}
}
+ private bool PlayerAddNote(ArrowType type, Beat beat)
+ {
+ if (!NPB.CanPlaceNote())
+ return false;
+
+ Note noteToPlace = NPB.NotePlaced();
+ noteToPlace.OnHit(this, Timing.Okay);
+
+ CD.AddPlayerNote(noteToPlace, type, beat);
+ Harbinger.Instance.InvokeNotePlaced(new ArrowData(type, beat, noteToPlace));
+ Harbinger.Instance.InvokeNoteHit(noteToPlace, Timing.Okay); //TODO: test how this feels? maybe take it out later
+ return true;
+ }
+
//Only called from CD signal when a note is processed
private void OnTimedInput(ArrowData data, double beatDif)
{
@@ -164,15 +172,16 @@ private void OnTimedInput(ArrowData data, double beatDif)
Timing timed = CheckTiming(beatDif);
data.NoteRef.OnHit(this, timed);
- NPB.HandleTiming(timed);
+ Harbinger.Instance.InvokeNoteHit(data.NoteRef, timed);
+ NPB.HandleTiming(timed, data.Type);
CM.ComboText(timed, data.Type, NPB.GetCurrentCombo());
}
private void ForceMiss(ArrowType type)
{
- NPB.HandleTiming(Timing.Miss);
+ NPB.HandleTiming(Timing.Miss, type);
CM.ComboText(Timing.Miss, type, NPB.GetCurrentCombo());
- Player.TakeDamage(4);
+ Player.TakeDamage(new DamageInstance(4, null, Player));
}
private Timing CheckTiming(double beatDif)
@@ -204,10 +213,15 @@ private void CheckBattleStatus(PuppetTemplate puppet) //Called when a puppet die
OnBattleLost();
return;
}
- if (puppet == Enemy)
+ if (puppet is EnemyPuppet && IsBattleWon())
OnBattleWon(); //will have to adjust this to account for when we have multiple enemies at once
}
+ private bool IsBattleWon()
+ {
+ return GetFirstEnemy() == null;
+ }
+
private void OnBattleWon()
{
Audio.StreamPaused = true;
@@ -220,7 +234,7 @@ private void OnBattleLost()
Audio.StreamPaused = true;
SaveSystem.ClearSave();
AddChild(GD.Load(EndScreen.LoadPath).Instantiate());
- GetTree().Paused = true;
+ ProcessMode = ProcessModeEnum.Disabled;
}
private void ShowRewardSelection(int amount)
@@ -241,30 +255,126 @@ private void TransitionOutOfBattle()
}
#endregion
- #region BattleEffect Handling
+ #region Battles
+ public void DealDamage(Note note, int damage, PuppetTemplate source)
+ {
+ PuppetTemplate[] targets = GetTargets(note.TargetType);
+ foreach (PuppetTemplate target in targets)
+ {
+ target.TakeDamage(new DamageInstance(damage, source, target));
+ }
+ }
- private delegate void NotePlacedHandler(BattleDirector BD);
- private event NotePlacedHandler NotePlaced;
+ public void DealDamage(
+ Targetting targetting,
+ int damage,
+ PuppetTemplate source,
+ bool targetPlayer = false
+ )
+ {
+ PuppetTemplate[] targets = GetTargets(targetting);
+ foreach (PuppetTemplate target in targets)
+ {
+ target.TakeDamage(new DamageInstance(damage, source, target));
+ }
+ }
- private delegate void ChartLoopHandler(BattleDirector BD);
- private event ChartLoopHandler ChartLooped;
+ public void AddStatus(Targetting targetting, StatusEffect status)
+ {
+ PuppetTemplate[] targets = GetTargets(targetting);
+ foreach (PuppetTemplate target in targets)
+ {
+ target.AddStatusEffect(status);
+ }
+
+ status.StatusEnd += RemoveStatus;
+ AddEvent(status);
+ }
+
+ public void RemoveStatus(StatusEffect status)
+ {
+ status.Sufferer.RemoveStatusEffect(status);
+ status.StatusEnd -= RemoveStatus;
+ RemoveEvent(status);
+ }
+
+ private PuppetTemplate[] GetTargets(Targetting targetting)
+ {
+ switch (targetting)
+ {
+ case Targetting.Player:
+ return [Player];
+ case Targetting.First:
+ if (GetFirstEnemy() != null)
+ return [GetFirstEnemy()];
+ return [];
+ case Targetting.All:
+ return _enemies.Where(x => x.GetCurrentHealth() > 0).ToArray();
+ default:
+ return null;
+ }
+ }
+
+ private PuppetTemplate GetFirstEnemy()
+ {
+ foreach (var enemy in _enemies)
+ {
+ if (enemy.GetCurrentHealth() > 0)
+ return enemy;
+ }
+ return null;
+ }
+ #endregion
+
+ #region BattleEffect Handling
private void AddEvent(IBattleEvent bEvent)
{
- switch (bEvent.GetTrigger()) //TODO: Look into a way to get eventhandler from string
+ switch (bEvent.GetTrigger())
{
case BattleEffectTrigger.NotePlaced:
- NotePlaced += bEvent.OnTrigger;
+ Harbinger.Instance.NotePlaced += bEvent.OnTrigger;
break;
case BattleEffectTrigger.OnLoop:
- ChartLooped += bEvent.OnTrigger;
+ Harbinger.Instance.ChartLooped += bEvent.OnTrigger;
+ break;
+ case BattleEffectTrigger.NoteHit:
+ Harbinger.Instance.NoteHit += bEvent.OnTrigger;
+ break;
+ case BattleEffectTrigger.OnBattleEnd:
+ Harbinger.Instance.BattleEnded += bEvent.OnTrigger;
+ break;
+ case BattleEffectTrigger.OnDamageInstance:
+ Harbinger.Instance.OnDamageInstance += bEvent.OnTrigger;
break;
}
}
- private void AddEnemyEffects()
+ private void RemoveEvent(IBattleEvent bEvent)
{
- foreach (var effect in Enemy.GetBattleEvents())
+ switch (bEvent.GetTrigger())
+ {
+ case BattleEffectTrigger.NotePlaced:
+ Harbinger.Instance.NotePlaced -= bEvent.OnTrigger;
+ break;
+ case BattleEffectTrigger.OnLoop:
+ Harbinger.Instance.ChartLooped -= bEvent.OnTrigger;
+ break;
+ case BattleEffectTrigger.NoteHit:
+ Harbinger.Instance.NoteHit -= bEvent.OnTrigger;
+ break;
+ case BattleEffectTrigger.OnBattleEnd:
+ Harbinger.Instance.BattleEnded -= bEvent.OnTrigger;
+ break;
+ case BattleEffectTrigger.OnDamageInstance:
+ Harbinger.Instance.OnDamageInstance -= bEvent.OnTrigger;
+ break;
+ }
+ }
+
+ private void AddEnemyEffects(EnemyPuppet enemy)
+ {
+ foreach (var effect in enemy.GetBattleEvents())
{
AddEvent(effect);
}
@@ -293,8 +403,121 @@ private void CleanUpRelics()
}
#endregion
+ public partial class Harbinger : Resource
+ {
+ private static Harbinger _instance;
+ public static Harbinger Instance => _instance;
+
+ private BattleDirector _curDirector;
+
+ static Harbinger() { }
+
+ private Harbinger(BattleDirector BD)
+ {
+ _curDirector = BD;
+ }
+
+ internal static void Init(BattleDirector BD)
+ {
+ _instance = new Harbinger(BD);
+ }
+
+ ///
+ /// Event Args to handle event types triggering from the action of a note, without timing.
+ ///
+ /// The BattleDirector calling the event.
+ /// The note data of the passing note.
+ public class NoteEventArgs(BattleDirector bd, ArrowData data) : BattleEventArgs(bd)
+ {
+ public ArrowData Data = data;
+ }
+
+ ///
+ /// Event Args to handle event types triggering from the start of a new loop.
+ ///
+ /// The BattleDirector calling the event.
+ /// The loop starting.
+ public class LoopEventArgs(BattleDirector bd, int incomingLoop, bool artificialLoop = true)
+ : BattleEventArgs(bd)
+ {
+ public int Loop = incomingLoop;
+ public bool ArtificialLoop = artificialLoop;
+ }
+
+ ///
+ /// Event Args to handle notes being hit
+ ///
+ /// The BattleDirector calling the event.
+ /// The Note being hit.
+ public class NoteHitArgs(BattleDirector bd, Note note, Timing timing) : BattleEventArgs(bd)
+ {
+ public Note Note = note;
+ public Timing Timing = timing;
+ }
+
+ internal delegate void NotePlacedHandler(BattleEventArgs e);
+ internal event NotePlacedHandler NotePlaced;
+
+ public void InvokeNotePlaced(ArrowData data)
+ {
+ NotePlaced?.Invoke(new NoteEventArgs(_curDirector, data));
+ }
+
+ internal delegate void ChartLoopHandler(BattleEventArgs e);
+ internal event ChartLoopHandler ChartLooped;
+
+ public void InvokeChartLoop(int incLoop, bool artificialLoop = true)
+ {
+ ChartLooped?.Invoke(new LoopEventArgs(_curDirector, incLoop, artificialLoop));
+ }
+
+ internal delegate void NoteHitHandler(BattleEventArgs e);
+ internal event NoteHitHandler NoteHit;
+
+ public void InvokeNoteHit(Note note, Timing timing)
+ {
+ NoteHit?.Invoke(new NoteHitArgs(_curDirector, note, timing));
+ }
+
+ internal delegate void BattleEndedHandler(BattleEventArgs e);
+ internal event BattleEndedHandler BattleEnded;
+
+ public void InvokeBattleEnded()
+ {
+ BattleEnded?.Invoke(new BattleEventArgs(_curDirector));
+ }
+
+ ///
+ /// Event Args to handle a damage instance being dealt. Happens before taking damage.
+ /// This allows damage to be intercepted, to be reduced/increased, to counter, or heal based on incoming damage.
+ ///
+ /// The BattleDirector calling the event.
+ /// The damage instance being thrown.
+ public class OnDamageInstanceArgs(BattleDirector bd, DamageInstance dmg)
+ : BattleEventArgs(bd)
+ {
+ public DamageInstance Dmg = dmg;
+ }
+
+ internal delegate void OnDamageInstanceHandler(OnDamageInstanceArgs e);
+ internal event OnDamageInstanceHandler OnDamageInstance;
+
+ public void InvokeOnDamageInstance(DamageInstance dmg)
+ {
+ OnDamageInstance?.Invoke(new OnDamageInstanceArgs(_curDirector, dmg));
+ }
+ }
+
private void DebugKillEnemy()
{
- //Enemy.TakeDamage(1000);
+ foreach (EnemyPuppet enemy in _enemies)
+ {
+ enemy.TakeDamage(new DamageInstance(1000, null, enemy));
+ }
+ }
+
+ private void DebugRefillEnergy()
+ {
+ NPB.IncreaseCharge(100);
}
}
diff --git a/Scenes/BattleDirector/Scripts/Conductor.cs b/Scenes/BattleDirector/Scripts/Conductor.cs
index 0aa92719..146bba7a 100644
--- a/Scenes/BattleDirector/Scripts/Conductor.cs
+++ b/Scenes/BattleDirector/Scripts/Conductor.cs
@@ -23,7 +23,7 @@ public void Initialize(SongData curSong)
if (_initialized)
return;
- MM = new MidiMaestro(StageProducer.Config.CurSong.MIDILocation);
+ MM = new MidiMaestro(StageProducer.Config.CurSong.SongMapLocation);
CM.ArrowFromInput += ReceiveNoteInput;
CM.Initialize(curSong);
@@ -41,14 +41,9 @@ private void AddInitialNotes()
{
foreach (ArrowType type in Enum.GetValues(typeof(ArrowType)))
{
- foreach (MidiNoteInfo mNote in MM.GetNotes(type))
+ foreach (NoteInfo Note in MM.GetNotes(type))
{
- AddNoteData(
- Scribe.NoteDictionary[0],
- type,
- new Beat((int)mNote.GetStartTimeBeat()),
- mNote.GetDurationBeats()
- );
+ AddNoteData(Scribe.NoteDictionary[0], type, new Beat((int)Note.Beat), Note.Length);
}
}
SpawnInitialNotes();
diff --git a/Scenes/BattleDirector/Scripts/NotePlacementBar.cs b/Scenes/BattleDirector/Scripts/NotePlacementBar.cs
index f9fdca1f..00d2952a 100644
--- a/Scenes/BattleDirector/Scripts/NotePlacementBar.cs
+++ b/Scenes/BattleDirector/Scripts/NotePlacementBar.cs
@@ -28,6 +28,7 @@ private double CurrentBarValue
[Export]
private TextureProgressBar _notePlacementBar;
+ private Gradient _gradTex;
[Export]
private GpuParticles2D _particles;
@@ -42,23 +43,28 @@ private double CurrentBarValue
private TextEdit _currentComboMultText;
private int _currentCombo;
- private int ComboMult => _currentCombo / _notesToIncreaseCombo + 1 + _bonusMult;
+ private int MaxComboMult;
+ private int ComboMult =>
+ Math.Min(_currentCombo / _notesToIncreaseCombo + 1 + _bonusMult, MaxComboMult);
private int _bonusMult;
private int _notesToIncreaseCombo;
private void UpdateComboMultText()
{
- _currentComboMultText.Text = $" x{ComboMult.ToString()}";
+ _currentComboMultText.Text = $" x{ComboMult.ToString()}";
}
#endregion
#region Initialization
public override void _Ready()
{
- MaxValue = 80;
+ MaxValue = 60;
_notesToIncreaseCombo = 4;
_barInitPosition = _notePlacementBar.Position;
+
+ if (_notePlacementBar.TextureProgress is GradientTexture2D gradientTexture)
+ _gradTex = gradientTexture.Gradient;
}
public override void _Process(double delta)
@@ -77,6 +83,9 @@ public void Setup(PlayerStats playerStats)
}
ShuffleNoteQueue();
ProgressQueue();
+ MaxValue = playerStats.MaxComboBar;
+ MaxComboMult = playerStats.MaxComboMult;
+ _notesToIncreaseCombo = playerStats.NotesToIncreaseCombo;
}
#endregion
@@ -151,12 +160,42 @@ private Note GetNote(bool getNextNote = false)
}
#endregion
+ #region Helpers
+ private Color[] _fillColors = [Colors.Green, Colors.Aqua, Colors.Pink, Colors.Red];
+
+ private void FillWithColor(ArrowType type, Color overrideCol = default)
+ {
+ // ReSharper disable once CompareOfFloatsByEqualityOperator
+ if (CurrentBarValue == MaxValue)
+ return;
+ Color color = overrideCol == default ? _fillColors[(int)type] : overrideCol;
+
+ if (_gradTex.GetPointCount() == 1)
+ _gradTex.SetColor(0, color);
+
+ float offset = (float)((CurrentBarValue) / MaxValue);
+ _gradTex.AddPoint(offset, color);
+ }
+
+ private void ClearColors()
+ {
+ for (int i = _gradTex.GetPointCount() - 1; i > 0; i--)
+ _gradTex.RemovePoint(i);
+ }
+ #endregion
+
#region Public Functions
public int GetCurrentCombo()
{
return _currentCombo;
}
+ public void ResetCurrentCombo()
+ {
+ _currentCombo = 0;
+ _bonusMult = 0;
+ }
+
//For external events to directly change mult
public void IncreaseBonusMult(int amount = 1)
{
@@ -166,6 +205,7 @@ public void IncreaseBonusMult(int amount = 1)
public void IncreaseCharge(int amount = 1)
{
+ FillWithColor(default, Colors.DarkViolet);
CurrentBarValue += amount;
}
@@ -179,12 +219,16 @@ public Note NotePlaced()
{
if (!CanPlaceNote())
GD.PushWarning("Note is attempting placement without a full bar!");
- Note placedNote = GetNote(Input.IsActionPressed("Secondary"));
+ string inputType = SaveSystem
+ .GetConfigValue(SaveSystem.ConfigSettings.InputType)
+ .ToString();
+ Note placedNote = GetNote(Input.IsActionPressed(inputType + "_secondaryPlacement"));
CurrentBarValue -= placedNote.CostModifier * MaxValue;
+ ClearColors();
return placedNote;
}
- public void HandleTiming(Timing timed)
+ public void HandleTiming(Timing timed, ArrowType type)
{
if (timed == Timing.Miss)
{
@@ -192,25 +236,37 @@ public void HandleTiming(Timing timed)
}
else
{
- HitNote();
+ HitNote(type);
}
}
// Hitting a note increases combo, combo mult, and note placement bar
- private void HitNote()
+ private void HitNote(ArrowType type)
{
_currentCombo++;
+ FillWithColor(type);
CurrentBarValue += ComboMult;
UpdateComboMultText();
}
+ public bool IgnoreMiss; //a one time safe miss
+
// Missing a note resets combo
private void MissNote()
{
- _currentCombo = 0;
- _bonusMult = 0;
+ if (IgnoreMiss)
+ {
+ IgnoreMiss = false;
+ return;
+ }
+ ResetCurrentCombo();
UpdateComboMultText();
}
+
+ public void SetIgnoreMiss(bool ignore)
+ {
+ IgnoreMiss = ignore;
+ }
#endregion
#region Shake
diff --git a/Scenes/ChartViewport/ChartViewport.tscn b/Scenes/ChartViewport/ChartViewport.tscn
index db3c8af3..f65b608e 100644
--- a/Scenes/ChartViewport/ChartViewport.tscn
+++ b/Scenes/ChartViewport/ChartViewport.tscn
@@ -1,10 +1,10 @@
[gd_scene load_steps=7 format=3 uid="uid://dfevfib11kou1"]
-[ext_resource type="Script" path="res://Scenes/ChartViewport/Scripts/ChartManager.cs" id="1_ruh2l"]
+[ext_resource type="Script" uid="uid://btgmfxqoe2wqx" path="res://Scenes/ChartViewport/Scripts/ChartManager.cs" id="1_ruh2l"]
[ext_resource type="Texture2D" uid="uid://cp78odda2doab" path="res://Scenes/ChartViewport/LoopMarker.png" id="2_q5cjc"]
-[ext_resource type="Script" path="res://Scenes/ChartViewport/Scripts/Loopable.cs" id="3_5u57h"]
+[ext_resource type="Script" uid="uid://cf58ep1c2o0q2" path="res://Scenes/ChartViewport/Scripts/Loopable.cs" id="3_5u57h"]
[ext_resource type="PackedScene" uid="uid://bn8txx53xlguw" path="res://Scenes/NoteManager/NoteManager.tscn" id="4_fd5fw"]
-[ext_resource type="Shader" path="res://SharedAssets/StarryNight.gdshader" id="5_kqrxg"]
+[ext_resource type="Shader" uid="uid://dp36iuuy414k1" path="res://SharedAssets/StarryNight.gdshader" id="5_kqrxg"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_5uw0y"]
shader = ExtResource("5_kqrxg")
diff --git a/Scenes/ChartViewport/HitParticles.tscn b/Scenes/ChartViewport/HitParticles.tscn
index 1a9315fe..0e30982d 100644
--- a/Scenes/ChartViewport/HitParticles.tscn
+++ b/Scenes/ChartViewport/HitParticles.tscn
@@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://bcf6vs4aqoxr5"]
-[ext_resource type="Script" path="res://Scenes/ChartViewport/Scripts/HitParticles.cs" id="1_7gkj5"]
+[ext_resource type="Script" uid="uid://bjj3132iu8lrc" path="res://Scenes/ChartViewport/Scripts/HitParticles.cs" id="1_7gkj5"]
[node name="HitParticles" type="CPUParticles2D"]
z_index = -1
diff --git a/Scenes/ChestScene/ChestScene.cs b/Scenes/ChestScene/ChestScene.cs
index 96e1b977..8d32acbf 100644
--- a/Scenes/ChestScene/ChestScene.cs
+++ b/Scenes/ChestScene/ChestScene.cs
@@ -12,10 +12,13 @@ public partial class ChestScene : Node2D
[Export]
public Button ChestButton;
+ [Export]
+ public Marker2D PlayerMarker;
+
public override void _Ready()
{
_player = GD.Load(PlayerPuppet.LoadPath).Instantiate();
- AddChild(_player);
+ PlayerMarker.AddChild(_player);
ChestButton.Pressed += GetLoot;
}
diff --git a/Scenes/ChestScene/ChestScene.tscn b/Scenes/ChestScene/ChestScene.tscn
index d81450a4..4ce4a918 100644
--- a/Scenes/ChestScene/ChestScene.tscn
+++ b/Scenes/ChestScene/ChestScene.tscn
@@ -1,11 +1,13 @@
-[gd_scene load_steps=9 format=3 uid="uid://c4vmb783d3v03"]
+[gd_scene load_steps=11 format=3 uid="uid://c4vmb783d3v03"]
-[ext_resource type="Script" path="res://Scenes/ChestScene/ChestScene.cs" id="1_ardd2"]
+[ext_resource type="Script" uid="uid://cetn71kolbrmg" path="res://Scenes/ChestScene/ChestScene.cs" id="1_ardd2"]
[ext_resource type="AudioStream" uid="uid://be5ial13ynf3o" path="res://Audio/Song1.ogg" id="2_x78jo"]
-[ext_resource type="Script" path="res://Scenes/UI/Scripts/MenuModule.cs" id="3_5uvci"]
-[ext_resource type="Shader" path="res://SharedAssets/StarryNight.gdshader" id="5_whthd"]
+[ext_resource type="Script" uid="uid://pl57giqyhckb" path="res://Scenes/UI/Scripts/MenuModule.cs" id="3_5uvci"]
+[ext_resource type="Script" uid="uid://cp6t6haqyef7o" path="res://Scenes/AreaBasedBackground.cs" id="5_u0wcg"]
+[ext_resource type="Shader" uid="uid://dp36iuuy414k1" path="res://SharedAssets/StarryNight.gdshader" id="5_whthd"]
[ext_resource type="Texture2D" uid="uid://qhwve7fik4do" path="res://SharedAssets/BackGround_Full.png" id="6_37nar"]
[ext_resource type="Texture2D" uid="uid://d0ywqw1j1k71v" path="res://Scenes/ChestScene/Assets/Chest.png" id="6_58hf4"]
+[ext_resource type="Theme" uid="uid://bcejp4llrb3m0" path="res://Scenes/UI/Assets/EmptyButton.tres" id="6_pisq4"]
[ext_resource type="Texture2D" uid="uid://dbjotl0v1ymia" path="res://SharedAssets/BattleFrame1.png" id="7_kkck7"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_8x17a"]
@@ -15,10 +17,11 @@ shader_parameter/bg_bottom_color = Vector4(0.028, 0.008, 0.184, 0)
shader_parameter/gradient_ratio = 1.0
shader_parameter/time_scale = 1.0
-[node name="ChestScene" type="Node2D" node_paths=PackedStringArray("ChestButton")]
+[node name="ChestScene" type="Node2D" node_paths=PackedStringArray("ChestButton", "PlayerMarker")]
process_mode = 1
script = ExtResource("1_ardd2")
-ChestButton = NodePath("ChestButton")
+ChestButton = NodePath("CenterContainer/VBoxContainer/ChestButton")
+PlayerMarker = NodePath("PlayerMarker")
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
stream = ExtResource("2_x78jo")
@@ -28,11 +31,15 @@ autoplay = true
script = ExtResource("3_5uvci")
CurSceneNode = NodePath("..")
+[node name="PlayerMarker" type="Marker2D" parent="."]
+position = Vector2(158, 125)
+
[node name="BackGround" type="TextureRect" parent="."]
z_index = -1
offset_right = 640.0
offset_bottom = 178.0
texture = ExtResource("6_37nar")
+script = ExtResource("5_u0wcg")
[node name="BattleFrame" type="TextureRect" parent="."]
z_index = 1
@@ -41,12 +48,34 @@ offset_right = 640.0
offset_bottom = 360.0
texture = ExtResource("7_kkck7")
-[node name="ChestButton" type="Button" parent="."]
-offset_left = 375.0
-offset_top = 126.0
-offset_right = 431.0
+[node name="CenterContainer" type="CenterContainer" parent="."]
+offset_left = 348.0
+offset_top = 88.0
+offset_right = 424.0
offset_bottom = 166.0
+
+[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"]
+layout_mode = 2
+
+[node name="OpenLabel" type="Label" parent="CenterContainer/VBoxContainer"]
+layout_mode = 2
+theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
+theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
+theme_override_constants/shadow_offset_x = 2
+theme_override_constants/shadow_offset_y = 3
+theme_override_constants/outline_size = 2
+theme_override_constants/shadow_outline_size = 6
+theme_override_font_sizes/font_size = 48
+text = "!"
+horizontal_alignment = 1
+vertical_alignment = 1
+
+[node name="ChestButton" type="Button" parent="CenterContainer/VBoxContainer"]
+custom_minimum_size = Vector2(60, 44)
+layout_mode = 2
+theme = ExtResource("6_pisq4")
icon = ExtResource("6_58hf4")
+icon_alignment = 1
[node name="SubViewportContainer" type="SubViewportContainer" parent="."]
z_index = -2
diff --git a/Scenes/Maps/Cartographer.tscn b/Scenes/Maps/Cartographer.tscn
index 204a2880..157ca73d 100644
--- a/Scenes/Maps/Cartographer.tscn
+++ b/Scenes/Maps/Cartographer.tscn
@@ -1,24 +1,37 @@
-[gd_scene load_steps=5 format=3 uid="uid://cydmo2lbnj1de"]
+[gd_scene load_steps=7 format=3 uid="uid://cydmo2lbnj1de"]
-[ext_resource type="Script" path="res://Scenes/Maps/Scripts/Cartographer.cs" id="1_u4q3n"]
+[ext_resource type="Script" uid="uid://dtlpiwt4n3pta" path="res://Scenes/Maps/Scripts/Cartographer.cs" id="1_u4q3n"]
[ext_resource type="Texture2D" uid="uid://qhwve7fik4do" path="res://SharedAssets/BackGround_Full.png" id="2_5g6at"]
-[ext_resource type="Script" path="res://Scenes/UI/Scripts/MenuModule.cs" id="2_cl7v0"]
+[ext_resource type="Script" uid="uid://pl57giqyhckb" path="res://Scenes/UI/Scripts/MenuModule.cs" id="2_cl7v0"]
+[ext_resource type="Theme" uid="uid://bcejp4llrb3m0" path="res://Scenes/UI/Assets/EmptyButton.tres" id="2_rnj1y"]
[ext_resource type="Texture2D" uid="uid://cmc7gcplqnebx" path="res://SharedAssets/Player.png" id="3_qiprp"]
+[ext_resource type="Script" uid="uid://cp6t6haqyef7o" path="res://Scenes/AreaBasedBackground.cs" id="5_taedj"]
-[node name="Cartographer" type="Node2D" node_paths=PackedStringArray("PlayerSprite")]
+[node name="Cartographer" type="Node2D" node_paths=PackedStringArray("PlayerSprite", "Camera")]
process_mode = 1
script = ExtResource("1_u4q3n")
PlayerSprite = NodePath("Player")
+ButtonTheme = ExtResource("2_rnj1y")
+Camera = NodePath("TheView")
+
+[node name="TheView" type="Camera2D" parent="."]
+top_level = true
+anchor_mode = 0
+limit_top = -10
[node name="UI" type="CanvasLayer" parent="." node_paths=PackedStringArray("CurSceneNode")]
script = ExtResource("2_cl7v0")
CurSceneNode = NodePath("..")
-[node name="BG" type="Sprite2D" parent="."]
+[node name="BG" type="TextureRect" parent="."]
modulate = Color(0.462, 0.462, 0.66, 1)
-position = Vector2(870, 219)
-scale = Vector2(2.75702, 2.75702)
+offset_left = -557.0
+offset_top = -84.0
+offset_right = 83.0
+offset_bottom = 96.0
+scale = Vector2(3, 3)
texture = ExtResource("2_5g6at")
+script = ExtResource("5_taedj")
[node name="Player" type="Sprite2D" parent="."]
z_index = 2
diff --git a/Scenes/Maps/InBetween.tscn b/Scenes/Maps/InBetween.tscn
new file mode 100644
index 00000000..d425f862
--- /dev/null
+++ b/Scenes/Maps/InBetween.tscn
@@ -0,0 +1,24 @@
+[gd_scene load_steps=4 format=3 uid="uid://djlperv5n75y1"]
+
+[ext_resource type="Texture2D" uid="uid://qhwve7fik4do" path="res://SharedAssets/BackGround_Full.png" id="1_74aj5"]
+[ext_resource type="Theme" uid="uid://d37e3tpsbxwak" path="res://Scenes/UI/Assets/GeneralTheme.tres" id="2_dapxv"]
+[ext_resource type="Script" uid="uid://cahjluc6v7ked" path="res://Scenes/UI/TitleScreen/Scripts/SceneChange.cs" id="3_35xdc"]
+
+[node name="Inbetween" type="Node2D"]
+
+[node name="Sprite2D" type="Sprite2D" parent="."]
+position = Vector2(197, 86)
+rotation = 3.72628
+scale = Vector2(3.81, 3.81)
+texture = ExtResource("1_74aj5")
+
+[node name="Button" type="Button" parent="."]
+offset_left = 263.0
+offset_top = 276.0
+offset_right = 271.0
+offset_bottom = 284.0
+theme = ExtResource("2_dapxv")
+text = "INBETWEEN_CONTINUE"
+script = ExtResource("3_35xdc")
+ScenePath = 5
+_startFocused = true
diff --git a/Scenes/Maps/Scripts/Cartographer.cs b/Scenes/Maps/Scripts/Cartographer.cs
index c48f82b8..ebd07533 100644
--- a/Scenes/Maps/Scripts/Cartographer.cs
+++ b/Scenes/Maps/Scripts/Cartographer.cs
@@ -14,6 +14,12 @@ public partial class Cartographer : Node2D
[Export]
public Sprite2D PlayerSprite;
+ [Export]
+ public Theme ButtonTheme;
+
+ [Export]
+ public Camera2D Camera;
+
private Button[] _validButtons = Array.Empty