commit b4d33cdc7a1eee16bbbecc522e5d13b9f11c8e34 Author: MeowcaTheoRange Date: Sat Nov 23 14:36:52 2024 -0600 first commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4a8ac9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Godot 4+ specific ignores +.godot/ +/android/ +/addons/ diff --git a/FunkPanion.csproj b/FunkPanion.csproj new file mode 100644 index 0000000..4032b9f --- /dev/null +++ b/FunkPanion.csproj @@ -0,0 +1,12 @@ + + + net6.0 + net7.0 + net8.0 + true + + + + + + \ No newline at end of file diff --git a/FunkPanion.sln b/FunkPanion.sln new file mode 100644 index 0000000..288815c --- /dev/null +++ b/FunkPanion.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FunkPanion", "FunkPanion.csproj", "{3011DD22-D461-4393-B9FC-2AD111BC5DD7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + ExportDebug|Any CPU = ExportDebug|Any CPU + ExportRelease|Any CPU = ExportRelease|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3011DD22-D461-4393-B9FC-2AD111BC5DD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3011DD22-D461-4393-B9FC-2AD111BC5DD7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3011DD22-D461-4393-B9FC-2AD111BC5DD7}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU + {3011DD22-D461-4393-B9FC-2AD111BC5DD7}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU + {3011DD22-D461-4393-B9FC-2AD111BC5DD7}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU + {3011DD22-D461-4393-B9FC-2AD111BC5DD7}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU + EndGlobalSection +EndGlobal diff --git a/assets/BOYFRIEND.png b/assets/BOYFRIEND.png new file mode 100644 index 0000000..9877e1d Binary files /dev/null and b/assets/BOYFRIEND.png differ diff --git a/assets/BOYFRIEND.png.import b/assets/BOYFRIEND.png.import new file mode 100644 index 0000000..dd313cf --- /dev/null +++ b/assets/BOYFRIEND.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6x537wsl3853" +path="res://.godot/imported/BOYFRIEND.png-39cdb7ee73d5290fc66317ae894c5c49.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/BOYFRIEND.png" +dest_files=["res://.godot/imported/BOYFRIEND.png-39cdb7ee73d5290fc66317ae894c5c49.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/assets/BOYFRIEND.xml b/assets/BOYFRIEND.xml new file mode 100644 index 0000000..d9df890 --- /dev/null +++ b/assets/BOYFRIEND.xml @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/beep.ogg b/assets/beep.ogg new file mode 100644 index 0000000..b7344e9 Binary files /dev/null and b/assets/beep.ogg differ diff --git a/assets/beep.ogg.import b/assets/beep.ogg.import new file mode 100644 index 0000000..799d17b --- /dev/null +++ b/assets/beep.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://bj6b8h153h1bn" +path="res://.godot/imported/beep.ogg-5c9be1364add69e4f2108f8168ecb666.oggvorbisstr" + +[deps] + +source_file="res://assets/beep.ogg" +dest_files=["res://.godot/imported/beep.ogg-5c9be1364add69e4f2108f8168ecb666.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/assets/config/body.tres b/assets/config/body.tres new file mode 100644 index 0000000..9bc40b4 --- /dev/null +++ b/assets/config/body.tres @@ -0,0 +1,6 @@ +[gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://c2s3eclr25ots"] + +[ext_resource type="FontFile" uid="uid://ce5fs0ro7kp8r" path="res://assets/fonts/Lexend.ttf" id="1_j6m8r"] + +[resource] +font = ExtResource("1_j6m8r") diff --git a/assets/config/button_icon.tres b/assets/config/button_icon.tres new file mode 100644 index 0000000..9a8309a --- /dev/null +++ b/assets/config/button_icon.tres @@ -0,0 +1,12 @@ +[gd_resource type="FontVariation" load_steps=2 format=3 uid="uid://d1apfvux6r82v"] + +[ext_resource type="FontFile" uid="uid://4e3o2ds5xy7p" path="res://assets/fonts/MatSymOut.ttf" id="1_2fm8g"] + +[resource] +base_font = ExtResource("1_2fm8g") +variation_opentype = { +1179208780: 1, +1196572996: 0, +1869640570: 24, +2003265652: 400 +} diff --git a/assets/config/header_bold.tres b/assets/config/header_bold.tres new file mode 100644 index 0000000..4a3a1b6 --- /dev/null +++ b/assets/config/header_bold.tres @@ -0,0 +1,7 @@ +[gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://2iu8q0tylumo"] + +[ext_resource type="FontFile" uid="uid://c6ck52v6qotm" path="res://assets/fonts/B_Lexend.ttf" id="1_gt0lt"] + +[resource] +font = ExtResource("1_gt0lt") +font_size = 24 diff --git a/assets/fonts/B_Lexend.ttf b/assets/fonts/B_Lexend.ttf new file mode 100644 index 0000000..49c0bb7 Binary files /dev/null and b/assets/fonts/B_Lexend.ttf differ diff --git a/assets/fonts/B_Lexend.ttf.import b/assets/fonts/B_Lexend.ttf.import new file mode 100644 index 0000000..d993120 --- /dev/null +++ b/assets/fonts/B_Lexend.ttf.import @@ -0,0 +1,34 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://c6ck52v6qotm" +path="res://.godot/imported/B_Lexend.ttf-11a8f3d5fbc8db49d05674d6da816c6b.fontdata" + +[deps] + +source_file="res://assets/fonts/B_Lexend.ttf" +dest_files=["res://.godot/imported/B_Lexend.ttf-11a8f3d5fbc8db49d05674d6da816c6b.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/assets/fonts/Lexend.ttf b/assets/fonts/Lexend.ttf new file mode 100644 index 0000000..01eab76 Binary files /dev/null and b/assets/fonts/Lexend.ttf differ diff --git a/assets/fonts/Lexend.ttf.import b/assets/fonts/Lexend.ttf.import new file mode 100644 index 0000000..9595f43 --- /dev/null +++ b/assets/fonts/Lexend.ttf.import @@ -0,0 +1,34 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://ce5fs0ro7kp8r" +path="res://.godot/imported/Lexend.ttf-97328ab4efa8698b78e5efb478c79f57.fontdata" + +[deps] + +source_file="res://assets/fonts/Lexend.ttf" +dest_files=["res://.godot/imported/Lexend.ttf-97328ab4efa8698b78e5efb478c79f57.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/assets/fonts/MatSymOut.ttf b/assets/fonts/MatSymOut.ttf new file mode 100644 index 0000000..b7be1bd Binary files /dev/null and b/assets/fonts/MatSymOut.ttf differ diff --git a/assets/fonts/MatSymOut.ttf.import b/assets/fonts/MatSymOut.ttf.import new file mode 100644 index 0000000..ab30ecd --- /dev/null +++ b/assets/fonts/MatSymOut.ttf.import @@ -0,0 +1,46 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://4e3o2ds5xy7p" +path="res://.godot/imported/MatSymOut.ttf-0ac93f0cdc98631aa68f08c390fa1be0.fontdata" + +[deps] + +source_file="res://assets/fonts/MatSymOut.ttf" +dest_files=["res://.godot/imported/MatSymOut.ttf-0ac93f0cdc98631aa68f08c390fa1be0.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[{ +"chars": [57344, 57345, 57346, 57347, 57348, 57354, 57355, 57356, 57357, 57359, 57360, 57361, 57363, 57364, 57365, 57366, 57367, 57368, 57369, 57370, 57371, 57372, 57373, 57374, 57375, 57376, 57377, 57378, 57379, 57380, 57381, 57382, 57383, 57384, 57385, 57386, 57387, 57388, 57389, 57390, 57391, 57392, 57393, 57394, 57395, 57396, 57397, 57398, 57399, 57400, 57401, 57403, 57404, 57405, 57406, 57407, 57408, 57409, 57410, 57411, 57412, 57413, 57414, 57415, 57416, 57417, 57418, 57419, 57420, 57421, 57422, 57423, 57424, 57425, 57426, 57427, 57429, 57430, 57431, 57432, 57433, 57434, 57435, 57436, 57437, 57438, 57439, 57440, 57441, 57442, 57443, 57444, 57445, 57446, 57447, 57448, 57449, 57450, 57451, 57452, 57453, 57454, 57455, 57456, 57457, 57458, 57459, 57460, 57461, 57462, 57481, 57486, 57489, 57491, 57492, 57495, 57496, 57497, 57500, 57502, 57503, 57504, 57505, 57509, 57510, 57511, 57512, 57513, 57514, 57516, 57517, 57518, 57519, 57520, 57521, 57522, 57523, 57524, 57525, 57526, 57527, 57528, 57529, 57530, 57531, 57532, 57534, 57535, 57537, 57538, 57539, 57540, 57542, 57543, 57544, 57545, 57546, 57547, 57548, 57549, 57550, 57551, 57552, 57553, 57554, 57555, 57556, 57557, 57558, 57559, 57560, 57561, 57562, 57563, 57564, 57565, 57566, 57567, 57568, 57569, 57570, 57571, 57572, 57573, 57574, 57575, 57576, 57577, 57578, 57579, 57580, 57581, 57582, 57583, 57584, 57585, 57587, 57588, 57590, 57595, 57596, 57598, 57603, 57604, 57605, 57606, 57611, 57612, 57613, 57614, 57616, 57620, 57621, 57622, 57624, 57625, 57629, 57630, 57631, 57632, 57633, 57634, 57635, 57636, 57637, 57639, 57640, 57645, 57649, 57650, 57651, 57655, 57656, 57657, 57659, 57660, 57661, 57662, 57663, 57669, 57670, 57671, 57672, 57673, 57674, 57675, 57676, 57677, 57678, 57679, 57680, 57681, 57682, 57683, 57684, 57685, 57686, 57687, 57688, 57689, 57690, 57691, 57692, 57693, 57694, 57695, 57696, 57697, 57698, 57699, 57700, 57701, 57702, 57703, 57704, 57705, 57706, 57707, 57708, 57709, 57710, 57711, 57712, 57713, 57714, 57715, 57716, 57717, 57718, 57719, 57720, 57721, 57736, 57744, 57745, 57746, 57747, 57748, 57749, 57753, 57756, 57758, 57759, 57760, 57761, 57762, 57763, 57764, 57765, 57766, 57767, 57768, 57769, 57770, 57771, 57772, 57773, 57774, 57775, 57776, 57777, 57778, 57779, 57780, 57781, 57782, 57783, 57784, 57785, 57786, 57787, 57788, 57789, 57790, 57791, 57792, 57793, 57794, 57795, 57796, 57797, 57798, 57800, 57802, 57803, 57804, 57805, 57806, 57807, 57808, 57809, 57810, 57811, 57813, 57816, 57817, 57818, 57819, 57820, 57821, 57822, 57824, 57825, 57826, 57830, 57831, 57832, 57833, 57834, 57835, 57842, 57843, 57844, 57847, 57849, 57854, 57855, 57856, 57857, 57858, 57859, 57860, 57861, 57862, 57865, 57868, 57872, 57875, 57876, 57884, 57885, 57886, 57891, 57892, 57894, 57895, 57896, 57897, 57898, 57899, 57900, 57901, 57902, 57903, 57904, 57905, 57906, 57907, 57908, 57909, 57910, 57911, 57912, 57913, 57914, 57915, 57916, 57917, 57918, 57919, 57920, 57921, 57922, 57923, 57924, 57925, 57926, 57927, 57928, 57929, 57930, 57931, 57932, 57933, 57934, 57935, 57936, 57937, 57938, 57939, 57940, 57941, 57942, 57943, 57944, 57945, 57946, 57947, 57948, 57949, 57950, 57951, 57952, 57953, 57954, 57955, 57956, 57957, 57958, 57959, 57960, 57961, 57962, 57963, 57964, 57971, 57974, 57975, 57976, 57980, 57982, 57986, 57987, 57988, 57989, 57990, 57994, 57995, 57996, 57997, 57998, 58001, 58003, 58005, 58006, 58010, 58012, 58015, 58023, 58024, 58026, 58027, 58028, 58030, 58031, 58036, 58038, 58041, 58042, 58043, 58044, 58045, 58046, 58047, 58048, 58049, 58050, 58051, 58052, 58053, 58054, 58055, 58056, 58057, 58058, 58059, 58060, 58061, 58075, 58076, 58077, 58086, 58087, 58090, 58091, 58092, 58119, 58120, 58122, 58123, 58124, 58125, 58126, 58127, 58128, 58129, 58130, 58131, 58132, 58133, 58134, 58135, 58136, 58138, 58139, 58140, 58141, 58142, 58143, 58144, 58145, 58146, 58147, 58148, 58149, 58150, 58151, 58152, 58153, 58154, 58155, 58156, 58157, 58158, 58159, 58160, 58161, 58162, 58163, 58164, 58165, 58166, 58167, 58168, 58169, 58170, 58198, 58260, 58265, 58269, 58270, 58271, 58272, 58273, 58274, 58275, 58276, 58277, 58278, 58279, 58280, 58281, 58282, 58283, 58284, 58285, 58286, 58287, 58288, 58289, 58290, 58291, 58292, 58293, 58294, 58295, 58296, 58297, 58298, 58299, 58300, 58301, 58302, 58303, 58304, 58305, 58306, 58307, 58308, 58309, 58310, 58311, 58312, 58313, 58314, 58315, 58316, 58317, 58318, 58319, 58320, 58321, 58322, 58323, 58324, 58325, 58326, 58327, 58328, 58329, 58330, 58331, 58332, 58333, 58334, 58335, 58336, 58337, 58338, 58339, 58340, 58341, 58342, 58343, 58344, 58345, 58346, 58347, 58348, 58349, 58350, 58351, 58353, 58354, 58355, 58356, 58357, 58358, 58359, 58360, 58361, 58362, 58363, 58364, 58365, 58366, 58367, 58368, 58369, 58370, 58371, 58372, 58373, 58374, 58375, 58376, 58377, 58378, 58379, 58380, 58381, 58382, 58383, 58384, 58385, 58386, 58387, 58389, 58390, 58391, 58392, 58393, 58394, 58395, 58396, 58397, 58398, 58399, 58400, 58401, 58402, 58403, 58404, 58405, 58406, 58407, 58408, 58409, 58410, 58411, 58412, 58413, 58414, 58416, 58417, 58418, 58419, 58420, 58421, 58422, 58423, 58424, 58425, 58426, 58427, 58428, 58429, 58430, 58431, 58432, 58478, 58483, 58491, 58499, 58505, 58509, 58511, 58523, 58528, 58535, 58553, 58568, 58570, 58571, 58576, 58585, 58590, 58603, 58611, 58612, 58613, 58614, 58615, 58616, 58617, 58618, 58619, 58620, 58621, 58622, 58623, 58624, 58625, 58626, 58627, 58629, 58630, 58633, 58634, 58635, 58636, 58637, 58638, 58639, 58640, 58641, 58642, 58643, 58644, 58645, 58648, 58650, 58652, 58653, 58654, 58655, 58656, 58657, 58661, 58663, 58669, 58670, 58671, 58672, 58673, 58674, 58675, 58676, 58677, 58678, 58680, 58681, 58682, 58683, 58684, 58685, 58686, 58687, 58688, 58689, 58690, 58691, 58692, 58693, 58694, 58695, 58696, 58697, 58698, 58699, 58700, 58701, 58702, 58703, 58704, 58705, 58706, 58707, 58708, 58709, 58710, 58711, 58712, 58713, 58714, 58715, 58716, 58717, 58718, 58719, 58720, 58721, 58722, 58723, 58724, 58725, 58726, 58727, 58728, 58729, 58730, 58731, 58732, 58733, 58734, 58735, 58736, 58737, 58738, 58739, 58740, 58741, 58742, 58743, 58744, 58745, 58746, 58747, 58748, 58749, 58755, 58756, 58757, 58758, 58759, 58760, 58761, 58762, 58763, 58764, 58765, 58766, 58767, 58768, 58769, 58771, 58772, 58773, 58776, 58777, 58778, 58819, 58820, 58821, 58822, 58823, 58824, 58825, 58826, 58827, 58828, 58829, 58830, 58831, 58832, 58833, 58834, 58835, 58836, 58837, 58838, 58839, 58840, 58841, 58842, 58843, 58844, 58845, 58846, 58847, 58848, 58849, 58866, 58867, 58871, 58874, 58880, 58882, 58889, 58894, 58895, 58896, 58897, 58898, 58899, 58900, 58901, 58902, 58903, 58904, 58905, 58906, 58907, 58908, 58909, 58910, 58911, 58912, 58915, 58916, 58917, 58918, 58919, 58920, 58921, 58922, 58923, 58924, 58925, 58926, 58927, 58928, 58929, 58930, 58931, 58932, 58933, 58934, 58935, 58936, 58937, 58938, 58939, 58940, 58941, 58942, 58943, 58944, 58945, 58946, 58947, 58948, 58949, 58950, 58951, 58952, 58953, 58956, 58958, 58960, 58961, 58963, 58966, 58969, 58970, 58971, 58972, 58975, 58976, 58977, 58978, 58979, 58980, 58981, 58982, 58983, 58985, 58987, 58989, 58997, 59000, 59014, 59022, 59023, 59026, 59028, 59029, 59030, 59031, 59032, 59033, 59034, 59035, 59036, 59037, 59038, 59039, 59042, 59045, 59047, 59050, 59052, 59057, 59059, 59063, 59064, 59071, 59074, 59075, 59076, 59077, 59078, 59079, 59081, 59082, 59087, 59088, 59089, 59090, 59091, 59092, 59094, 59098, 59101, 59102, 59103, 59105, 59112, 59113, 59118, 59128, 59129, 59130, 59131, 59137, 59138, 59139, 59141, 59142, 59144, 59147, 59148, 59149, 59150, 59152, 59156, 59157, 59159, 59161, 59162, 59163, 59164, 59166, 59168, 59174, 59176, 59177, 59178, 59181, 59182, 59183, 59184, 59185, 59186, 59187, 59188, 59194, 59196, 59197, 59198, 59200, 59201, 59202, 59203, 59204, 59205, 59206, 59207, 59208, 59209, 59232, 59233, 59234, 59235, 59236, 59238, 59239, 59241, 59245, 59246, 59247, 59248, 59258, 59259, 59260, 59262, 59265, 59267, 59268, 59269, 59270, 59272, 59273, 59274, 59276, 59277, 59278, 59279, 59282, 59288, 59289, 59290, 59291, 59292, 59298, 59299, 59309, 59311, 59312, 59315, 59336, 59337, 59338, 59339, 59340, 59341, 59342, 59343, 59344, 59345, 59347, 59352, 59361, 59362, 59363, 59364, 59365, 59366, 59369, 59370, 59372, 59373, 59374, 59375, 59376, 59377, 59378, 59379, 59380, 59381, 59382, 59383, 59384, 59385, 59386, 59387, 59388, 59389, 59390, 59391, 59392, 59393, 59403, 59404, 59405, 59406, 59407, 59408, 59409, 59410, 59411, 59412, 59413, 59414, 59415, 59416, 59417, 59418, 59432, 59433, 59434, 59436, 59437, 59438, 59440, 59442, 59443, 59444, 59445, 59446, 59447, 59448, 59449, 59450, 59451, 59452, 59453, 59454, 59455, 59456, 59457, 59458, 59459, 59460, 59462, 59463, 59465, 59468, 59469, 59470, 59471, 59472, 59473, 59474, 59475, 59476, 59477, 59478, 59479, 59480, 59481, 59482, 59483, 59484, 59485, 59486, 59487, 59488, 59489, 59490, 59491, 59492, 59493, 59494, 59495, 59496, 59497, 59498, 59499, 59500, 59501, 59502, 59503, 59504, 59505, 59506, 59507, 59508, 59509, 59510, 59511, 59512, 59513, 59514, 59515, 59516, 59517, 59518, 59519, 59520, 59521, 59522, 59523, 59524, 59525, 59526, 59527, 59528, 59529, 59530, 59531, 59532, 59533, 59534, 59536, 59537, 59538, 59539, 59540, 59541, 59542, 59543, 59544, 59545, 59546, 59547, 59548, 59549, 59550, 59551, 59552, 59553, 59554, 59555, 59556, 59557, 59558, 59559, 59560, 59561, 59562, 59563, 59564, 59565, 59566, 59567, 59568, 59569, 59570, 59571, 59572, 59573, 59574, 59575, 59576, 59577, 59578, 59579, 59580, 59581, 59582, 59583, 59584, 59585, 59586, 59587, 59588, 59589, 59590, 59591, 59592, 59593, 59594, 59595, 59596, 59597, 59598, 59599, 59600, 59601, 59602, 59603, 59604, 59605, 59606, 59607, 59608, 59609, 59610, 59611, 59612, 59613, 59614, 59615, 59616, 59617, 59618, 59619, 59620, 59621, 59622, 59623, 59624, 59625, 59626, 59627, 59628, 59629, 59630, 59631, 59632, 59633, 59634, 59635, 59636, 59637, 59638, 59639, 59640, 59641, 59642, 59643, 59644, 59645, 59646, 59647, 59648, 59650, 59651, 59652, 59653, 59654, 59655, 59656, 59657, 59658, 59659, 59660, 59661, 59662, 59663, 59665, 59666, 59667, 59668, 59669, 59670, 59671, 59672, 59673, 59674, 59675, 59676, 59677, 59678, 59679, 59680, 59681, 59682, 59683, 59684, 59685, 59686, 59687, 59688, 59689, 59690, 59691, 59692, 59693, 59694, 59695, 59696, 59697, 59698, 59699, 59700, 59701, 59702, 59703, 59704, 59705, 59706, 59707, 59708, 59709, 59710, 59711, 59712, 59713, 59714, 59715, 59716, 59717, 59718, 59719, 59720, 59721, 59722, 59723, 59724, 59725, 59726, 59727, 59728, 59729, 59730, 59731, 59732, 59733, 59734, 59735, 59736, 59737, 59738, 59739, 59740, 59741, 59742, 59743, 59744, 59745, 59746, 59747, 59748, 59749, 59750, 59751, 59752, 59753, 59754, 59755, 59756, 59757, 59758, 59759, 59760, 59761, 59762, 59763, 59764, 59765, 59766, 59767, 59768, 59769, 59770, 59771, 59772, 59773, 59774, 59775, 59777, 59778, 59779, 59780, 59781, 59782, 59783, 59784, 59785, 59786, 59787, 59788, 59789, 59790, 59791, 59793, 59794, 59795, 59796, 59797, 59798, 59799, 59800, 59801, 59802, 59803, 59804, 59805, 59806, 59807, 59808, 59809, 59810, 59811, 59812, 59813, 59814, 59815, 59816, 59817, 59818, 59819, 59820, 59821, 59824, 59825, 59826, 59828, 59829, 59830, 59831, 59832, 59833, 59834, 59835, 59836, 59837, 59838, 59839, 59840, 59841, 59842, 59843, 59844, 59845, 59847, 59848, 59849, 59851, 59852, 59853, 59854, 59855, 59856, 59857, 59858, 59859, 59860, 59861, 59862, 59863, 59864, 59865, 59866, 59867, 59868, 59869, 59871, 59872, 59873, 59874, 59875, 59876, 59878, 59879, 59880, 59881, 59882, 59884, 59885, 59886, 59887, 59889, 59890, 59891, 59892, 59893, 59894, 59895, 59896, 59897, 59898, 59899, 59900, 59902, 59904, 59905, 59907, 59908, 59912, 59913, 59914, 59915, 59916, 59918, 59919, 59920, 59921, 59922, 59923, 59924, 59925, 59926, 59927, 59928, 59929, 59930, 59931, 59932, 59933, 59934, 59935, 59936, 59937, 59938, 59939, 59940, 59941, 59942, 59943, 59944, 59945, 59946, 59947, 59948, 59949, 59950, 59951, 59952, 59953, 59954, 59955, 59956, 59957, 59958, 59959, 59960, 59961, 59962, 59963, 59964, 59965, 59966, 59967, 59968, 59969, 59970, 59971, 59972, 59973, 59974, 59975, 59976, 59977, 59978, 59979, 59980, 59981, 59982, 59983, 59984, 59985, 59986, 59987, 59988, 59989, 59990, 59991, 59992, 59993, 59994, 59995, 59996, 59997, 59998, 59999, 60000, 60001, 60002, 60003, 60004, 60005, 60006, 60007, 60008, 60009, 60016, 60017, 60018, 60019, 60020, 60021, 60022, 60023, 60024, 60025, 60029, 60037, 60039, 60046, 60057, 60058, 60059, 60066, 60070, 60071, 60072, 60073, 60074, 60075, 60076, 60077, 60078, 60079, 60080, 60081, 60082, 60083, 60093, 60098, 60099, 60102, 60103, 60104, 60105, 60106, 60108, 60109, 60111, 60112, 60113, 60114, 60115, 60116, 60117, 60118, 60119, 60120, 60121, 60122, 60123, 60125, 60126, 60130, 60131, 60132, 60133, 60134, 60135, 60136, 60137, 60138, 60139, 60140, 60141, 60142, 60143, 60144, 60145, 60146, 60147, 60148, 60149, 60150, 60151, 60152, 60153, 60154, 60155, 60160, 60161, 60162, 60163, 60164, 60165, 60168, 60169, 60170, 60171, 60172, 60173, 60174, 60175, 60176, 60178, 60179, 60180, 60181, 60182, 60183, 60184, 60185, 60186, 60187, 60188, 60189, 60190, 60191, 60198, 60199, 60200, 60201, 60202, 60203, 60204, 60205, 60206, 60207, 60208, 60209, 60210, 60211, 60212, 60213, 60214, 60215, 60216, 60217, 60218, 60219, 60220, 60221, 60222, 60223, 60224, 60225, 60226, 60227, 60228, 60229, 60230, 60231, 60232, 60233, 60234, 60235, 60236, 60237, 60238, 60239, 60240, 60241, 60242, 60243, 60244, 60246, 60247, 60248, 60249, 60250, 60254, 60255, 60256, 60257, 60258, 60259, 60260, 60261, 60262, 60263, 60264, 60265, 60266, 60267, 60269, 60271, 60272, 60273, 60274, 60275, 60276, 60277, 60278, 60279, 60282, 60283, 60284, 60285, 60286, 60287, 60288, 60289, 60290, 60291, 60292, 60293, 60294, 60295, 60296, 60298, 60299, 60300, 60301, 60302, 60303, 60304, 60305, 60306, 60307, 60308, 60309, 60310, 60311, 60312, 60313, 60314, 60315, 60316, 60317, 60318, 60319, 60320, 60321, 60322, 60323, 60324, 60325, 60326, 60327, 60328, 60329, 60330, 60331, 60332, 60338, 60342, 60343, 60344, 60345, 60346, 60347, 60348, 60349, 60350, 60351, 60356, 60357, 60358, 60359, 60360, 60362, 60363, 60364, 60365, 60366, 60367, 60368, 60369, 60370, 60371, 60372, 60373, 60374, 60375, 60376, 60377, 60378, 60379, 60380, 60381, 60382, 60383, 60384, 60385, 60386, 60387, 60388, 60389, 60390, 60391, 60392, 60393, 60394, 60395, 60396, 60397, 60398, 60399, 60400, 60401, 60402, 60403, 60404, 60406, 60407, 60412, 60413, 60414, 60415, 60424, 60425, 60426, 60427, 60428, 60429, 60430, 60431, 60432, 60433, 60434, 60435, 60436, 60437, 60438, 60439, 60440, 60441, 60442, 60443, 60444, 60445, 60446, 60447, 60448, 60465, 60485, 60530, 60531, 61239, 61240, 61241, 61242, 61243, 61244, 61245, 61246, 61247, 61248, 61249, 61250, 61251, 61252, 61253, 61255, 61256, 61257, 61258, 61259, 61260, 61261, 61262, 61263, 61264, 61265, 61266, 61267, 61268, 61269, 61270, 61271, 61272, 61273, 61274, 61275, 61276, 61277, 61278, 61279, 61283, 61284, 61286, 61288, 61289, 61290, 61291, 61292, 61293, 61294, 61295, 61296, 61297, 61298, 61299, 61300, 61301, 61302, 61303, 61304, 61307, 61308, 61309, 61311, 61313, 61314, 61315, 61316, 61317, 61318, 61327, 61328, 61329, 61330, 61331, 61335, 61340, 61341, 61342, 61343, 61344, 61345, 61346, 61347, 61348, 61351, 61353, 61355, 61356, 61357, 61358, 61359, 61361, 61362, 61366, 61367, 61368, 61375, 61376, 61377, 61378, 61379, 61381, 61382, 61385, 61386, 61387, 61389, 61390, 61391, 61392, 61393, 61394, 61395, 61396, 61397, 61398, 61399, 61400, 61401, 61402, 61403, 61404, 61405, 61406, 61407, 61408, 61409, 61410, 61411, 61412, 61413, 61414, 61415, 61416, 61417, 61418, 61419, 61420, 61421, 61422, 61423, 61424, 61425, 61426, 61427, 61428, 61429, 61430, 61431, 61432, 61433, 61434, 61435, 61436, 61437, 61438, 61439, 61440, 61441, 61442, 61443, 61444, 61445, 61446, 61447, 61448, 61449, 61450, 61451, 61452, 61453, 61454, 61455, 61456, 61457, 61458, 61459, 61460, 61461, 61462, 61463, 61464, 61465, 61466, 61467, 61468, 61469, 61470, 61471, 61472, 61473, 61474, 61475, 61476, 61477, 61478, 61480, 61481, 61482, 61483, 61484, 61485, 61486, 61487, 61488, 61489, 61490, 61491, 61492, 61493, 61494, 61495, 61496, 61497, 61498, 61499, 61500, 61501, 61502, 61503, 61504, 61505, 61506, 61507, 61508, 61509, 61511, 61512, 61513, 61514, 61515, 61516, 61517, 61518, 61519, 61520, 61521, 61522, 61523, 61524, 61525, 61526, 61527, 61528, 61529, 61530, 61531, 61532, 61533, 61534, 61535, 61536, 61537, 61538, 61539, 61540, 61541, 61543, 61544, 61545, 61546, 61547, 61548, 61549, 61550, 61551, 61552, 61553, 61554, 61555, 61556, 61557, 61558, 61559, 61560, 61561, 61562, 61563, 61564, 61565, 61566, 61567, 61568, 61569, 61570, 61571, 61572, 61573, 61575, 61576, 61577, 61579, 61580, 61581, 61582, 61583, 61584, 61585, 61586, 61591, 61593, 61594, 61595, 61596, 61597, 61598, 61599, 61600, 61601, 61602, 61603, 61604, 61605, 61606, 61607, 61608, 61609, 61610, 61611, 61612, 61616, 61623, 61626, 61627, 61628, 61629, 61630, 61631, 61632, 61633, 61634, 61635, 61637, 61638, 61639, 61640, 61644, 61646, 61647, 61648, 61649, 61650, 61651, 61652, 61657, 61658, 61660, 61661, 61666, 61667, 61668, 61671, 61675, 61676, 61679, 61683, 61685, 61686, 61690, 61691, 61692, 61693, 61694, 61695, 61696, 61697, 61698, 61700, 61702, 61703, 61704, 61705, 61706, 61707, 61709, 61710, 61711, 61713, 61714, 61716, 61718, 61721, 61722, 61723, 61724, 61725, 61726, 61727, 61728, 61729, 61730, 61731, 61732, 61733, 61734, 61735, 61737, 61738, 61739, 61740, 61741, 61742, 61743, 61744, 61745, 61746, 61747, 61748, 61749, 61750, 61751, 61755, 61763, 61767, 61768, 61769, 61770, 61771, 61773, 61775, 61776, 61778, 61780, 61781, 61782, 61783, 61785, 61787, 61788, 61789, 61791, 61793, 61795, 61796, 61797, 61798, 61799, 61800, 61802, 61803, 61805, 61807, 61810, 61812, 61814, 61816, 61819, 61821, 61822, 61823, 61825, 61826, 61828, 61831, 61832, 61833, 61834, 61835, 61836, 61840, 61841, 61844, 61846, 61847, 61849, 61851, 61852, 61853, 61854, 61855, 61856, 61857, 61858, 61859, 61861, 61862, 61863, 61864, 61865, 61866, 61867, 61868, 61869, 61870, 61871, 61872, 61873, 61874, 61875, 61876, 61877, 61878, 61879, 61880, 61881, 61883, 61884, 61886, 61887, 61888, 61889, 61890, 61891, 61892, 61893, 61894, 61895, 61896, 61897, 61898, 61900, 61901, 61902, 61903, 61904, 61905, 61906, 61908, 61909, 61910, 61911, 61912, 61913, 61914, 61915, 61916, 61917, 61918, 61919, 61920, 61921, 61922, 61923, 61924, 61925, 61926, 61927, 61928, 61929, 61930, 61935, 61936, 61937, 61938, 61939, 61940, 61941, 61942, 61943, 61944, 61945, 61946, 61947, 61948, 61950, 61951, 61952, 61953, 61954, 61955, 61956, 61957, 61958, 61962, 61963, 61964, 61966, 61969, 61970, 61974, 61975, 61976, 61977, 61978, 61980, 61981, 61982, 61983, 61984, 61985, 61986, 61987, 61991, 61995, 61996, 61998, 61999, 62000, 62002, 62003, 62005, 62006, 62007, 62008, 62009, 62010, 62011, 62325, 62327, 62328, 62331, 62332, 62335, 62337, 62338, 62343, 62345, 62346, 62347, 62349, 62352, 62353, 62354, 62355, 62356, 62359, 62360, 62361, 62362, 62363, 62364, 62365, 62366, 62367, 62368, 62369, 62370, 62372, 62373, 62374, 62375, 62378, 62379, 62380, 62384, 62386, 62387, 62388, 62389, 62390, 62392, 62393, 62394, 62395, 62396, 62397, 62398, 62399, 62401, 62402, 62403, 62404, 62405, 62406, 62407, 62408, 62409, 62410, 62411, 62412, 62413, 62414, 62415, 62416, 62417, 62418, 62419, 62420, 62421, 62422, 62423, 62424, 62425, 62426, 62427, 62428, 62429, 62430, 62431, 62432, 62433, 62434, 62435, 62436, 62437, 62438, 62439, 62442, 62443, 62444, 62445, 62446, 62449, 62450, 62451, 62452, 62453, 62456, 62457, 62458, 62459, 62460, 62461, 62462, 62463, 62464, 62465, 62466, 62470, 62471, 62472, 62473, 62474, 62475, 62476, 62477, 62478, 62479, 62480, 62481, 62483, 62484, 62487, 62488, 62490, 62491, 62492, 62493, 62494, 62495, 62496, 62497, 62498, 62499, 62500, 62501, 62502, 62503, 62504, 62505, 62506, 62507, 62508, 62509, 62514, 62519, 62520, 62521, 62522, 62523, 62538, 62539, 62540, 62541, 62542, 62543, 62544, 62545, 62548, 62549, 62550, 62551, 62552, 62553, 62554, 62555, 62556, 62557, 62558, 62559, 62560, 62561, 62562, 62563, 62564, 62566, 62567, 62568, 62569, 62570, 62571, 62572, 62573, 62574, 62575, 62576, 62577, 62578, 62579, 62580, 62581, 62582, 62583, 62584, 62585, 62586, 62587, 62588, 62589, 62590, 62591, 62592, 62593, 62594, 62595, 62597, 62598, 62600, 62601, 62602, 62603, 62606, 62607, 62608, 62609, 62610, 62611, 62612, 62613, 62614, 62615, 62616, 62617, 62618, 62619, 62620, 62621, 62622, 62623, 62624, 62629, 62630, 62632, 62633, 62634, 62635, 62636, 62638, 62639, 62640, 62641, 62642, 62644, 62645, 62646, 62647, 62648, 62649, 62650, 62651, 62652, 62656, 62657, 62658, 62659, 62660, 62661, 62662, 62663, 62664, 62665, 62666, 62667, 62668, 62669, 62671, 62672, 62673, 62674, 62675, 62676, 62677, 62678, 62680, 62681, 62682, 62688, 62689, 62690, 62691, 62692, 62693, 62694, 62699, 62703, 62705, 62706, 62707, 62710, 62711, 62713, 62714, 62716, 62717, 62718, 62720, 62721, 62722, 62723, 62726, 62727, 62728, 62729, 62730, 62731, 62732, 62733, 62734, 62736, 62737, 62738, 62739, 62740, 62741, 62743, 62744, 62746, 62747, 62748, 62749, 62750, 62751, 62753, 62754, 62755, 62756, 62757, 62758, 62759, 62760, 62761, 62762, 62763, 62764, 62765, 62767, 62768, 62770, 62773, 62775, 62777, 62779, 62780, 62781, 62782, 62783, 62784, 62786, 62787, 62788, 62789, 62791, 62793, 62794, 62795, 62797, 62798, 62799, 62800, 62802, 62803, 62804, 62805, 62806, 62807, 62808, 62809, 62810, 62811, 62816, 62817, 62818, 62819, 62820, 62821, 62822, 62823, 62824, 62825, 62826, 62827, 62828, 62829, 62830, 62831, 62832, 62833, 62839, 62840, 62841, 62842, 62843, 62844, 62845, 62846, 62847, 62848, 62849, 62850, 62851, 62852, 62853, 62854, 62855, 62856, 62858, 62859, 62860, 62861, 62862, 62863, 62865, 62866, 62874, 62875, 62877, 62878, 62880, 62881, 62882, 62883, 62884, 62885, 62886, 62887, 62927, 62928, 62929, 62930, 62931, 62934, 62935, 62936, 62937, 62939, 62940, 62941, 62942, 62943, 62945, 62946, 62947, 62948, 62949, 62951, 62952, 62953, 62957, 62958, 62959, 62960, 62961, 62962, 62963, 62964, 62965, 62966, 62967, 62968, 63488, 63489, 63490, 63491, 63492, 63493, 63494, 63495, 63496, 63497, 63498, 63499, 63500, 63501, 63502, 63503, 63504, 63505, 63506, 63507, 63508, 63509, 63510, 63511, 63512, 63513, 63514, 63515, 63516, 63517, 63519, 63520, 63521, 63522, 63523, 63524, 63525, 63526, 63527, 63528, 63529, 63530, 63531, 63532, 63533, 63534, 63535, 63536, 63537, 63538, 63539, 63540, 63541, 63543, 63544, 63547, 63548, 63549, 63550, 63551, 63552, 63553, 63554, 63556, 63557, 63558, 63559, 63560, 63561, 63562, 63563, 63564, 63565, 63566, 63567, 63568, 63569, 63570, 63571, 63572, 63573, 63574, 63575, 63576, 63577, 63578, 63579, 63580, 63581, 63582, 63583, 63584, 63585, 63586, 63587, 63588, 63589, 63590, 63591, 63592, 63593, 63594, 63595, 63596, 63597, 63598, 63599, 63600, 63601, 63602, 63603, 63604, 63605, 63606, 63607, 63608, 63609, 63610, 63611, 63612, 63613, 63614, 63615, 63616, 63617, 63618, 63619, 63620, 63621, 63622, 63623, 63624, 63625, 63626, 63627, 63628, 63629, 63630, 63631, 63633, 63634, 63635, 63636, 63637, 63638, 63639, 63640, 63641, 63642, 63643, 63644, 63645, 63646, 63647, 63648, 63650, 63651, 63652, 63653, 63654, 63655, 63656, 63657, 63658, 63659, 63660, 63661, 63662, 63663, 63664, 63665, 63666, 63667, 63668, 63669, 63670, 63671, 63672, 63673, 63674, 63675, 63676, 63677, 63678, 63679, 63680, 63681, 63682, 63683, 63684, 63685, 63686, 63687, 63688, 63689, 63690, 63691, 63692, 63693, 63694, 63695, 63696, 63697, 63698, 63699, 63701, 63702, 63703, 63704, 63705, 63706, 63707, 63708, 63709, 63710, 63711, 63712, 63713, 63714, 63715, 63716, 63717, 63718, 63719, 63720, 63722, 63723, 63724, 63725, 63726, 63727, 63728, 63730, 63731, 63732, 63733, 63734, 63735, 63736, 63737, 63741, 63742, 63743, 62979, 62980, 62983, 62984, 62985, 62986, 62987, 62988, 62989, 62990, 62994, 62995, 62996, 62997, 62998, 62999, 63000, 63003, 63004, 63005, 63006, 63007, 63009, 63010, 63011, 63012, 63013, 63052, 63053, 63054, 63055, 63056, 63057, 63058, 63059, 63060, 63061, 63062, 63063, 63064, 63088, 63089, 63090, 63091, 63092, 63093, 63094, 63095, 63096, 63097, 63098, 63099, 63100, 63101, 63102, 63103, 63105, 63106, 63107, 63108, 63109, 63110, 63111, 63127, 63131, 63132, 63133, 63134, 63135, 63136, 63137, 63138, 63139, 63140, 63141, 63142, 63143, 63144, 63145, 63146, 63147, 63148, 63149, 63150, 63151, 63152, 63156, 63157, 63158, 63159, 63160, 63161, 63162, 63163, 63165, 63166, 63167, 63168, 63169, 63170, 63171, 63172, 63179, 63180, 63183, 63188, 63189, 63190, 63191, 63192, 63193, 63194, 63195, 63196, 63197, 63198, 63199, 63200, 63201, 63202, 63203, 63204, 63205, 63206, 63207, 63208, 63209, 63210, 63211, 63212, 63214, 63215, 63216, 63217, 63218, 63219, 63220, 63221, 63222, 63223, 63224, 63225, 63226, 63227, 63229, 63230, 63231, 63232, 63233, 63234, 63235, 63236, 63237, 63238, 63239, 63240, 63241, 63242, 63243, 63244, 63245, 63246, 63247, 63248, 63249, 63250, 63251, 63252, 63253, 63254, 63255, 63256, 63257, 63258, 63259, 63260, 63261, 63262, 63263, 63264, 63265, 63266, 63267, 63268, 63269, 63270, 63271, 63272, 63273, 63274, 63275, 63276, 63277, 63278, 63279, 63280, 63281, 63282, 63283, 63284, 63285, 63286, 63287, 63288, 63289, 63290, 63291, 63292, 63293, 63294, 63295, 63296, 63297, 63298, 63299, 63300, 63301, 63302, 63303, 63304, 63305, 63306, 63307, 63308, 63309, 63310, 63311, 63312, 63313, 63314, 63315, 63316, 63317, 63318, 63319, 63320, 63321, 63322, 63323, 63324, 63325, 63326, 63327, 63328, 63329, 63330, 63331, 63332, 63333, 63334, 63335, 63336, 63337, 63338, 63339, 63340, 63341, 63342, 63343, 63344, 63345, 63346, 63347, 63348, 63349, 63350, 63351, 63352, 63353, 63354, 63355, 63356, 63357, 63358, 63359, 63360, 63361, 63362, 63363, 63364, 63365, 63366, 63367, 63368, 63369, 63370, 63371, 63372, 63373, 63374, 63375, 63376, 63377, 63378, 63379, 63380, 63381, 63382, 63383, 63384, 63385, 63386, 63387, 63388, 63389, 63390, 63391, 63392, 63393, 63394, 63395, 63396, 63397, 63398, 63399, 63400, 63401, 63402, 63403, 63404, 63405, 63406, 63407, 63408, 63409, 63410, 63411, 63412, 63439, 63440, 63441, 63442, 63443, 63444, 63445, 63446, 63447, 63448, 63449, 63450, 63451, 63452, 63453, 63454, 63455, 63456, 63457, 63458, 63459, 63460, 63461, 63462, 63463, 63464, 63465, 63466, 63467, 63468, 63469, 63470, 63471, 63472, 63473, 63474, 63475, 63476, 63477, 63478, 63479, 63480, 63481, 63482, 63483, 63484, 63485, 63486, 63487], +"glyphs": [], +"name": "MATERIAL", +"size": Vector2i(24, 0), +"variation_embolden": 0.0, +"variation_opentype": { +1179208780: 1, +1196572996: 0, +1869640570: 24, +2003265652: 400 +} +}] +language_support={} +script_support={} +opentype_features={} diff --git a/assets/icons/add.png b/assets/icons/add.png new file mode 100644 index 0000000..9054eff Binary files /dev/null and b/assets/icons/add.png differ diff --git a/assets/icons/add.png.import b/assets/icons/add.png.import new file mode 100644 index 0000000..33a0a3b --- /dev/null +++ b/assets/icons/add.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c0cm2mtr1hjvx" +path="res://.godot/imported/add.png-e64c7df49d8f0ae02115175505e1c3d4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icons/add.png" +dest_files=["res://.godot/imported/add.png-e64c7df49d8f0ae02115175505e1c3d4.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/assets/icons/close.png b/assets/icons/close.png new file mode 100644 index 0000000..a10aa6b Binary files /dev/null and b/assets/icons/close.png differ diff --git a/assets/icons/close.png.import b/assets/icons/close.png.import new file mode 100644 index 0000000..1e0edbc --- /dev/null +++ b/assets/icons/close.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cl7yh6r8n5nns" +path="res://.godot/imported/close.png-8e81c1f7f49413c3a4d65fae064a1aae.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icons/close.png" +dest_files=["res://.godot/imported/close.png-8e81c1f7f49413c3a4d65fae064a1aae.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/assets/icons/placeholder.tres b/assets/icons/placeholder.tres new file mode 100644 index 0000000..df79324 --- /dev/null +++ b/assets/icons/placeholder.tres @@ -0,0 +1,4 @@ +[gd_resource type="PlaceholderTexture2D" format=3 uid="uid://gvxrvxva3u1v"] + +[resource] +size = Vector2(16, 16) diff --git a/assets/tap.wav b/assets/tap.wav new file mode 100644 index 0000000..c641db3 Binary files /dev/null and b/assets/tap.wav differ diff --git a/assets/tap.wav.import b/assets/tap.wav.import new file mode 100644 index 0000000..628b6c2 --- /dev/null +++ b/assets/tap.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://8ij8fr2uqb65" +path="res://.godot/imported/tap.wav-212d47e612c1e3e9050ccbc134a93d4e.sample" + +[deps] + +source_file="res://assets/tap.wav" +dest_files=["res://.godot/imported/tap.wav-212d47e612c1e3e9050ccbc134a93d4e.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=0 diff --git a/default_bus_layout.tres b/default_bus_layout.tres new file mode 100644 index 0000000..aa85803 --- /dev/null +++ b/default_bus_layout.tres @@ -0,0 +1,9 @@ +[gd_resource type="AudioBusLayout" format=3 uid="uid://bfmidrbpoihrj"] + +[resource] +bus/1/name = &"OSD" +bus/1/solo = false +bus/1/mute = false +bus/1/bypass_fx = false +bus/1/volume_db = 0.0 +bus/1/send = &"Master" diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..8456474 --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,110 @@ +[preset.0] + +name="Windows Desktop" +platform="Windows Desktop" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="../export/FunkPanion/FunkPanion-win.zip" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=true +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +binary_format/architecture="x86_64" +codesign/enable=false +codesign/timestamp=true +codesign/timestamp_server_url="" +codesign/digest_algorithm=1 +codesign/description="" +codesign/custom_options=PackedStringArray() +application/modify_resources=true +application/icon="" +application/console_wrapper_icon="" +application/icon_interpolation=0 +application/file_version="" +application/product_version="" +application/company_name="MeowcaTheoRange" +application/product_name="FunkPanion" +application/file_description="" +application/copyright="MeowcaTheoRange 2024" +application/trademarks="" +application/export_angle=0 +application/export_d3d12=0 +application/d3d12_agility_sdk_multiarch=true +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}' +$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}' +$trigger = New-ScheduledTaskTrigger -Once -At 00:00 +$settings = New-ScheduledTaskSettingsSet +$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings +Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true +Start-ScheduledTask -TaskName godot_remote_debug +while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 } +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue" +ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue +Remove-Item -Recurse -Force '{temp_dir}'" +dotnet/include_scripts_content=false +dotnet/include_debug_symbols=false +dotnet/embed_build_outputs=false + +[preset.1] + +name="Linux" +platform="Linux" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="../export/FunkPanion/FunkPanion-nix.zip" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.1.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=true +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" +dotnet/include_scripts_content=false +dotnet/include_debug_symbols=false +dotnet/embed_build_outputs=false diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..7948fae Binary files /dev/null and b/icon.png differ diff --git a/icon.png.import b/icon.png.import new file mode 100644 index 0000000..c1c0878 --- /dev/null +++ b/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dm5s1hfa53hpq" +path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.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/osd.tres b/osd.tres new file mode 100644 index 0000000..6df48b9 --- /dev/null +++ b/osd.tres @@ -0,0 +1,108 @@ +[gd_resource type="Theme" load_steps=10 format=3 uid="uid://e62vyqlhf1v1"] + +[ext_resource type="FontFile" uid="uid://ce5fs0ro7kp8r" path="res://assets/fonts/Lexend.ttf" id="1_6hrko"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7ci2v"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.12549, 0.12549, 0.12549, 0.25098) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cpmw2"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.25098, 0.25098, 0.25098, 0.752941) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gks1w"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.25098, 0.25098, 0.25098, 0.752941) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mcbjr"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.12549, 0.12549, 0.12549, 0.752941) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q2tue"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0, 0, 0, 0.752941) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8oovs"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.12549, 0.12549, 0.12549, 0.878431) +border_color = Color(0.12549, 0.12549, 0.12549, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6r5yo"] +content_margin_left = 8.0 +content_margin_top = 4.0 +content_margin_right = 8.0 +content_margin_bottom = 4.0 +bg_color = Color(0.25098, 0.25098, 0.25098, 0.752941) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_n886s"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.12549, 0.12549, 0.12549, 0.878431) +border_color = Color(0.12549, 0.12549, 0.12549, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[resource] +Button/fonts/font = ExtResource("1_6hrko") +Button/styles/disabled = SubResource("StyleBoxFlat_7ci2v") +Button/styles/focus = SubResource("StyleBoxFlat_cpmw2") +Button/styles/hover = SubResource("StyleBoxFlat_gks1w") +Button/styles/normal = SubResource("StyleBoxFlat_mcbjr") +Button/styles/pressed = SubResource("StyleBoxFlat_q2tue") +Label/fonts/font = ExtResource("1_6hrko") +PanelContainer/styles/panel = SubResource("StyleBoxFlat_8oovs") +PopupMenu/constants/item_end_padding = 8 +PopupMenu/constants/item_start_padding = 8 +PopupMenu/constants/v_separation = 8 +PopupMenu/styles/hover = SubResource("StyleBoxFlat_6r5yo") +PopupMenu/styles/panel = SubResource("StyleBoxFlat_n886s") diff --git a/plugins/GlobalInput/GlobalInputCSharp.cs b/plugins/GlobalInput/GlobalInputCSharp.cs new file mode 100644 index 0000000..5f861f2 --- /dev/null +++ b/plugins/GlobalInput/GlobalInputCSharp.cs @@ -0,0 +1,682 @@ +using Godot; +using Godot.Collections; +using System; +using SharpHook; +using System.Linq; + +public partial class GlobalInputCSharp : Node +{ + #region Setup + TaskPoolGlobalHook Hook = new(); + + Dictionary GodotKeyToSharpKey = new() + { + {Key.None.ToString(), new string[] {SharpHook.Native.KeyCode.VcUndefined.ToString()}}, + + {Key.Escape.ToString(), new string[] {SharpHook.Native.KeyCode.VcEscape.ToString()}}, + {Key.F1.ToString(), new string[] {SharpHook.Native.KeyCode.VcF1.ToString()}}, + {Key.F2.ToString(), new string[] {SharpHook.Native.KeyCode.VcF2.ToString()}}, + {Key.F3.ToString(), new string[] {SharpHook.Native.KeyCode.VcF3.ToString()}}, + {Key.F4.ToString(), new string[] {SharpHook.Native.KeyCode.VcF4.ToString()}}, + {Key.F5.ToString(), new string[] {SharpHook.Native.KeyCode.VcF5.ToString()}}, + {Key.F6.ToString(), new string[] {SharpHook.Native.KeyCode.VcF6.ToString()}}, + {Key.F7.ToString(), new string[] {SharpHook.Native.KeyCode.VcF7.ToString()}}, + {Key.F8.ToString(), new string[] {SharpHook.Native.KeyCode.VcF8.ToString()}}, + {Key.F9.ToString(), new string[] {SharpHook.Native.KeyCode.VcF9.ToString()}}, + {Key.F10.ToString(), new string[] {SharpHook.Native.KeyCode.VcF10.ToString()}}, + {Key.F11.ToString(), new string[] {SharpHook.Native.KeyCode.VcF11.ToString()}}, + {Key.F12.ToString(), new string[] {SharpHook.Native.KeyCode.VcF12.ToString()}}, + {Key.F13.ToString(), new string[] {SharpHook.Native.KeyCode.VcF13.ToString()}}, + {Key.F14.ToString(), new string[] {SharpHook.Native.KeyCode.VcF14.ToString()}}, + {Key.F15.ToString(), new string[] {SharpHook.Native.KeyCode.VcF15.ToString()}}, + {Key.F16.ToString(), new string[] {SharpHook.Native.KeyCode.VcF16.ToString()}}, + {Key.F17.ToString(), new string[] {SharpHook.Native.KeyCode.VcF17.ToString()}}, + {Key.F18.ToString(), new string[] {SharpHook.Native.KeyCode.VcF18.ToString()}}, + {Key.F19.ToString(), new string[] {SharpHook.Native.KeyCode.VcF19.ToString()}}, + {Key.F20.ToString(), new string[] {SharpHook.Native.KeyCode.VcF20.ToString()}}, + {Key.F21.ToString(), new string[] {SharpHook.Native.KeyCode.VcF21.ToString()}}, + {Key.F22.ToString(), new string[] {SharpHook.Native.KeyCode.VcF22.ToString()}}, + {Key.F23.ToString(), new string[] {SharpHook.Native.KeyCode.VcF23.ToString()}}, + {Key.F24.ToString(), new string[] {SharpHook.Native.KeyCode.VcF24.ToString()}}, + + {Key.Quoteleft.ToString(), new string[] {SharpHook.Native.KeyCode.VcBackQuote.ToString()}}, + {Key.Key0.ToString(), new string[] {SharpHook.Native.KeyCode.Vc0.ToString()}}, + {Key.Key1.ToString(), new string[] {SharpHook.Native.KeyCode.Vc1.ToString()}}, + {Key.Key2.ToString(), new string[] {SharpHook.Native.KeyCode.Vc2.ToString()}}, + {Key.Key3.ToString(), new string[] {SharpHook.Native.KeyCode.Vc3.ToString()}}, + {Key.Key4.ToString(), new string[] {SharpHook.Native.KeyCode.Vc4.ToString()}}, + {Key.Key5.ToString(), new string[] {SharpHook.Native.KeyCode.Vc5.ToString()}}, + {Key.Key6.ToString(), new string[] {SharpHook.Native.KeyCode.Vc6.ToString()}}, + {Key.Key7.ToString(), new string[] {SharpHook.Native.KeyCode.Vc7.ToString()}}, + {Key.Key8.ToString(), new string[] {SharpHook.Native.KeyCode.Vc8.ToString()}}, + {Key.Key9.ToString(), new string[] {SharpHook.Native.KeyCode.Vc9.ToString()}}, + {Key.Minus.ToString(), new string[] {SharpHook.Native.KeyCode.VcMinus.ToString()}}, + {Key.Equal.ToString(), new string[] {SharpHook.Native.KeyCode.VcEquals.ToString()}}, + {Key.Backspace.ToString(), new string[] {SharpHook.Native.KeyCode.VcBackspace.ToString()}}, + + {Key.Tab.ToString(), new string[] {SharpHook.Native.KeyCode.VcTab.ToString()}}, + {Key.Capslock.ToString(), new string[] {SharpHook.Native.KeyCode.VcCapsLock.ToString()}}, + + {Key.A.ToString(), new string[] {SharpHook.Native.KeyCode.VcA.ToString()}}, + {Key.B.ToString(), new string[] {SharpHook.Native.KeyCode.VcB.ToString()}}, + {Key.C.ToString(), new string[] {SharpHook.Native.KeyCode.VcC.ToString()}}, + {Key.D.ToString(), new string[] {SharpHook.Native.KeyCode.VcD.ToString()}}, + {Key.E.ToString(), new string[] {SharpHook.Native.KeyCode.VcE.ToString()}}, + {Key.F.ToString(), new string[] {SharpHook.Native.KeyCode.VcF.ToString()}}, + {Key.G.ToString(), new string[] {SharpHook.Native.KeyCode.VcG.ToString()}}, + {Key.H.ToString(), new string[] {SharpHook.Native.KeyCode.VcH.ToString()}}, + {Key.I.ToString(), new string[] {SharpHook.Native.KeyCode.VcI.ToString()}}, + {Key.J.ToString(), new string[] {SharpHook.Native.KeyCode.VcJ.ToString()}}, + {Key.K.ToString(), new string[] {SharpHook.Native.KeyCode.VcK.ToString()}}, + {Key.L.ToString(), new string[] {SharpHook.Native.KeyCode.VcL.ToString()}}, + {Key.M.ToString(), new string[] {SharpHook.Native.KeyCode.VcM.ToString()}}, + {Key.N.ToString(), new string[] {SharpHook.Native.KeyCode.VcN.ToString()}}, + {Key.O.ToString(), new string[] {SharpHook.Native.KeyCode.VcO.ToString()}}, + {Key.P.ToString(), new string[] {SharpHook.Native.KeyCode.VcP.ToString()}}, + {Key.Q.ToString(), new string[] {SharpHook.Native.KeyCode.VcQ.ToString()}}, + {Key.R.ToString(), new string[] {SharpHook.Native.KeyCode.VcR.ToString()}}, + {Key.S.ToString(), new string[] {SharpHook.Native.KeyCode.VcS.ToString()}}, + {Key.T.ToString(), new string[] {SharpHook.Native.KeyCode.VcT.ToString()}}, + {Key.U.ToString(), new string[] {SharpHook.Native.KeyCode.VcU.ToString()}}, + {Key.V.ToString(), new string[] {SharpHook.Native.KeyCode.VcV.ToString()}}, + {Key.W.ToString(), new string[] {SharpHook.Native.KeyCode.VcW.ToString()}}, + {Key.X.ToString(), new string[] {SharpHook.Native.KeyCode.VcX.ToString()}}, + {Key.Y.ToString(), new string[] {SharpHook.Native.KeyCode.VcY.ToString()}}, + {Key.Z.ToString(), new string[] {SharpHook.Native.KeyCode.VcZ.ToString()}}, + + {Key.Bracketleft.ToString(), new string[] {SharpHook.Native.KeyCode.VcOpenBracket.ToString()}}, + {Key.Bracketright.ToString(), new string[] {SharpHook.Native.KeyCode.VcCloseBracket.ToString()}}, + {Key.Backslash.ToString(), new string[] {SharpHook.Native.KeyCode.VcBackslash.ToString()}}, + {Key.Semicolon.ToString(), new string[] {SharpHook.Native.KeyCode.VcSemicolon.ToString()}}, + {Key.Quotedbl.ToString(), new string[] {SharpHook.Native.KeyCode.VcQuote.ToString()}}, + {Key.Enter.ToString(), new string[] {SharpHook.Native.KeyCode.VcEnter.ToString()}}, + {Key.Comma.ToString(), new string[] {SharpHook.Native.KeyCode.VcComma.ToString()}}, + {Key.Period.ToString(), new string[] {SharpHook.Native.KeyCode.VcPeriod.ToString()}}, + {Key.Slash.ToString(), new string[] {SharpHook.Native.KeyCode.VcSlash.ToString()}}, + {Key.Space.ToString(), new string[] {SharpHook.Native.KeyCode.VcSpace.ToString()}}, + + {Key.Print.ToString(), new string[] {SharpHook.Native.KeyCode.VcPrintScreen.ToString()}}, + {Key.Insert.ToString(), new string[] {SharpHook.Native.KeyCode.VcInsert.ToString()}}, + {Key.Home.ToString(), new string[] {SharpHook.Native.KeyCode.VcHome.ToString()}}, + {Key.Pageup.ToString(), new string[] {SharpHook.Native.KeyCode.VcPageUp.ToString()}}, + {Key.Delete.ToString(), new string[] {SharpHook.Native.KeyCode.VcDelete.ToString()}}, + {Key.End.ToString(), new string[] {SharpHook.Native.KeyCode.VcEnd.ToString()}}, + {Key.Pagedown.ToString(), new string[] {SharpHook.Native.KeyCode.VcPageDown.ToString()}}, + + {Key.Down.ToString(), new string[] {SharpHook.Native.KeyCode.VcDown.ToString()}}, + {Key.Up.ToString(), new string[] {SharpHook.Native.KeyCode.VcUp.ToString()}}, + {Key.Right.ToString(), new string[] {SharpHook.Native.KeyCode.VcRight.ToString()}}, + {Key.Left.ToString(), new string[] {SharpHook.Native.KeyCode.VcLeft.ToString()}}, + + {Key.Numlock.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumLock.ToString()}}, + {Key.Kp0.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad0.ToString()}}, + {Key.Kp1.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad1.ToString()}}, + {Key.Kp2.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad2.ToString()}}, + {Key.Kp3.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad3.ToString()}}, + {Key.Kp4.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad4.ToString()}}, + {Key.Kp5.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad5.ToString()}}, + {Key.Kp6.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad6.ToString()}}, + {Key.Kp7.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad7.ToString()}}, + {Key.Kp8.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad8.ToString()}}, + {Key.Kp9.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPad9.ToString()}}, + {Key.KpDivide.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPadDivide.ToString()}}, + {Key.KpMultiply.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPadMultiply.ToString()}}, + {Key.KpSubtract.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPadSubtract.ToString()}}, + {Key.KpAdd.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPadAdd.ToString()}}, + {Key.KpPeriod.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPadDecimal.ToString()}}, + {Key.KpEnter.ToString(), new string[] {SharpHook.Native.KeyCode.VcNumPadEnter.ToString()}}, + + {Key.Shift.ToString(), new string[] {SharpHook.Native.KeyCode.VcLeftShift.ToString(), SharpHook.Native.KeyCode.VcRightShift.ToString()}}, + {Key.Ctrl.ToString(), new string[] {SharpHook.Native.KeyCode.VcLeftControl.ToString(), SharpHook.Native.KeyCode.VcRightControl.ToString()}}, + {Key.Alt.ToString(), new string[] {SharpHook.Native.KeyCode.VcLeftAlt.ToString(), SharpHook.Native.KeyCode.VcRightAlt.ToString()}}, + {Key.Meta.ToString(), new string[] {SharpHook.Native.KeyCode.VcLeftMeta.ToString(), SharpHook.Native.KeyCode.VcRightMeta.ToString()}}, + + {"Mouse" + MouseButton.Left.ToString(), new string[] {SharpHook.Native.MouseButton.Button1.ToString()}}, + {"Mouse" + MouseButton.Right.ToString(), new string[] {SharpHook.Native.MouseButton.Button2.ToString()}}, + {"Mouse" + MouseButton.Middle.ToString(), new string[] {SharpHook.Native.MouseButton.Button3.ToString()}}, + {"Mouse" + MouseButton.Xbutton1.ToString(), new string[] {SharpHook.Native.MouseButton.Button4.ToString()}}, + {"Mouse" + MouseButton.Xbutton2.ToString(), new string[] {SharpHook.Native.MouseButton.Button5.ToString()}}, + + {"Mouse" + MouseButton.WheelUp.ToString(), new string[] {"WheelUp"}}, + {"Mouse" + MouseButton.WheelDown.ToString(), new string[] {"WheelDown"}}, + {"Mouse" + MouseButton.WheelLeft.ToString(), new string[] {"WheelLeft"}}, + {"Mouse" + MouseButton.WheelRight.ToString(), new string[] {"WheelRight"}}, + }; + + Dictionary SharpKeyState = new(); + Dictionary>> ActionDictionary = new(); + + static string OverallInputEventName = "Overall"; + #endregion + + #region Setup Methods + private void InitializeSharpKeyState() + { + string[] sharpKeyNames = Enum.GetNames(typeof(SharpHook.Native.KeyCode)); + foreach (string sharpKeyName in sharpKeyNames) SharpKeyState.Add(sharpKeyName, false); + + string[] sharpMouseButtonNames = Enum.GetNames(typeof(SharpHook.Native.MouseButton)); + foreach (string sharpMouseButtonName in sharpMouseButtonNames) SharpKeyState.Add(sharpMouseButtonName, false); + + SharpKeyState.Add("WheelUp", false); + SharpKeyState.Add("WheelDown", false); + SharpKeyState.Add("WheelLeft", false); + SharpKeyState.Add("WheelRight", false); + } + + private void InitializeActionDictionary() + { + foreach (string actionName in InputMap.GetActions()) + { + ActionDictionary.Add(actionName, new Dictionary>()); + + foreach (InputEvent inputEvent in InputMap.ActionGetEvents(actionName)) + { + string inputEventName = GetEventName(inputEvent); + if (!ActionDictionary[actionName].ContainsKey(inputEventName)) ActionDictionary[actionName].Add(inputEventName, new Dictionary() + { + {"justPressedPrevState", false}, + {"justPressedState", false}, + {"pressedPrevState", false}, + {"pressedState", false}, + {"justReleasedPrevState", false}, + {"justReleasedState", false} + }); + } + + if (ActionDictionary[actionName].ContainsKey(OverallInputEventName)) continue; + + ActionDictionary[actionName].Add(OverallInputEventName, new Dictionary() + { + {"justPressedPrevState", false}, + {"justPressedState", false}, + {"pressedPrevState", false}, + {"pressedState", false}, + {"justReleasedPrevState", false}, + {"justReleasedState", false} + }); + } + } + + private void InitializeSignals() + { + Hook.KeyPressed += OnHookKeyPressed; + Hook.KeyReleased += OnHookKeyReleased; + + Hook.MousePressed += OnHookMouseButtonPressed; + Hook.MouseReleased += OnHookMouseButtonReleased; + + Hook.MouseWheel += OnHookMouseWheel; + } + #endregion + + #region Sharp Hook Signal Handlers + private void OnHookKeyPressed(object sender, KeyboardHookEventArgs e) + { + string[] sharpKeyNames = MapGodotKeyToSharpKey( + MapSharpKeyToGodotKey(e.RawEvent.Keyboard.KeyCode.ToString()) + ); + + UpdateSharpKeyState(sharpKeyNames, true); + } + + private void OnHookKeyReleased(object sender, KeyboardHookEventArgs e) + { + string[] sharpKeyNames = MapGodotKeyToSharpKey( + MapSharpKeyToGodotKey(e.RawEvent.Keyboard.KeyCode.ToString()) + ); + + UpdateSharpKeyState(sharpKeyNames, false); + } + + private void OnHookMouseButtonPressed(object sender, MouseHookEventArgs e) + { + string[] sharpKeyNames = MapGodotKeyToSharpKey( + MapSharpKeyToGodotKey(e.RawEvent.Mouse.Button.ToString()) + ); + + UpdateSharpKeyState(sharpKeyNames, true); + } + + private void OnHookMouseButtonReleased(object sender, MouseHookEventArgs e) + { + string[] sharpKeyNames = MapGodotKeyToSharpKey( + MapSharpKeyToGodotKey(e.RawEvent.Mouse.Button.ToString()) + ); + + UpdateSharpKeyState(sharpKeyNames, false); + } + + private void OnHookMouseWheel(object sender, MouseWheelHookEventArgs e) + { + SharpHook.Native.MouseWheelScrollDirection direction = e.RawEvent.Wheel.Direction; + int rotation = e.RawEvent.Wheel.Rotation; + if (direction == SharpHook.Native.MouseWheelScrollDirection.Vertical) + { + if (rotation > 0) + { + UpdateSharpKeyState("WheelUp", true); + } + else if (rotation < 0) + { + UpdateSharpKeyState("WheelDown", true); + } + } + else if (direction == SharpHook.Native.MouseWheelScrollDirection.Horizontal) + { + if (rotation > 0) + { + UpdateSharpKeyState("WheelLeft", true); + } + else if (rotation < 0) + { + UpdateSharpKeyState("WheelRight", true); + } + } + } + #endregion + + #region Helper Functions + private static string GetEventName(InputEvent inputEvent) + { + string eventName = ""; + switch(inputEvent) + { + case InputEventMouse inputEventMouse: + eventName = inputEventMouse.AsText(); + break; + case InputEventKey inputEventKey: + eventName = inputEventKey.AsText().Replace("(Physical)", "").Trim(); + break; + } + return eventName; + } + + private static string GetEventGodotKey(InputEvent inputEvent) + { + string godotKey = ""; + switch (inputEvent) + { + case InputEventMouseButton inputEventMouseButton: + godotKey = "Mouse" + inputEventMouseButton.ButtonIndex.ToString(); + break; + case InputEventKey inputEventKey: + godotKey = inputEventKey.PhysicalKeycode.ToString().Replace("(Physical)", "").Trim(); + if (godotKey == "") godotKey = inputEventKey.Keycode.ToString().Trim(); + break; + } + return godotKey; + } + + private bool GetEventState(InputEvent inputEvent) + { + bool state = false; + + string godotKeyName = GetEventGodotKey(inputEvent); + string[] sharpKeynames = MapGodotKeyToSharpKey(godotKeyName); + foreach (string sharpKeyName in sharpKeynames) + { + if (SharpKeyState[sharpKeyName]) state = true; + } + + return state; + } + + private string[] MapGodotKeyToSharpKey(string godotKey) + { + string[] sharpKeyNames = new string[0]; + if (GodotKeyToSharpKey.ContainsKey(godotKey)) + { + sharpKeyNames = GodotKeyToSharpKey[godotKey]; + } + + return sharpKeyNames; + } + + private string MapSharpKeyToGodotKey(string sharpKey) + { + string godotKey = ""; + foreach (string godotKeyName in GodotKeyToSharpKey.Keys){ + string[] sharpKeyNames = GodotKeyToSharpKey[godotKeyName]; + foreach (string sharpKeyName in sharpKeyNames) + { + if (sharpKeyName == sharpKey) { + godotKey = godotKeyName; + } + } + + if (godotKey != "") break; + } + return godotKey; + } + + private bool IsEventModifierPressed(KeyModifierMask keyModifierMask) + { + bool keyModifierState = true; + if (keyModifierMask > 0) + { + string keyModifierString = keyModifierMask.ToString().Replace("Mask", ""); + string[] keyModifierStringArray = keyModifierString.Split(", "); + foreach (string keyModifier in keyModifierStringArray) + { + string[] sharpKeyNames = MapGodotKeyToSharpKey(keyModifier); + + foreach (string sharpKeyName in sharpKeyNames) + { + if (!SharpKeyState[sharpKeyName]) keyModifierState = false; + } + } + } + return keyModifierState; + } + + private void UpdateActionDictionary(string action) + { + // Add action if it doesn't exist + if (!ActionDictionary.ContainsKey(action)) + { + ActionDictionary.Add(action, new Dictionary>()); + } + + // Add events if they don't exist in the dictionary + Dictionary> actionDict = ActionDictionary[action]; + + InputMap.ActionGetEvents(action).All((inputEvent) => { + if (!actionDict.ContainsKey(GetEventName(inputEvent))) actionDict.Add(GetEventName(inputEvent), new Dictionary() + { + {"justPressedPrevState", false}, + {"justPressedState", false}, + {"pressedPrevState", false}, + {"pressedState", false}, + {"justReleasedPrevState", false}, + {"justReleasedState", false} + }); + return true; + }); + + // Remove events if they don't exist in the input map + foreach (string eventKey in actionDict.Keys) + { + string[] inputEventNames = InputMap.ActionGetEvents(action).Select((inputEvent) => GetEventName(inputEvent)).ToArray(); + if (!inputEventNames.Contains(eventKey) && eventKey != OverallInputEventName) + { + actionDict.Remove(eventKey); + } + } + + // Add overall event + if (actionDict.ContainsKey(OverallInputEventName)) return; + + actionDict.Add(OverallInputEventName, new Dictionary() + { + {"justPressedPrevState", false}, + {"justPressedState", false}, + {"pressedPrevState", false}, + {"pressedState", false}, + {"justReleasedPrevState", false}, + {"justReleasedState", false} + }); + } + + + private void UpdateAction(string action, string stateString, string prevStateString) + { + UpdateActionDictionary(action); + + bool hasWheel = false; + int eventCount = 0; + int eventLength = InputMap.ActionGetEvents(action).Count; + System.Collections.Generic.List wheelEventNames = new System.Collections.Generic.List(); + foreach (InputEvent inputEvent in InputMap.ActionGetEvents(action)) + { + string inputEventType = null; + string inputEventName = null; + KeyModifierMask? inputEventModifierMask = null; + switch (inputEvent) + { + case InputEventMouse inputEventMouse: + inputEventType = "MouseButton"; + inputEventName = GetEventName(inputEventMouse); + inputEventModifierMask = inputEventMouse.GetModifiersMask(); + break; + case InputEventKey inputEventKey: + inputEventType = "Key"; + inputEventName = GetEventName(inputEventKey); + inputEventModifierMask = inputEventKey.GetModifiersMask(); + break; + } + + if (inputEventType == null) continue; + + Dictionary eventStateDictionary = ActionDictionary[action][inputEventName]; + bool eventModifierState = IsEventModifierPressed((KeyModifierMask) inputEventModifierMask); + + eventStateDictionary[prevStateString] = eventStateDictionary[stateString]; + eventStateDictionary[stateString] = GetEventState(inputEvent) && eventModifierState; + + UpdateOverallEventDictionary(action); + + if (GetEventName(inputEvent).Contains("Wheel")) + { + hasWheel = true; + wheelEventNames.Add(GetEventName(inputEvent)); + } + + if (eventCount == eventLength - 1 && hasWheel) + { + // GD.Print(wheelEventNames); + foreach (string wheelEventName in wheelEventNames) + { + Dictionary wheelEventStateDictionary = ActionDictionary[action][wheelEventName]; + if (wheelEventName.Contains("Wheel Up")) { + UpdateSharpKeyState("WheelUp", false); + wheelEventStateDictionary[stateString] = false; + } + if (wheelEventName.Contains("Wheel Down")) { + UpdateSharpKeyState("WheelDown", false); + wheelEventStateDictionary[stateString] = false; + } + if (wheelEventName.Contains("Wheel Left")) { + UpdateSharpKeyState("WheelLeft", false); + wheelEventStateDictionary[stateString] = false; + } + if (wheelEventName.Contains("Wheel Right")) { + UpdateSharpKeyState("WheelRight", false); + wheelEventStateDictionary[stateString] = false; + } + } + } + eventCount++; + } + } + + private void UpdateSharpKeyState(string sharpKeyName, bool state) + { + if (SharpKeyState.ContainsKey(sharpKeyName)) + { + SharpKeyState[sharpKeyName] = state; + } + } + + private void UpdateSharpKeyState(string[] sharpKeyNames, bool state) + { + foreach (string sharpKeyName in sharpKeyNames) + { + UpdateSharpKeyState(sharpKeyName, state); + } + } + + private void UpdateOverallEventDictionary(string actionName) + { + Dictionary overallEventStateDictionary = ActionDictionary[actionName]["Overall"]; + + bool justPressedPrevState = false; + bool justPressedState = false; + bool pressedPrevState = false; + bool pressedState = false; + bool justReleasedPrevState = false; + bool justReleasedState = false; + + foreach (InputEvent inputEvent in InputMap.ActionGetEvents(actionName)) + { + string eventType = null; + string eventName = null; + + switch (inputEvent) + { + case InputEventMouseButton eventMouseButton: + eventType = "MouseButton"; + eventName = GetEventName(eventMouseButton); + break; + case InputEventKey eventKey: + eventType = "Key"; + eventName = GetEventName(eventKey); + break; + } + + if (eventType == null) continue; + + Dictionary eventStateDictionary = ActionDictionary[actionName][eventName]; + if (eventStateDictionary["justPressedPrevState"]) justPressedPrevState = true; + if (eventStateDictionary["justPressedState"]) justPressedState = true; + if (eventStateDictionary["pressedPrevState"]) pressedPrevState = true; + if (eventStateDictionary["pressedState"]) pressedState = true; + if (eventStateDictionary["justReleasedPrevState"]) justReleasedPrevState = true; + if (eventStateDictionary["justReleasedState"]) justReleasedState = true; + } + + overallEventStateDictionary["justPressedPrevState"] = justPressedPrevState; + overallEventStateDictionary["justPressedState"] = justPressedState; + overallEventStateDictionary["pressedPrevState"] = pressedPrevState; + overallEventStateDictionary["pressedState"] = pressedState; + overallEventStateDictionary["justReleasedPrevState"] = justReleasedPrevState; + overallEventStateDictionary["justReleasedState"] = justReleasedState; + } + + #endregion + + Node2D MousePositionNode2D; + Vector2? screen_min = null; + Vector2? screen_max = null; + public override void _Ready() + { + for (int i = 0; i < DisplayServer.GetScreenCount(); i++) + { + var pos = DisplayServer.ScreenGetPosition(i) - DisplayServer.ScreenGetPosition((int)DisplayServer.ScreenPrimary); + var size = DisplayServer.ScreenGetSize(i); + if (screen_min == null) + { + screen_min = pos; + } + else { + if (pos.X < ((Vector2)screen_min).X) screen_min = new Vector2(pos.X, ((Vector2)screen_min).Y); + if (pos.Y < ((Vector2)screen_min).Y) screen_min = new Vector2(((Vector2)screen_min).X, pos.Y); + } + + if (screen_max == null) + { + screen_max = new Vector2(pos.X + size.X, pos.Y + size.Y); + } + else + { + if (pos.X + size.X > ((Vector2)screen_max).X) screen_max = new Vector2(pos.X + size.X, ((Vector2)screen_max).Y); + if (pos.Y + size.Y > ((Vector2)screen_max).Y) screen_max = new Vector2(((Vector2)screen_max).X, pos.Y + size.Y); + } + } + MousePositionNode2D = new(); + AddChild(MousePositionNode2D); + InitializeSharpKeyState(); + InitializeActionDictionary(); + InitializeSignals(); + + Hook.RunAsync(); + } + + public override void _Input(InputEvent inputEvent) + { + if (inputEvent is InputEventKey inputEventKey) + { + if (inputEventKey.IsPressed()) + { + string[] sharpKeyNames = MapGodotKeyToSharpKey(GetEventGodotKey(inputEventKey)); + UpdateSharpKeyState(sharpKeyNames, true); + } + else if (inputEventKey.IsReleased()) + { + string[] sharpKeyNames = MapGodotKeyToSharpKey(GetEventGodotKey(inputEventKey)); + UpdateSharpKeyState(sharpKeyNames, false); + } + } + GC.Collect(); + } + + #region Godot Input Methods + public bool IsActionJustPressed(string action) + { + string stateString = "justPressedState"; + string prevStateString = "justPressedPrevState"; + UpdateAction(action, stateString, prevStateString); + Dictionary overallEventStateDictionary = ActionDictionary[action][OverallInputEventName]; + bool prevState = overallEventStateDictionary[prevStateString]; + bool state = overallEventStateDictionary[stateString]; + if (!prevState && state) return true; + return false; + } + + public bool IsActionPressed(string action) + { + string stateString = "pressedState"; + string prevStateString = "pressedPrevState"; + UpdateAction(action, stateString, prevStateString); + Dictionary overallEventStateDictionary = ActionDictionary[action][OverallInputEventName]; + bool prevState = overallEventStateDictionary[prevStateString]; + bool state = overallEventStateDictionary[stateString]; + if (prevState && state) return true; + return false; + } + + public bool IsActionJustReleased(string action) + { + string stateString = "justReleasedState"; + string prevStateString = "justReleasedPrevState"; + UpdateAction(action, stateString, prevStateString); + Dictionary overallEventStateDictionary = ActionDictionary[action][OverallInputEventName]; + bool prevState = overallEventStateDictionary[prevStateString]; + bool state = overallEventStateDictionary[stateString]; + if (prevState && !state) return true; + return false; + } + + public Vector2 GetVector(StringName negativeX, StringName positiveX, StringName negativeY, StringName positiveY){ + Vector2 inputVector = new Vector2 + { + X = (IsActionPressed(positiveX) ? 1 : 0) - (IsActionPressed(negativeX) ? 1 : 0), + Y = (IsActionPressed(positiveY) ? 1 : 0) - (IsActionPressed(negativeY) ? 1 : 0) + }; + return inputVector; + } + + public bool IsKeyPressed(Key key) + { + string[] sharpKeyNames = MapGodotKeyToSharpKey(key.ToString()); + if (sharpKeyNames.Length == 0) return false; + foreach (string sharpKeyName in sharpKeyNames) + { + if (SharpKeyState[sharpKeyName]) return true; + } + return false; + } + + public bool IsAnythingPressed(){ + foreach (string actionName in InputMap.GetActions()){ + if (IsActionPressed(actionName)) return true; + } + return false; + } + + + public Vector2 GetMousePosition() { + return (Vector2)(MousePositionNode2D.GetGlobalMousePosition() + DisplayServer.WindowGetPosition() + screen_min); + } + #endregion +} diff --git a/plugins/GlobalInput/GlobalInputGDScript.tscn b/plugins/GlobalInput/GlobalInputGDScript.tscn new file mode 100644 index 0000000..3cc150a --- /dev/null +++ b/plugins/GlobalInput/GlobalInputGDScript.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://iejpghgiofrp"] + +[ext_resource type="Script" path="res://plugins/GlobalInput/global_input_gd_script.gd" id="1_nf3vv"] + +[node name="GlobalInputGdScript" type="Node"] +script = ExtResource("1_nf3vv") diff --git a/plugins/GlobalInput/global_input_gd_script.gd b/plugins/GlobalInput/global_input_gd_script.gd new file mode 100644 index 0000000..000e7bb --- /dev/null +++ b/plugins/GlobalInput/global_input_gd_script.gd @@ -0,0 +1,57 @@ +#region Header +#01. tool + +#02. class_name + +#03. extends +extends Node +#endregion + +#region Documentation +#----------------------------------------------------------- +#04. # docstring +## hoge +#----------------------------------------------------------- +#endregion + +#region Body +#05. signals +#----------------------------------------------------------- + +#----------------------------------------------------------- +#06. enums +#----------------------------------------------------------- + +#----------------------------------------------------------- +#08. variables +#----------------------------------------------------------- +var global_input_csharp = preload("res://plugins/GlobalInput/GlobalInputCSharp.cs") +var global_input = global_input_csharp.new() +#----------------------------------------------------------- +#09. methods +#----------------------------------------------------------- + +func _ready() -> void: + add_child(global_input) + print("GLOBAL KEY INPUT READY") + +func is_action_just_pressed(action: StringName) -> bool: + return global_input.IsActionJustPressed(action) + +func is_action_pressed(action: StringName) -> bool: + return global_input.IsActionPressed(action) + +func is_action_just_released(action: StringName) -> bool: + return global_input.IsActionJustReleased(action) + +func is_key_pressed(key: int) -> bool: + return global_input.IsKeyPressed(key) + +func get_vector(negative_x, positive_x, negative_y, positive_y) -> Vector2: + return global_input.GetVector(negative_x, positive_x, negative_y, positive_y) + +func get_mouse_position() -> Vector2: + return global_input.GetMousePosition() +#----------------------------------------------------------- +#10. signal methods +#----------------------------------------------------------- diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..1de60b3 --- /dev/null +++ b/project.godot @@ -0,0 +1,106 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="FunkPanion" +config/version="0.1.0" +run/main_scene="res://scenes/main.tscn" +run/disable_stderr=true +config/use_custom_user_dir=true +config/custom_user_dir_name="FunkPanion" +config/quit_on_go_back=false +config/features=PackedStringArray("4.3", "C#", "Forward Plus") +run/max_fps=60 +boot_splash/bg_color=Color(0.254902, 0.282353, 0.298039, 1) +boot_splash/image="res://icon.png" +boot_splash/fullsize=false +boot_splash/use_filter=false +config/icon="res://icon.png" +boot_splash/minimum_display_time=2000 + +[autoload] + +GlobalInput="*res://plugins/GlobalInput/GlobalInputGDScript.tscn" +GlobalConfig="*res://scenes/global_config.tscn" + +[display] + +window/size/viewport_width=640 +window/size/viewport_height=480 +window/size/initial_position_type=3 +window/size/borderless=true +window/size/always_on_top=true +window/size/transparent=true +window/energy_saving/keep_screen_on=false +window/per_pixel_transparency/allowed=true +window/handheld/orientation=6 +window/vsync/vsync_mode=2 + +[dotnet] + +project/assembly_name="FunkPanion" + +[editor] + +version_control/plugin_name="GitPlugin" +version_control/autoload_on_startup=true + +[gui] + +theme/custom_font="res://assets/fonts/Lexend.ttf" + +[input] + +bpm_increase={ +"deadzone": 0.5, +"events": [] +} +bpm_decrease={ +"deadzone": 0.5, +"events": [] +} +bpm_tsincrease={ +"deadzone": 0.5, +"events": [] +} +bpm_tsdecrease={ +"deadzone": 0.5, +"events": [] +} +bpm_opaincrease={ +"deadzone": 0.5, +"events": [] +} +bpm_opadecrease={ +"deadzone": 0.5, +"events": [] +} +bpm_reset={ +"deadzone": 0.5, +"events": [] +} +mod_enable={ +"deadzone": 0.5, +"events": [] +} +mod_snap_fine={ +"deadzone": 0.5, +"events": [] +} +mod_snap_coarse={ +"deadzone": 0.5, +"events": [] +} + +[rendering] + +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" diff --git a/scenes/config.tscn b/scenes/config.tscn new file mode 100644 index 0000000..e39df68 --- /dev/null +++ b/scenes/config.tscn @@ -0,0 +1,605 @@ +[gd_scene load_steps=13 format=3 uid="uid://b0m4v3w4sqrfs"] + +[ext_resource type="Script" path="res://scripts/config/config_window.gd" id="1_3y2tp"] +[ext_resource type="LabelSettings" uid="uid://2iu8q0tylumo" path="res://assets/config/header_bold.tres" id="1_lbm3n"] +[ext_resource type="FontFile" uid="uid://ce5fs0ro7kp8r" path="res://assets/fonts/Lexend.ttf" id="2_bbfo5"] +[ext_resource type="Script" path="res://scripts/config/form_handler.gd" id="2_ouac2"] +[ext_resource type="FontFile" uid="uid://4e3o2ds5xy7p" path="res://assets/fonts/MatSymOut.ttf" id="2_pm82g"] +[ext_resource type="LabelSettings" uid="uid://c2s3eclr25ots" path="res://assets/config/body.tres" id="3_gn7vd"] +[ext_resource type="FontFile" uid="uid://c6ck52v6qotm" path="res://assets/fonts/B_Lexend.ttf" id="3_qguou"] +[ext_resource type="FontVariation" uid="uid://d1apfvux6r82v" path="res://assets/config/button_icon.tres" id="8_qusr6"] +[ext_resource type="Script" path="res://scripts/config/open_file_dialog.gd" id="9_1o6y4"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3ax2o"] +bg_color = Color(0.12549, 0.12549, 0.12549, 1) + +[sub_resource type="LabelSettings" id="LabelSettings_gk6wd"] +font = ExtResource("3_qguou") +font_size = 32 + +[sub_resource type="LabelSettings" id="LabelSettings_2l8nx"] +font = ExtResource("2_bbfo5") +font_size = 14 +font_color = Color(1, 1, 1, 0.501961) + +[node name="ScrollContainer" type="ScrollContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_3ax2o") +follow_focus = true +horizontal_scroll_mode = 0 +script = ExtResource("1_3y2tp") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="Form" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 8 +script = ExtResource("2_ouac2") + +[node name="Header" type="HBoxContainer" parent="MarginContainer/Form"] +layout_mode = 2 + +[node name="BackButton" type="Button" parent="MarginContainer/Form/Header"] +custom_minimum_size = Vector2(40, 40) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Save and exit." +theme_override_fonts/font = ExtResource("2_pm82g") +theme_override_font_sizes/font_size = 24 +text = "arrow_back" +flat = true + +[node name="Header" type="Label" parent="MarginContainer/Form/Header"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Config" +label_settings = SubResource("LabelSettings_gk6wd") + +[node name="Reset" type="Button" parent="MarginContainer/Form/Header"] +custom_minimum_size = Vector2(40, 40) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "Save and exit." +theme_override_fonts/font = ExtResource("2_pm82g") +theme_override_font_sizes/font_size = 24 +text = "reset_settings" + +[node name="OSD and UI" type="VBoxContainer" parent="MarginContainer/Form"] +layout_mode = 2 + +[node name="Header" type="Label" parent="MarginContainer/Form/OSD and UI"] +layout_mode = 2 +text = "OSD and UI" +label_settings = ExtResource("1_lbm3n") + +[node name="Tooltip" type="Label" parent="MarginContainer/Form/OSD and UI"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +mouse_filter = 0 +text = "Configure FunkPanion's user interface." +label_settings = SubResource("LabelSettings_2l8nx") +vertical_alignment = 1 + +[node name="Controls" type="MarginContainer" parent="MarginContainer/Form/OSD and UI"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/Form/OSD and UI/Controls"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="osd_dwell_time" type="HBoxContainer" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/osd_dwell_time"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "How long the OSD will stay visible for." +mouse_filter = 0 +text = "OSD Dwell Time" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="input" type="SpinBox" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/osd_dwell_time"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +min_value = 100.0 +max_value = 10000.0 +step = 100.0 +value = 1000.0 +suffix = "ms" +select_all_on_focus = true + +[node name="osd_fade_time" type="HBoxContainer" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/osd_fade_time"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "How long the OSD takes to fade out." +mouse_filter = 0 +text = "OSD Fade Time" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="input" type="SpinBox" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/osd_fade_time"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +min_value = 100.0 +max_value = 10000.0 +step = 100.0 +value = 1000.0 +suffix = "ms" +select_all_on_focus = true + +[node name="osd_opacity" type="HBoxContainer" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/osd_opacity"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "The opacity of the OSD." +mouse_filter = 0 +text = "OSD Opacity" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="value" type="Label" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/osd_opacity"] +layout_mode = 2 +text = "100%" + +[node name="input" type="HSlider" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/osd_opacity"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +max_value = 1.0 +step = 0.01 +value = 1.0 + +[node name="menu_opacity" type="HBoxContainer" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/menu_opacity"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "The opacity of the menu button." +mouse_filter = 0 +text = "Menu Opacity" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="value" type="Label" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/menu_opacity"] +layout_mode = 2 +text = "100%" + +[node name="input" type="HSlider" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/menu_opacity"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +max_value = 1.0 +step = 0.01 +value = 1.0 + +[node name="background_opacity" type="HBoxContainer" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/background_opacity"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "The opacity of the application's background." +mouse_filter = 0 +text = "Background Opacity" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="value" type="Label" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/background_opacity"] +layout_mode = 2 +text = "100%" + +[node name="input" type="HSlider" parent="MarginContainer/Form/OSD and UI/Controls/VBoxContainer/background_opacity"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +max_value = 1.0 +step = 0.01 +value = 1.0 + +[node name="Tempo" type="VBoxContainer" parent="MarginContainer/Form"] +layout_mode = 2 + +[node name="Header" type="Label" parent="MarginContainer/Form/Tempo"] +layout_mode = 2 +text = "Tempo" +label_settings = ExtResource("1_lbm3n") + +[node name="Controls" type="MarginContainer" parent="MarginContainer/Form/Tempo"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/Form/Tempo/Controls"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="tempo_default" type="HBoxContainer" parent="MarginContainer/Form/Tempo/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/Tempo/Controls/VBoxContainer/tempo_default"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "The tempo that the application starts with." +mouse_filter = 0 +text = "Default Tempo" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="input" type="SpinBox" parent="MarginContainer/Form/Tempo/Controls/VBoxContainer/tempo_default"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +min_value = 60.0 +max_value = 10000.0 +value = 120.0 +suffix = "bpm" +custom_arrow_step = 5.0 +select_all_on_focus = true + +[node name="tempo_numerator" type="HBoxContainer" parent="MarginContainer/Form/Tempo/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/Tempo/Controls/VBoxContainer/tempo_numerator"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "The time signature that the application starts with." +mouse_filter = 0 +text = "Default Time Signature" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="input" type="SpinBox" parent="MarginContainer/Form/Tempo/Controls/VBoxContainer/tempo_numerator"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +min_value = 2.0 +max_value = 16.0 +value = 4.0 +suffix = "/ 4" +select_all_on_focus = true + +[node name="Sprites" type="VBoxContainer" parent="MarginContainer/Form"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/Form/Sprites"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="Header" type="Label" parent="MarginContainer/Form/Sprites/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Sprites" +label_settings = ExtResource("1_lbm3n") + +[node name="ImportButton" type="Button" parent="MarginContainer/Form/Sprites/HBoxContainer"] +visible = false +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +theme_override_fonts/font = ExtResource("8_qusr6") +text = "file_open" + +[node name="ExportButton" type="Button" parent="MarginContainer/Form/Sprites/HBoxContainer"] +visible = false +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +theme_override_fonts/font = ExtResource("8_qusr6") +text = "save" + +[node name="Controls" type="MarginContainer" parent="MarginContainer/Form/Sprites"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/Form/Sprites/Controls"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="spritesheet_image" type="HBoxContainer" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 +script = ExtResource("9_1o6y4") + +[node name="Label" type="Label" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_image"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "A link to a sprite atlas." +mouse_filter = 0 +text = "Spritesheet Image" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="switch" type="Button" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_image"] +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +theme_override_fonts/font = ExtResource("8_qusr6") +text = "folder_open" + +[node name="value" type="LineEdit" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_image"] +layout_mode = 2 +size_flags_horizontal = 3 +editable = false + +[node name="save" type="Button" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_image"] +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "Save the selected file to FunkPanion's application storage." +theme_override_fonts/font = ExtResource("8_qusr6") +text = "upload" + +[node name="spritesheet_data" type="HBoxContainer" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 +script = ExtResource("9_1o6y4") + +[node name="Label" type="Label" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_data"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "A link to the file that provides frames for the sprite atlas." +mouse_filter = 0 +text = "Spritesheet Data" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="switch" type="Button" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_data"] +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +theme_override_fonts/font = ExtResource("8_qusr6") +text = "folder_open" + +[node name="value" type="LineEdit" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_data"] +layout_mode = 2 +size_flags_horizontal = 3 +editable = false + +[node name="save" type="Button" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_data"] +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "Save the selected file to FunkPanion's application storage." +theme_override_fonts/font = ExtResource("8_qusr6") +text = "upload" + +[node name="spritesheet_anims" type="VBoxContainer" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="Label" type="Label" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_anims"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +mouse_filter = 0 +text = "Spritesheet Animations" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="Tree" type="Tree" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_anims"] +custom_minimum_size = Vector2(0, 150) +layout_mode = 2 +size_flags_vertical = 3 +columns = 3 +column_titles_visible = true +allow_search = false +hide_root = true +select_mode = 1 + +[node name="AddNew" type="HBoxContainer" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_anims"] +layout_mode = 2 + +[node name="name" type="LineEdit" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_anims/AddNew"] +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Animation Name" + +[node name="switch" type="Button" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_anims/AddNew"] +layout_mode = 2 +text = "Add" + +[node name="spritesheet_anchor" type="HBoxContainer" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_anchor"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "Where the sprite is anchored. +If the spritesheet frames are different sizes, +this can stop the animation from jittering." +mouse_filter = 0 +text = "Sprite Anchor" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="input" type="OptionButton" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_anchor"] +layout_mode = 2 +selected = 7 +item_count = 9 +popup/item_0/text = "Top Left" +popup/item_1/text = "Top" +popup/item_1/id = 1 +popup/item_2/text = "Top Right" +popup/item_2/id = 2 +popup/item_3/text = "Left" +popup/item_3/id = 3 +popup/item_4/text = "Center" +popup/item_4/id = 4 +popup/item_5/text = "Right" +popup/item_5/id = 5 +popup/item_6/text = "Bottom Left" +popup/item_6/id = 6 +popup/item_7/text = "Bottom" +popup/item_7/id = 7 +popup/item_8/text = "Bottom Right" +popup/item_8/id = 8 + +[node name="sprite_scale" type="HBoxContainer" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/sprite_scale"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "How big the sprite is compared to its default size." +mouse_filter = 0 +text = "Sprite Scale" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="input" type="SpinBox" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/sprite_scale"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +min_value = 0.1 +max_value = 10.0 +step = 0.1 +value = 1.0 +suffix = "x" +custom_arrow_step = 0.5 +select_all_on_focus = true + +[node name="sprite_flip" type="HBoxContainer" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/sprite_flip"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "How big the sprite is compared to its default size." +mouse_filter = 0 +text = "Flip Sprite" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="input" type="CheckButton" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/sprite_flip"] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 + +[node name="spritesheet_default_opacity" type="HBoxContainer" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_default_opacity"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +tooltip_text = "The opacity of the sprite." +mouse_filter = 0 +text = "Default Sprite Opacity" +label_settings = ExtResource("3_gn7vd") +vertical_alignment = 1 + +[node name="value" type="Label" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_default_opacity"] +layout_mode = 2 +text = "100%" + +[node name="input" type="HSlider" parent="MarginContainer/Form/Sprites/Controls/VBoxContainer/spritesheet_default_opacity"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 4 +max_value = 1.0 +step = 0.01 +value = 1.0 + +[node name="Binds" type="VBoxContainer" parent="MarginContainer/Form"] +layout_mode = 2 + +[node name="Header" type="Label" parent="MarginContainer/Form/Binds"] +layout_mode = 2 +text = "Binds" +label_settings = ExtResource("1_lbm3n") + +[node name="Tooltip" type="Label" parent="MarginContainer/Form/Binds"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 1 +mouse_filter = 0 +text = "Configure FunkPanion's keybinds." +label_settings = SubResource("LabelSettings_2l8nx") +vertical_alignment = 1 + +[node name="Controls" type="MarginContainer" parent="MarginContainer/Form/Binds"] +layout_mode = 2 +theme_override_constants/margin_left = 4 +theme_override_constants/margin_top = 4 +theme_override_constants/margin_right = 4 +theme_override_constants/margin_bottom = 4 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/Form/Binds/Controls"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="Tree" type="Tree" parent="MarginContainer/Form/Binds/Controls/VBoxContainer"] +custom_minimum_size = Vector2(0, 300) +layout_mode = 2 +size_flags_vertical = 3 +columns = 3 +allow_search = false +hide_root = true +select_mode = 1 + +[connection signal="pressed" from="MarginContainer/Form/Header/BackButton" to="." method="_on_back_button_pressed"] +[connection signal="pressed" from="MarginContainer/Form/Header/BackButton" to="MarginContainer/Form" method="_on_back_button_pressed"] +[connection signal="pressed" from="MarginContainer/Form/Header/Reset" to="." method="_on_reset_pressed"] diff --git a/scenes/global_config.tscn b/scenes/global_config.tscn new file mode 100644 index 0000000..49a888e --- /dev/null +++ b/scenes/global_config.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://gi5oyfgmhnjn"] + +[ext_resource type="Script" path="res://scripts/GlobalConfigPreInit.gd" id="1_ausyg"] + +[node name="GlobalConfig" type="Node"] +script = ExtResource("1_ausyg") diff --git a/scenes/main.tscn b/scenes/main.tscn new file mode 100644 index 0000000..b17d3b2 --- /dev/null +++ b/scenes/main.tscn @@ -0,0 +1,84 @@ +[gd_scene load_steps=11 format=3 uid="uid://d0hedjnfwtu0"] + +[ext_resource type="Script" path="res://scripts/window.gd" id="1_6nwqw"] +[ext_resource type="Script" path="res://scripts/animator.gd" id="2_hov7x"] +[ext_resource type="PackedScene" uid="uid://qnb6qrsgpbwc" path="res://scenes/osd.tscn" id="2_m7sf8"] +[ext_resource type="AudioStream" uid="uid://bj6b8h153h1bn" path="res://assets/beep.ogg" id="3_tvnah"] +[ext_resource type="AudioStream" uid="uid://8ij8fr2uqb65" path="res://assets/tap.wav" id="3_xnh3p"] +[ext_resource type="Texture2D" uid="uid://b6x537wsl3853" path="res://assets/BOYFRIEND.png" id="4_fraqq"] +[ext_resource type="Theme" uid="uid://e62vyqlhf1v1" path="res://osd.tres" id="7_bx315"] +[ext_resource type="FontFile" uid="uid://4e3o2ds5xy7p" path="res://assets/fonts/MatSymOut.ttf" id="7_q7jy6"] +[ext_resource type="Script" path="res://scripts/menu.gd" id="8_3lc1r"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_y0egr"] +atlas = ExtResource("4_fraqq") +region = Rect2(0, 0, 1, 1) + +[node name="Control" type="ColorRect"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0.501961, 0.501961, 0.501961, 0.12549) +script = ExtResource("1_6nwqw") + +[node name="Beep" type="AudioStreamPlayer" parent="."] +stream = ExtResource("3_tvnah") + +[node name="Tap" type="AudioStreamPlayer" parent="."] +stream = ExtResource("3_xnh3p") + +[node name="vert_align" type="BoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +alignment = 2 +vertical = true +metadata/_edit_use_anchors_ = true + +[node name="horiz_align" type="BoxContainer" parent="vert_align"] +layout_mode = 2 +alignment = 1 + +[node name="scale" type="Control" parent="vert_align/horiz_align"] +custom_minimum_size = Vector2(413, 411) +layout_mode = 2 +mouse_filter = 2 + +[node name="TextureRect" type="TextureRect" parent="vert_align/horiz_align/scale"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -0.5 +offset_top = -0.5 +offset_right = 0.5 +offset_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 +texture = SubResource("AtlasTexture_y0egr") +script = ExtResource("2_hov7x") + +[node name="OSD" parent="." instance=ExtResource("2_m7sf8")] +layout_mode = 1 + +[node name="MenuButton" type="MenuButton" parent="."] +custom_minimum_size = Vector2(48, 48) +layout_mode = 0 +offset_right = 8.0 +offset_bottom = 8.0 +theme = ExtResource("7_bx315") +theme_override_fonts/font = ExtResource("7_q7jy6") +theme_override_font_sizes/font_size = 32 +text = "menu" +item_count = 2 +popup/item_0/text = "Config" +popup/item_1/text = "Exit" +popup/item_1/id = 1 +script = ExtResource("8_3lc1r") diff --git a/scenes/osd.tscn b/scenes/osd.tscn new file mode 100644 index 0000000..340bf7e --- /dev/null +++ b/scenes/osd.tscn @@ -0,0 +1,59 @@ +[gd_scene load_steps=6 format=3 uid="uid://qnb6qrsgpbwc"] + +[ext_resource type="Theme" uid="uid://e62vyqlhf1v1" path="res://osd.tres" id="1_gvcqe"] +[ext_resource type="FontFile" uid="uid://c6ck52v6qotm" path="res://assets/fonts/B_Lexend.ttf" id="2_3mso8"] +[ext_resource type="Script" path="res://scripts/osd.gd" id="2_vrpyu"] + +[sub_resource type="LabelSettings" id="LabelSettings_h5r2p"] +font_color = Color(1, 1, 1, 0.501961) + +[sub_resource type="LabelSettings" id="LabelSettings_58gdk"] +font = ExtResource("2_3mso8") +font_size = 24 + +[node name="OSD" type="PanelContainer"] +visible = false +clip_contents = true +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -100.0 +offset_top = -50.0 +offset_right = 100.0 +offset_bottom = 50.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme = ExtResource("1_gvcqe") +script = ExtResource("2_vrpyu") + +[node name="OSDTagline" type="Label" parent="."] +custom_minimum_size = Vector2(0, 31) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 0 +text = "BPM" +label_settings = SubResource("LabelSettings_h5r2p") +horizontal_alignment = 1 +vertical_alignment = 1 +justification_flags = 0 +text_overrun_behavior = 3 + +[node name="OSDTitle" type="Label" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +text = "120" +label_settings = SubResource("LabelSettings_58gdk") +horizontal_alignment = 1 +vertical_alignment = 1 +justification_flags = 0 +text_overrun_behavior = 3 + +[node name="OSDActionBar" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_vertical = 8 +mouse_filter = 2 +alignment = 1 diff --git a/scenes/osd_action.tscn b/scenes/osd_action.tscn new file mode 100644 index 0000000..d5ad257 --- /dev/null +++ b/scenes/osd_action.tscn @@ -0,0 +1,53 @@ +[gd_scene load_steps=3 format=3 uid="uid://4er5u7okv1gr"] + +[ext_resource type="FontFile" uid="uid://4e3o2ds5xy7p" path="res://assets/fonts/MatSymOut.ttf" id="1_wgmk7"] + +[sub_resource type="LabelSettings" id="LabelSettings_dy2ef"] +font = ExtResource("1_wgmk7") + +[node name="OSDAction" type="Button"] +custom_minimum_size = Vector2(0, 24) +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_top = -4.0 +offset_bottom = 4.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +mouse_filter = 1 +keep_pressed_outside = true +text_overrun_behavior = 3 + +[node name="Icon" type="Label" parent="."] +custom_minimum_size = Vector2(24, 24) +layout_mode = 1 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_right = 16.0 +grow_vertical = 2 +text = "add" +label_settings = SubResource("LabelSettings_dy2ef") +horizontal_alignment = 1 +vertical_alignment = 1 +justification_flags = 0 + +[node name="Title" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -39.5 +offset_top = -10.0 +offset_right = 39.5 +offset_bottom = 10.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "More BPM" +horizontal_alignment = 1 +vertical_alignment = 1 +justification_flags = 0 diff --git a/scripts/ArrayUtils.gd b/scripts/ArrayUtils.gd new file mode 100644 index 0000000..c834453 --- /dev/null +++ b/scripts/ArrayUtils.gd @@ -0,0 +1,18 @@ +class_name ArrayUtils + +static func get_sclosest(array:Array, target:Variant, error = 0): + var floor = 0 + var ceil = array.size() - 1 + var closest = error + + while floor <= ceil: + var searchPoint = floor((floor + ceil) / 2) + var num = array[searchPoint] + + if num <= target: + closest = searchPoint + floor = searchPoint + 1 + else: + ceil = searchPoint - 1 + + return closest diff --git a/scripts/Config.gd b/scripts/Config.gd new file mode 100644 index 0000000..9d1eed1 --- /dev/null +++ b/scripts/Config.gd @@ -0,0 +1,190 @@ +class_name Config +extends Node + +enum SpritesheetAnimType { + DEFAULT, + IDLE +} +static var SpritesheetAnchorIndices = [ + "TOP_LEFT", + "TOP", + "TOP_RIGHT", + "LEFT", + "CENTER", + "RIGHT", + "BOTTOM_LEFT", + "BOTTOM", + "BOTTOM_RIGHT" +] +static var SpritesheetAnchor = { + TOP_LEFT = [BoxContainer.AlignmentMode.ALIGNMENT_BEGIN, BoxContainer.AlignmentMode.ALIGNMENT_BEGIN], + TOP = [BoxContainer.AlignmentMode.ALIGNMENT_CENTER, BoxContainer.AlignmentMode.ALIGNMENT_BEGIN], + TOP_RIGHT = [BoxContainer.AlignmentMode.ALIGNMENT_END, BoxContainer.AlignmentMode.ALIGNMENT_BEGIN], + LEFT = [BoxContainer.AlignmentMode.ALIGNMENT_BEGIN, BoxContainer.AlignmentMode.ALIGNMENT_CENTER], + CENTER = [BoxContainer.AlignmentMode.ALIGNMENT_CENTER, BoxContainer.AlignmentMode.ALIGNMENT_CENTER], + RIGHT = [BoxContainer.AlignmentMode.ALIGNMENT_END, BoxContainer.AlignmentMode.ALIGNMENT_CENTER], + BOTTOM_LEFT = [BoxContainer.AlignmentMode.ALIGNMENT_BEGIN, BoxContainer.AlignmentMode.ALIGNMENT_END], + BOTTOM = [BoxContainer.AlignmentMode.ALIGNMENT_CENTER, BoxContainer.AlignmentMode.ALIGNMENT_END], + BOTTOM_RIGHT = [BoxContainer.AlignmentMode.ALIGNMENT_END, BoxContainer.AlignmentMode.ALIGNMENT_END] +} + +# OSD and UI +var osd_dwell_time = 2000 +var osd_fade_time = 1000 +var osd_opacity = 1.0 +var menu_opacity = 1.0 +var background_opacity = 0.1 + +# BPM snap +var coarse_bpm_snap = 10.0 +var fine_bpm_snap = 0.5 + +# Tempo +var tempo_default = 120.0 +var tempo_numerator = 4 + +# Sparrow spritesheet data +var spritesheet_data = "res://assets/BOYFRIEND.xml" +var spritesheet_image = "res://assets/BOYFRIEND.png" +var spritesheet_anims = { + "BF idle dance" = { + alias = null, + type = SpritesheetAnimType.IDLE + }, + "BF NOTE LEFT" = { + alias = "left", + type = SpritesheetAnimType.DEFAULT + }, + "BF NOTE DOWN" = { + alias = "down", + type = SpritesheetAnimType.DEFAULT + }, + "BF NOTE UP" = { + alias = "up", + type = SpritesheetAnimType.DEFAULT + }, + "BF NOTE RIGHT" = { + alias = "right", + type = SpritesheetAnimType.DEFAULT + }, + "BF HEY!!" = { + alias = "special", + type = SpritesheetAnimType.DEFAULT + } +} +var spritesheet_anchor = SpritesheetAnchor.BOTTOM +var sprite_scale = 1.0 +var sprite_flip = false +var spritesheet_default_opacity = 1.0 + +# Keybinds + # Bind change keys +var bpm_is_global = true +var bpm_needs_enable = true +var bpm_increase:Array[Key] = [ KEY_R ] +var bpm_decrease:Array[Key] = [ KEY_F ] +var bpm_tsincrease:Array[Key] = [ KEY_T ] +var bpm_tsdecrease:Array[Key] = [ KEY_G ] +var bpm_opaincrease:Array[Key] = [ KEY_BRACELEFT ] +var bpm_opadecrease:Array[Key] = [ KEY_BRACERIGHT ] +var bpm_reset:Array[Key] = [ KEY_V ] + + # Bind modifiers +var mod_enable:Array[Key] = [ KEY_SHIFT ] +var mod_snap_fine:Array[Key] = [ KEY_TAB ] +var mod_snap_coarse:Array[Key] = [ KEY_CTRL ] + + # Bind animations +var anim_is_global = true +var anim_needs_enable = false +var anim_binds = { + left = [ KEY_A, KEY_LEFT ], + down = [ KEY_S, KEY_DOWN ], + up = [ KEY_W, KEY_UP ], + right = [ KEY_D, KEY_RIGHT ], + special = [ KEY_Q ] +} + +static func new_clear() -> Config: + var config:Config = Config.new() + config.spritesheet_anims = {} + config.bpm_increase = [] + config.bpm_decrease = [] + config.bpm_tsincrease = [] + config.bpm_tsdecrease = [] + config.bpm_opaincrease = [] + config.bpm_opadecrease = [] + config.bpm_reset = [] + config.mod_enable = [] + config.mod_snap_fine = [] + config.mod_snap_coarse = [] + config.anim_binds = {} + return config + +func clone_config(config) -> void: + self.osd_dwell_time = config.osd_dwell_time + self.osd_fade_time = config.osd_fade_time + self.osd_opacity = config.osd_opacity + self.menu_opacity = config.menu_opacity + self.background_opacity = config.background_opacity + self.coarse_bpm_snap = config.coarse_bpm_snap + self.fine_bpm_snap = config.fine_bpm_snap + self.tempo_default = config.tempo_default + self.tempo_numerator = config.tempo_numerator + self.spritesheet_data = config.spritesheet_data + self.spritesheet_image = config.spritesheet_image + self.spritesheet_anims = config.spritesheet_anims.duplicate(true) + self.spritesheet_anchor = config.spritesheet_anchor + self.sprite_scale = config.sprite_scale + self.sprite_flip = config.sprite_flip + self.spritesheet_default_opacity = config.spritesheet_default_opacity + self.bpm_is_global = config.bpm_is_global + self.bpm_needs_enable = config.bpm_needs_enable + self.bpm_increase = config.bpm_increase.duplicate(true) + self.bpm_decrease = config.bpm_decrease.duplicate(true) + self.bpm_tsincrease = config.bpm_tsincrease.duplicate(true) + self.bpm_tsdecrease = config.bpm_tsdecrease.duplicate(true) + self.bpm_opaincrease = config.bpm_opaincrease.duplicate(true) + self.bpm_opadecrease = config.bpm_opadecrease.duplicate(true) + self.bpm_reset = config.bpm_reset.duplicate(true) + self.mod_enable = config.mod_enable.duplicate(true) + self.mod_snap_fine = config.mod_snap_fine.duplicate(true) + self.mod_snap_coarse = config.mod_snap_coarse.duplicate(true) + self.anim_is_global = config.anim_is_global + self.anim_needs_enable = config.anim_needs_enable + self.anim_binds = config.anim_binds.duplicate(true) + +func format_config() -> Dictionary: + var dict = {} + dict.osd_dwell_time = self.osd_dwell_time + dict.osd_fade_time = self.osd_fade_time + dict.osd_opacity = self.osd_opacity + dict.menu_opacity = self.menu_opacity + dict.background_opacity = self.background_opacity + dict.coarse_bpm_snap = self.coarse_bpm_snap + dict.fine_bpm_snap = self.fine_bpm_snap + dict.tempo_default = self.tempo_default + dict.tempo_numerator = self.tempo_numerator + dict.spritesheet_data = self.spritesheet_data + dict.spritesheet_image = self.spritesheet_image + dict.spritesheet_anims = self.spritesheet_anims.duplicate(true) + dict.sprite_scale = self.sprite_scale + dict.sprite_flip = self.sprite_flip + dict.spritesheet_anchor = self.spritesheet_anchor + dict.spritesheet_default_opacity = self.spritesheet_default_opacity + dict.bpm_is_global = self.bpm_is_global + dict.bpm_needs_enable = self.bpm_needs_enable + dict.bpm_increase = self.bpm_increase.duplicate(true) + dict.bpm_decrease = self.bpm_decrease.duplicate(true) + dict.bpm_tsincrease = self.bpm_tsincrease.duplicate(true) + dict.bpm_tsdecrease = self.bpm_tsdecrease.duplicate(true) + dict.bpm_opaincrease = self.bpm_opaincrease.duplicate(true) + dict.bpm_opadecrease = self.bpm_opadecrease.duplicate(true) + dict.bpm_reset = self.bpm_reset.duplicate(true) + dict.mod_enable = self.mod_enable.duplicate(true) + dict.mod_snap_fine = self.mod_snap_fine.duplicate(true) + dict.mod_snap_coarse = self.mod_snap_coarse.duplicate(true) + dict.anim_is_global = self.anim_is_global + dict.anim_needs_enable = self.anim_needs_enable + dict.anim_binds = self.anim_binds.duplicate(true) + return dict diff --git a/scripts/GlobalConfigPreInit.gd b/scripts/GlobalConfigPreInit.gd new file mode 100644 index 0000000..80c6f06 --- /dev/null +++ b/scripts/GlobalConfigPreInit.gd @@ -0,0 +1,61 @@ +class_name GlobalConfigPreInit +extends Config + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + self.clone_config(Config.new_clear()) + load_from_file() + +func bind_keys() -> void: + bind_all_keys(self.bpm_increase, "bpm_increase") + bind_all_keys(self.bpm_decrease, "bpm_decrease") + bind_all_keys(self.bpm_tsincrease, "bpm_tsincrease") + bind_all_keys(self.bpm_tsdecrease, "bpm_tsdecrease") + bind_all_keys(self.bpm_opaincrease, "bpm_opaincrease") + bind_all_keys(self.bpm_opadecrease, "bpm_opadecrease") + bind_all_keys(self.bpm_reset, "bpm_reset") + bind_all_keys(self.mod_enable, "mod_enable") + bind_all_keys(self.mod_snap_fine, "mod_snap_fine") + bind_all_keys(self.mod_snap_coarse, "mod_snap_coarse") + + for action in self.anim_binds: + if not InputMap.has_action("anim_" + action): + InputMap.add_action("anim_" + action) + bind_all_keys(self.anim_binds[action], "anim_" + action) + +func bind_all_keys(array, action) -> void: + InputMap.action_erase_events(action) + for key in array: + var ev = InputEventKey.new() + ev.physical_keycode = key + InputMap.action_add_event(action, ev) + +func reset_settings() -> void: + var config = ConfigFile.new() + GlobalConfig.clone_config(Config.new()) + config.save("user://config.cfg") + + bind_keys() + +func save_to_file() -> void: + var dict = self.format_config() + var config = ConfigFile.new() + for key in dict: + config.set_value("FunkPanion", key, dict[key]) + config.save("user://config.cfg") + + bind_keys() + +func load_from_file() -> void: + var config = ConfigFile.new() + var err = config.load("user://config.cfg") + + if err != OK: + return + + for key in config.get_section_keys("FunkPanion"): + var value = config.get_value("FunkPanion", key) + if value != null: + self[key] = value + + bind_keys() diff --git a/scripts/MathUtils.gd b/scripts/MathUtils.gd new file mode 100644 index 0000000..cc45317 --- /dev/null +++ b/scripts/MathUtils.gd @@ -0,0 +1,9 @@ +class_name MathUtils + +static func average(numbers: Array) -> float: + var sum = 0.0 + var size = numbers.size() - 1 + for n in size: + var diff = numbers[n + 1] - numbers[n] + sum += diff + return sum / size diff --git a/scripts/SpritesheetParser.gd b/scripts/SpritesheetParser.gd new file mode 100644 index 0000000..0cedc20 --- /dev/null +++ b/scripts/SpritesheetParser.gd @@ -0,0 +1,101 @@ +class_name SpritesheetParser + +var spritesheet +var max_dimensions : Vector2i + +var _parser = XMLParser.new() +var _filename_regex = RegEx.new() + +func parse_spritesheet(path): + var animations = {} + max_dimensions = Vector2i(0, 0) + _parser.open(path) + _filename_regex.compile("^(.+?)(\\d{1,4})$") + while _parser.read() != ERR_FILE_EOF: + if _parser.get_node_type() != XMLParser.NODE_ELEMENT: + continue + + var node_name = _parser.get_node_name() + if node_name != "SubTexture": + continue + + var frame_search = _filename_regex.search(_parser.get_named_attribute_value_safe("name")) + var frame_name = frame_search.get_string(1) + var frame_no = frame_search.get_string(2).to_int() + var frame_x = _parser.get_named_attribute_value_safe("x") + var frame_y = _parser.get_named_attribute_value_safe("y") + var frame_width = _parser.get_named_attribute_value_safe("width") + var frame_height = _parser.get_named_attribute_value_safe("height") + var frame_frame_x = _parser.get_named_attribute_value_safe("frameX") + var frame_frame_y = _parser.get_named_attribute_value_safe("frameY") + var frame_frame_width = _parser.get_named_attribute_value_safe("frameWidth") + var frame_frame_height = _parser.get_named_attribute_value_safe("frameHeight") + + if not animations.has(frame_name): + animations[frame_name] = [] + + var array = animations[frame_name] + var frame_data = {} + + if frame_x.is_valid_int() and frame_y.is_valid_int(): + frame_data.pos = Vector2i(frame_x.to_int(), frame_y.to_int()) + else: + frame_data.pos = Vector2i.ZERO + if frame_width.is_valid_int() and frame_height.is_valid_int(): + frame_data.size = Vector2i(frame_width.to_int(), frame_height.to_int()) + if frame_width.to_int() > max_dimensions.x: + max_dimensions.x = frame_width.to_int() + if frame_height.to_int() > max_dimensions.y: + max_dimensions.y = frame_height.to_int() + else: + frame_data.size = Vector2i.ZERO + if frame_frame_x.is_valid_int() and frame_frame_y.is_valid_int(): + frame_data.frame_pos = Vector2i(frame_frame_x.to_int(), frame_frame_y.to_int()) + else: + frame_data.frame_pos = Vector2i.ZERO + if frame_frame_width.is_valid_int() and frame_frame_height.is_valid_int(): + frame_data.frame_size = Vector2i(frame_frame_width.to_int(), frame_frame_height.to_int()) + if frame_frame_width.to_int() > max_dimensions.x: + max_dimensions.x = frame_frame_width.to_int() + if frame_frame_height.to_int() > max_dimensions.y: + max_dimensions.y = frame_frame_height.to_int() + else: + frame_data.frame_size = frame_data.size + + if frame_no >= array.size(): + array.resize(frame_no + 1) + + array[frame_no] = frame_data + + for animation in animations: + var anim_frames: Array = animations[animation] + while anim_frames.has(null): + anim_frames.erase(null) + + spritesheet = animations + +func get_props_of_animation_frame(anim:String, frameno:int): + var anim_frames = spritesheet[anim] + frameno = clamp(frameno, 0, anim_frames.size() - 1) + var frame = anim_frames[frameno] + return { + region = Rect2(frame.pos, frame.size), + margin = Rect2(-frame.frame_pos, frame.frame_size - frame.size) + } + +static func load_image(path: String): + if path.begins_with('res'): + return load(path) + else: + var file = FileAccess.open(path, FileAccess.READ) + if FileAccess.get_open_error() != OK: + print(str("Could not load image at: ",path)) + return + var buffer = file.get_buffer(file.get_length()) + var image = Image.new() + var error = image.load_png_from_buffer(buffer) + if error != OK: + print(str("Could not load image at: ",path," with error: ",error)) + return + var texture = ImageTexture.create_from_image(image) + return texture diff --git a/scripts/Tempo.gd b/scripts/Tempo.gd new file mode 100644 index 0000000..b57c6df --- /dev/null +++ b/scripts/Tempo.gd @@ -0,0 +1,13 @@ +class_name Tempo + +static func get_crotchet(bpm): + return ((60 / bpm) * 1000) + +static func get_bpm(crotchet): + return ((1000 / crotchet) * 60) + +static func beat_at_time(time, bpm): + return time / get_crotchet(bpm) + +static func time_at_beat(beat, bpm): + return beat * get_crotchet(bpm) diff --git a/scripts/animator.gd b/scripts/animator.gd new file mode 100644 index 0000000..c5886b9 --- /dev/null +++ b/scripts/animator.gd @@ -0,0 +1,58 @@ +extends TextureRect + +var spritesheet_parser: SpritesheetParser = SpritesheetParser.new() + +var bpm = float(GlobalConfig.tempo_default) +var anims = [ +] +var active_anims = [] + +func _ready() -> void: + texture = AtlasTexture.new() + texture.atlas = SpritesheetParser.load_image(GlobalConfig.spritesheet_image) + texture.region = Rect2(0, 0, 1, 1) + spritesheet_parser.parse_spritesheet(GlobalConfig.spritesheet_data) + + self.item_rect_changed.connect(func(): + self.get_parent().custom_minimum_size = self.size * self.scale + self.position = Vector2.ZERO) + + var sprani = GlobalConfig.spritesheet_anims + for animation in sprani: + anims.append({ + name = animation, + key = "anim_" + sprani[animation].alias if sprani[animation].alias else null, + tstamp = 0.0, + active = sprani[animation].type == Config.SpritesheetAnimType.IDLE, + keydown = sprani[animation].type == Config.SpritesheetAnimType.IDLE, + type = sprani[animation].type + }) + +func _process(delta: float) -> void: + var time = Time.get_ticks_msec() + var current_animation = active_anims.front() + + if current_animation == null: + return + + if not spritesheet_parser.spritesheet.has(current_animation.name): + return + + var animation = spritesheet_parser.spritesheet[current_animation.name] + + var beat_percentage = Tempo.beat_at_time(time - current_animation.tstamp, bpm) + + if beat_percentage >= 1 and current_animation.active: + if current_animation.type == Config.SpritesheetAnimType.IDLE: + current_animation.tstamp = time + beat_percentage = 0 + elif current_animation.type == Config.SpritesheetAnimType.DEFAULT and current_animation.keydown: + beat_percentage = 1 + + + var anim_current = floor(beat_percentage * animation.size()) + var anim_current_props = spritesheet_parser.get_props_of_animation_frame(current_animation.name, anim_current) + + self.texture.region = anim_current_props.region + self.texture.margin = anim_current_props.margin + self.size = anim_current_props.margin.size diff --git a/scripts/config/config_window.gd b/scripts/config/config_window.gd new file mode 100644 index 0000000..af80758 --- /dev/null +++ b/scripts/config/config_window.gd @@ -0,0 +1,31 @@ +extends ScrollContainer + +var main_window +var exiting_to_config = false + +# Called when the node enters the scene tree for the first time. +func _enter_tree() -> void: + print("enter tree") + main_window = get_tree().root + main_window.borderless = false + main_window.always_on_top = false + main_window.transparent = false + main_window.min_size = Vector2i(640, 480) + main_window.size = Vector2i(640, 480) + ProjectSettings.set_setting("display/window/subwindows/embed_subwindows", false) + +func _exit_tree() -> void: + if exiting_to_config: return + main_window.borderless = true + main_window.always_on_top = true + main_window.transparent = true + main_window.min_size = Vector2i(100, 100) + ProjectSettings.set_setting("display/window/subwindows/embed_subwindows", true) + +func _on_back_button_pressed() -> void: + get_tree().change_scene_to_file("res://scenes/main.tscn") + +func _on_reset_pressed() -> void: + GlobalConfig.reset_settings() + exiting_to_config = true + get_tree().reload_current_scene() diff --git a/scripts/config/form_handler.gd b/scripts/config/form_handler.gd new file mode 100644 index 0000000..141c394 --- /dev/null +++ b/scripts/config/form_handler.gd @@ -0,0 +1,387 @@ +extends VBoxContainer + +@onready var osd_dwell_time = $"OSD and UI/Controls/VBoxContainer/osd_dwell_time" +@onready var osd_fade_time = $"OSD and UI/Controls/VBoxContainer/osd_fade_time" +@onready var osd_opacity = $"OSD and UI/Controls/VBoxContainer/osd_opacity" +@onready var menu_opacity = $"OSD and UI/Controls/VBoxContainer/menu_opacity" +@onready var background_opacity = $"OSD and UI/Controls/VBoxContainer/background_opacity" + +@onready var tempo_default = $"Tempo/Controls/VBoxContainer/tempo_default" +@onready var tempo_numerator = $"Tempo/Controls/VBoxContainer/tempo_numerator" + +@onready var spritesheet_image = $"Sprites/Controls/VBoxContainer/spritesheet_image" +@onready var spritesheet_data = $"Sprites/Controls/VBoxContainer/spritesheet_data" +@onready var spritesheet_anims = $"Sprites/Controls/VBoxContainer/spritesheet_anims" +@onready var spritesheet_anchor = $"Sprites/Controls/VBoxContainer/spritesheet_anchor" +@onready var sprite_scale = $"Sprites/Controls/VBoxContainer/sprite_scale" +@onready var sprite_flip = $"Sprites/Controls/VBoxContainer/sprite_flip" +@onready var spritesheet_default_opacity = $"Sprites/Controls/VBoxContainer/spritesheet_default_opacity" + +@onready var anims = $"Binds/Controls/VBoxContainer" + +@onready var matsymout = load("res://assets/fonts/MatSymOut.ttf") +@onready var close_icon = load("res://assets/icons/close.png") +@onready var add_icon = load("res://assets/icons/add.png") + +var temp_config = Config.new_clear() + +func _parse_form_values() -> void: + pass + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + temp_config.clone_config(GlobalConfig) + link_range_to_property(osd_dwell_time, "osd_dwell_time") + link_range_to_property(osd_fade_time, "osd_fade_time") + link_value_to_slider(osd_opacity) + link_range_to_property(osd_opacity, "osd_opacity") + link_value_to_slider(menu_opacity) + link_range_to_property(menu_opacity, "menu_opacity") + link_value_to_slider(background_opacity) + link_range_to_property(background_opacity, "background_opacity") + + link_range_to_property(tempo_default, "tempo_default") + link_range_to_property(tempo_numerator, "tempo_numerator") + + link_filebox_to_property(spritesheet_image, "spritesheet_image") + link_filebox_to_property(spritesheet_data, "spritesheet_data") + link_optionbutton_to_property(spritesheet_anchor, "spritesheet_anchor", Config.SpritesheetAnchor) + link_range_to_property(sprite_scale, "sprite_scale") + link_toggleable_to_property(sprite_flip, "sprite_flip") + link_value_to_slider(spritesheet_default_opacity) + link_range_to_property(spritesheet_default_opacity, "spritesheet_default_opacity") + fuckwith_anims() + fuckwith_spritesheet_anims() + +func _on_back_button_pressed() -> void: + GlobalConfig.clone_config(temp_config) + + GlobalConfig.save_to_file() + +var unlock_func = null + +func _input(event:InputEvent) -> void: + if unlock_func and event is InputEventKey: + unlock_func.call(event.physical_keycode) + +func link_value_to_slider(obj:BoxContainer) -> void: + var input:HSlider = obj.get_node("input") + var value:Label = obj.get_node("value") + + input.value_changed.connect(func(va: float): + value.text = str(va * 100) + "%") + +func link_range_to_property(obj:BoxContainer, property:String) -> void: + var input:Range = obj.get_node("input") + + input.value = temp_config[property] + + input.value_changed.connect(func(va: float): + temp_config[property] = va) + +func link_toggleable_to_property(obj:BoxContainer, property:String) -> void: + var input:BaseButton = obj.get_node("input") + + input.button_pressed = temp_config[property] + + input.toggled.connect(func(toggled_on: bool): + temp_config[property] = toggled_on) + +func link_textbox_to_property(obj:BoxContainer, property:String) -> void: + var input:LineEdit = obj.get_node("value") + + input.text = temp_config[property] + + input.text_changed.connect(func(va: String): + temp_config[property] = va) + +func link_filebox_to_property(obj:BoxContainer, property:String) -> void: + var input:LineEdit = obj.get_node("value") + + input.text = temp_config[property] + + obj.file_changed.connect(func(va: String): + temp_config[property] = va) + +func link_optionbutton_to_property(obj:BoxContainer, property:String, enu:Dictionary) -> void: + var input:OptionButton = obj.get_node("input") + + input.select( + Config.SpritesheetAnchorIndices.find( + enu.find_key(temp_config[property]) + ) + ) + + input.item_selected.connect(func(index: int): + temp_config[property] = enu[Config.SpritesheetAnchorIndices[index]]) + +func fuckwith_spritesheet_anims() -> void: + var tree:Tree = spritesheet_anims.get_node("Tree") + var add_new_name:LineEdit = spritesheet_anims.get_node("AddNew/name") + var add_new_switch:Button = spritesheet_anims.get_node("AddNew/switch") + + var tree_anims:Tree = anims.get_node("Tree") + + tree.set_column_title(0, "Animation Name") + tree.set_column_title(1, "Alias") + tree.set_column_title(2, "Type") + + var tree_root = tree.create_item() + var tree_anims_root = tree_anims.get_root() + var tree_anims_root_anims = tree_anims.get_root().get_child(0) + var tree_anims_root_functions = tree_anims.get_root().get_child(1) + + var keep_updated = func(): + temp_config.spritesheet_anims = {} + for child in tree_root.get_children(): + var child_name = child.get_text(0) + var child_alias = child.get_text(1) + var child_type = child.get_range(2) + + temp_config.spritesheet_anims[child_name] = { + alias = child_alias if child_alias.length() > 0 else null, + type = child_type + } + + var has_thing = false + for alias in tree_anims_root_anims.get_children(): + if child_alias.length() < 1 or alias.get_text(0) == child_alias: + has_thing = true + break + if not has_thing: + var anims_child = tree_anims.create_item(tree_anims_root_anims) + anims_child.set_cell_mode(0, TreeItem.CELL_MODE_STRING) + anims_child.set_editable(0, false) + anims_child.set_text(0, child_alias) + temp_config.anim_binds[child_alias] = [] + anims_child.add_button(2, add_icon, 1) + + for alias in tree_anims_root_anims.get_children(): + var has_thing = false + for child in tree_root.get_children(): + var child_alias = child.get_text(1) + if child_alias.length() > 0 and alias.get_text(0) == child_alias: + has_thing = true + break + if not has_thing: + temp_config.anim_binds.erase(alias.get_text(0)) + alias.free() + + spritesheet_data.file_changed.connect(func(path: String): + var spritesheet_parser = SpritesheetParser.new() + spritesheet_parser.parse_spritesheet(path) + + for animation in spritesheet_parser.spritesheet: + for child in tree_root.get_children(): + if child.get_text(0) == animation: + return + + var child = tree.create_item(tree_root) + child.set_cell_mode(0, TreeItem.CELL_MODE_STRING) + child.set_editable(0, true) + child.set_text(0, animation) + child.set_cell_mode(1, TreeItem.CELL_MODE_STRING) + child.set_editable(1, true) + child.set_text(1, "") + if animation.containsn("left"): + child.set_text(1, "left") + elif animation.containsn("down"): + child.set_text(1, "down") + elif animation.containsn("up"): + child.set_text(1, "up") + elif animation.containsn("right"): + child.set_text(1, "right") + child.set_cell_mode(2, TreeItem.CELL_MODE_RANGE) + child.set_editable(2, true) + child.set_text(2, "Default,Idle") + child.add_button(2, close_icon) + child.set_range(2, animation.containsn("idle")) + + keep_updated.call() + ) + + tree.item_edited.connect(keep_updated) + + tree.button_clicked.connect(func(item:TreeItem, column:int, _id, _mbi): + var item_name = item.get_text(0) + if column == 2: + item.free() + keep_updated.call() + ) + + for animation in temp_config.spritesheet_anims: + var child = tree.create_item(tree_root) + child.set_cell_mode(0, TreeItem.CELL_MODE_STRING) + child.set_editable(0, true) + child.set_text(0, animation) + child.set_cell_mode(1, TreeItem.CELL_MODE_STRING) + child.set_editable(1, true) + var alias = temp_config.spritesheet_anims.get(animation).alias + child.set_text(1, alias if alias else "") + child.set_cell_mode(2, TreeItem.CELL_MODE_RANGE) + child.set_editable(2, true) + child.set_text(2, "Default,Idle") + var type = temp_config.spritesheet_anims.get(animation).type + child.set_range(2, type) + child.add_button(2, close_icon) + + add_new_switch.pressed.connect(func(): + if add_new_name.text.length() > 0: + for child in tree_root.get_children(): + if child.get_text(0) == add_new_name.text: + return + + var child = tree.create_item(tree_root) + child.set_cell_mode(0, TreeItem.CELL_MODE_STRING) + child.set_editable(0, true) + child.set_text(0, add_new_name.text) + child.set_cell_mode(1, TreeItem.CELL_MODE_STRING) + child.set_editable(1, true) + child.set_text(1, "") + child.set_cell_mode(2, TreeItem.CELL_MODE_RANGE) + child.set_editable(2, true) + child.set_text(2, "Default,Idle") + child.add_button(2, close_icon) + + add_new_name.text = "" + + keep_updated.call() + ) + +func fuckwith_anims() -> void: + var tree:Tree = anims.get_node("Tree") + + tree.set_column_expand(1, false) + tree.set_column_custom_minimum_width(1, 150) + tree.set_column_expand(2, false) + tree.set_column_custom_minimum_width(2, 150) + + var tree_root = tree.create_item() + + var anims_root = tree.create_item(tree_root) + anims_root.set_cell_mode(0, TreeItem.CELL_MODE_STRING) + anims_root.set_editable(0, false) + anims_root.set_text(0, "Animation aliases") + anims_root.set_cell_mode(1, TreeItem.CELL_MODE_CHECK) + anims_root.set_editable(1, true) + anims_root.set_checked(1, temp_config.anim_is_global) + anims_root.set_text(1, "Global capture") + anims_root.set_tooltip_text(1, "If animation keypresses should be captured\noutside of the application.") + anims_root.set_cell_mode(2, TreeItem.CELL_MODE_CHECK) + anims_root.set_editable(2, true) + anims_root.set_checked(2, temp_config.anim_needs_enable) + anims_root.set_text(2, "Requires Enable") + anims_root.set_tooltip_text(2, "If animation keypresses should be ignored\nunless (Modifier) Enable is pressed.") + + var functions_root = tree.create_item(tree_root) + functions_root.set_cell_mode(0, TreeItem.CELL_MODE_STRING) + functions_root.set_editable(0, false) + functions_root.set_text(0, "Functions and modifiers") + functions_root.set_cell_mode(1, TreeItem.CELL_MODE_CHECK) + functions_root.set_editable(1, true) + functions_root.set_checked(1, temp_config.bpm_is_global) + functions_root.set_text(1, "Global capture") + functions_root.set_tooltip_text(1, "If function keypresses should be captured\noutside of the application.") + functions_root.set_cell_mode(2, TreeItem.CELL_MODE_CHECK) + functions_root.set_editable(2, true) + functions_root.set_checked(2, temp_config.bpm_needs_enable) + functions_root.set_text(2, "Requires Enable") + functions_root.set_tooltip_text(2, "if function keypresses should be ignored\nunless (Modifier) Enable is pressed.\nModifiers are not affected.") + var functions_index_to_key = {} + + var add_tree_item_for_function = func(key: String, label: String): + var anims_child = tree.create_item(functions_root) + anims_child.set_cell_mode(0, TreeItem.CELL_MODE_STRING) + anims_child.set_editable(0, false) + anims_child.set_text(0, label) + anims_child.add_button(2, add_icon, 1) + functions_index_to_key[anims_child.get_index()] = key + for phykey in temp_config[key]: + var child = tree.create_item(anims_child) + child.set_cell_mode(0, TreeItem.CELL_MODE_CUSTOM) + child.set_editable(0, false) + child.set_text(0, OS.get_keycode_string(DisplayServer.keyboard_get_keycode_from_physical(phykey))) + child.add_button(2, close_icon, 2) + + add_tree_item_for_function.call("bpm_increase", "Increase Tempo") + add_tree_item_for_function.call("bpm_decrease", "Decrease Tempo") + add_tree_item_for_function.call("bpm_tsincrease", "Increase Time Signature") + add_tree_item_for_function.call("bpm_tsdecrease", "Decrease Time Signature") + add_tree_item_for_function.call("bpm_opaincrease", "Increase Character Opacity") + add_tree_item_for_function.call("bpm_opadecrease", "Decrease Character Opacity") + add_tree_item_for_function.call("bpm_reset", "Sync Tempo") + add_tree_item_for_function.call("mod_enable", "(Modifier) Enable") + add_tree_item_for_function.call("mod_snap_coarse", "(Modifier) Snap Coarse") + add_tree_item_for_function.call("mod_snap_fine", "(Modifier) Snap Fine") + + tree.item_edited.connect(func(): + temp_config.anim_is_global = anims_root.is_checked(1) + temp_config.anim_needs_enable = anims_root.is_checked(2) + temp_config.bpm_is_global = functions_root.is_checked(1) + temp_config.bpm_needs_enable = functions_root.is_checked(2) + ) + + tree.item_activated.connect(func(): + var selected = tree.get_selected() + var level = 0 + var last_parent = selected + while last_parent != tree_root: + last_parent = last_parent.get_parent() + level += 1 + if level == 3: + var parent = selected.get_parent() + if unlock_func: + selected.set_text(0, "Double-click to set key") + if parent.get_parent() == anims_root: + temp_config.anim_binds[parent.get_text(0)][selected.get_index()] = -1 + elif parent.get_parent() == functions_root: + var key = functions_index_to_key[parent.get_index()] + temp_config[key][selected.get_index()] = -1 + unlock_func = null + else: + selected.set_text(0, "Press any key...") + unlock_func = func(keycode: Key): + selected.set_text(0, OS.get_keycode_string(DisplayServer.keyboard_get_keycode_from_physical(keycode))) + if parent.get_parent() == anims_root: + temp_config.anim_binds[parent.get_text(0)][selected.get_index()] = keycode + elif parent.get_parent() == functions_root: + var key = functions_index_to_key[parent.get_index()] + temp_config[key][selected.get_index()] = keycode + unlock_func = null + ) + + for keybind in temp_config.anim_binds: + var anims_child = tree.create_item(anims_root) + anims_child.set_cell_mode(0, TreeItem.CELL_MODE_STRING) + anims_child.set_editable(0, false) + anims_child.set_text(0, keybind) + anims_child.add_button(2, add_icon, 1) + for key in temp_config.anim_binds.get(keybind): + var child = tree.create_item(anims_child) + child.set_cell_mode(0, TreeItem.CELL_MODE_CUSTOM) + child.set_editable(0, false) + child.set_text(0, OS.get_keycode_string(DisplayServer.keyboard_get_keycode_from_physical(key))) + child.add_button(2, close_icon, 2) + + tree.button_clicked.connect(func(item:TreeItem, column:int, id:int, _mbi): + if column == 2: + var parent = item.get_parent() + if id == 1: + var child = tree.create_item(item) + if parent == anims_root: + temp_config.anim_binds[item.get_text(0)].insert(child.get_index(), -1) + elif parent == functions_root: + var key = functions_index_to_key[item.get_index()] + temp_config[key].insert(child.get_index(), -1) + child.set_cell_mode(0, TreeItem.CELL_MODE_CUSTOM) + child.set_editable(0, false) + child.set_text(0, "Double-click to set key") + child.add_button(2, close_icon, 2) + if id == 2: + if parent.get_parent() == anims_root: + temp_config.anim_binds[parent.get_text(0)].remove_at(item.get_index()) + elif parent.get_parent() == functions_root: + var key = functions_index_to_key[parent.get_index()] + temp_config[key].remove_at(item.get_index()) + item.free() + ) diff --git a/scripts/config/open_file_dialog.gd b/scripts/config/open_file_dialog.gd new file mode 100644 index 0000000..df86654 --- /dev/null +++ b/scripts/config/open_file_dialog.gd @@ -0,0 +1,43 @@ +extends HBoxContainer + +signal file_changed(path: String) + +@onready var root = get_tree().root +@onready var open_file = $"switch" +@onready var path = $"value" +@onready var upload_file = $"save" + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + print("User data dir located in " + OS.get_data_dir()) + open_file.pressed.connect(func(): + var file_dialog = FileDialog.new() + file_dialog.use_native_dialog = true + file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE + file_dialog.access = FileDialog.ACCESS_FILESYSTEM + file_dialog.current_path = OS.get_data_dir().path_join("FunkPanion/") + file_dialog.file_selected.connect(func(path_string: String): + var localized = path_string.simplify_path().replace(OS.get_data_dir().path_join("FunkPanion/"), "user://") + path.text = localized + file_changed.emit(localized) + file_dialog.queue_free() + ) + file_dialog.canceled.connect(func(path_string: String): + root.remove_child(file_dialog) + file_dialog.queue_free() + ) + root.add_child(file_dialog) + file_dialog.popup() + ) + upload_file.pressed.connect(func(): + if path.text.length() > 0: + print("path text yes", path.text) + if FileAccess.file_exists(path.text) and path.text.findn("user://") != 0 and ProjectSettings.localize_path(path.text).findn("user://") != 0: + print("path exists") + if not DirAccess.dir_exists_absolute("user://uploads/"): + DirAccess.make_dir_absolute("user://uploads/") + var new_path = "user://uploads/".path_join(path.text.get_file()) + DirAccess.copy_absolute(path.text, new_path) + path.text = new_path + file_changed.emit(new_path) + ) diff --git a/scripts/menu.gd b/scripts/menu.gd new file mode 100644 index 0000000..258ab9b --- /dev/null +++ b/scripts/menu.gd @@ -0,0 +1,18 @@ +extends MenuButton + +var popup:PopupMenu + +func _index_pressed(idx: int): + if idx == 0: + get_tree().change_scene_to_file("res://scenes/config.tscn") + elif idx == 1: + get_tree().quit() + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + popup = get_popup() + popup.index_pressed.connect(_index_pressed) + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + self_modulate.a = 1 if popup.visible else GlobalConfig.menu_opacity diff --git a/scripts/osd.gd b/scripts/osd.gd new file mode 100644 index 0000000..dbaeafa --- /dev/null +++ b/scripts/osd.gd @@ -0,0 +1,112 @@ +extends PanelContainer + +const res_action = preload("res://scenes/osd_action.tscn") + +enum ICON_POSITION { + LEFT, + CENTER, + RIGHT +} + +@onready var gui_tagline := $"OSDTagline" +@onready var gui_title := $"OSDTitle" +@onready var gui_actionbar := $"OSDActionBar" + +@onready var tagline: + get: gui_tagline.text + set(value): + gui_tagline.text = value +@onready var title: + get: gui_title.text + set(value): + gui_title.text = value +var last_shown = -(GlobalConfig.osd_dwell_time + GlobalConfig.osd_fade_time) + +func show_osd(a_tagline: String, a_title: String, a_actions: Array): + # Clear actions + for n in gui_actionbar.get_children(): + gui_actionbar.remove_child(n) + n.queue_free() + + tagline = a_tagline + title = a_title + for action in a_actions: + var button = res_action.instantiate() + var title: Label = button.get_node("Title") + var icon: Label = button.get_node("Icon") + + title.text = action.title + + if action.icon != null: + icon.text = action.icon + if action.icon_pos == ICON_POSITION.LEFT: + icon.set_anchors_preset(Control.PRESET_LEFT_WIDE) + icon.set_position(Vector2(0,-12)) + elif action.icon_pos == ICON_POSITION.CENTER: + icon.set_anchors_preset(Control.PRESET_VCENTER_WIDE) + icon.set_position(Vector2(-12,-12)) + else: + icon.set_anchors_preset(Control.PRESET_RIGHT_WIDE) + icon.set_position(Vector2(-24,-12)) + + if action.toggles: + button.toggle_mode = true + button.toggled.connect(action.action) + else: + button.pressed.connect(action.action) + + gui_actionbar.add_child(button) + + last_shown = Time.get_ticks_msec() + visible = true + modulate.a = GlobalConfig.osd_opacity + mouse_filter = Control.MOUSE_FILTER_STOP + for n in gui_actionbar.get_children(): + gui_actionbar.mouse_filter = Control.MOUSE_FILTER_STOP + +func hide_osd(): + last_shown = -(GlobalConfig.osd_dwell_time + GlobalConfig.osd_fade_time) + visible = false + modulate.a = GlobalConfig.osd_opacity + mouse_filter = Control.MOUSE_FILTER_IGNORE + for n in gui_actionbar.get_children(): + gui_actionbar.mouse_filter = Control.MOUSE_FILTER_IGNORE + +var mouse_inside = false +var mouse_just_inside = false + +func _ready() -> void: + mouse_entered.connect(func(): mouse_inside = true; mouse_just_inside = true) + mouse_exited.connect(func(): mouse_inside = false) + +func _process(delta: float) -> void: + var ft = GlobalConfig.osd_dwell_time + GlobalConfig.osd_fade_time + var time_since_shown = Time.get_ticks_msec() - last_shown + + if mouse_inside and time_since_shown >= GlobalConfig.osd_dwell_time and time_since_shown < ft: + if mouse_just_inside: + mouse_just_inside = false + visible = true + modulate.a = GlobalConfig.osd_opacity + mouse_filter = Control.MOUSE_FILTER_STOP + for n in gui_actionbar.get_children(): + gui_actionbar.mouse_filter = Control.MOUSE_FILTER_PASS + last_shown = Time.get_ticks_msec() + time_since_shown = 0 + + if time_since_shown >= ft and visible: + modulate.a = GlobalConfig.osd_opacity + visible = false + mouse_filter = Control.MOUSE_FILTER_IGNORE + for n in gui_actionbar.get_children(): + gui_actionbar.mouse_filter = Control.MOUSE_FILTER_IGNORE + elif time_since_shown >= GlobalConfig.osd_dwell_time and time_since_shown < ft: + var percentage = min(1, float(time_since_shown - GlobalConfig.osd_dwell_time) / GlobalConfig.osd_fade_time) + modulate.a = (1 - percentage) * GlobalConfig.osd_opacity + visible = true + elif time_since_shown < GlobalConfig.osd_dwell_time and not visible: + modulate.a = GlobalConfig.osd_opacity + visible = true + mouse_filter = Control.MOUSE_FILTER_STOP + for n in gui_actionbar.get_children(): + gui_actionbar.mouse_filter = Control.MOUSE_FILTER_STOP diff --git a/scripts/window.gd b/scripts/window.gd new file mode 100644 index 0000000..e8b5982 --- /dev/null +++ b/scripts/window.gd @@ -0,0 +1,254 @@ +extends ColorRect + +@onready var main_window = get_tree().root +@onready var character := $"vert_align/horiz_align/scale/TextureRect" +@onready var osd = $"OSD" +@onready var beepplayer := $"Beep" +@onready var tapplayer := $"Tap" + +@onready var vert_align = $"vert_align" +@onready var horiz_align = $"vert_align/horiz_align" +@onready var scale_node = $"vert_align/horiz_align/scale" + +var dragging = false +var dragging_start_position:Vector2i +var old_window_position:Vector2i + +var signal_offset = false +var signal_change = false +var signal_beep_num = false +var signal_beep_bpm = false + +var bpm_snap = 1 +var character_opacity = 100.0 +var numerator = int(GlobalConfig.tempo_numerator) +var change_array = [] + +func _ready() -> void: + horiz_align.alignment = GlobalConfig.spritesheet_anchor[0] + vert_align.alignment = GlobalConfig.spritesheet_anchor[1] + + main_window.min_size = Vector2i(100, 100) + main_window.size = character.spritesheet_parser.max_dimensions * GlobalConfig.sprite_scale + character.scale = Vector2(GlobalConfig.sprite_scale, GlobalConfig.sprite_scale) + character_opacity = GlobalConfig.spritesheet_default_opacity * 100 + character.flip_h = GlobalConfig.sprite_flip + self.color.a = GlobalConfig.background_opacity + +func _gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + if event.get_button_index() == 1: + dragging = event.is_pressed() + dragging_start_position = DisplayServer.mouse_get_position() + old_window_position = main_window.get_position() + +func _process(_delta: float) -> void: + set_default_cursor_shape(Control.CURSOR_DRAG if dragging else Control.CURSOR_ARROW) + if dragging: + main_window.set_position(old_window_position + (DisplayServer.mouse_get_position() - dragging_start_position)) + + character.modulate.a = character_opacity / 100 + + if not GlobalConfig.anim_is_global: + _anim_process(Input) + if not GlobalConfig.bpm_is_global: + _input_functions(Input) + +func _physics_process(_delta: float) -> void: + if GlobalConfig.anim_is_global: + _anim_process(GlobalInput) + if GlobalConfig.bpm_is_global: + _input_functions(GlobalInput) + +func _input_functions(input) -> void: + if GlobalConfig.bpm_needs_enable and not input.is_action_pressed("mod_enable"): + return + + bpm_snap = 1 + if input.is_action_pressed("mod_snap_fine"): + bpm_snap *= 0.5 + if input.is_action_pressed("mod_snap_coarse"): + bpm_snap *= 10 + + if input.is_action_just_pressed("bpm_decrease"): + _run_bpmchange(-bpm_snap) + _run_offset() + if input.is_action_just_pressed("bpm_increase"): + _run_bpmchange(bpm_snap) + _run_offset() + + if input.is_action_just_pressed("bpm_opadecrease"): + _run_opacitychange(-bpm_snap) + if input.is_action_just_pressed("bpm_opaincrease"): + _run_opacitychange(bpm_snap) + + if input.is_action_just_pressed("bpm_reset"): + _run_offset() + _run_tapper() + + if input.is_action_just_pressed("bpm_tsdecrease"): + _run_numeratorchange(-1) + if input.is_action_just_pressed("bpm_tsincrease"): + _run_numeratorchange(1) + +var time = 0.0 +func _anim_process(input) -> void: + var process_key_input = input.is_action_pressed("mod_enable") or not GlobalConfig.anim_needs_enable + + var prev_time = time + time = Time.get_ticks_msec() + for animation in character.anims: + if animation.key != null: + var kd_before = animation.keydown + animation.keydown = process_key_input and input.is_action_pressed(animation.key) + if animation.keydown and not kd_before: + animation.tstamp = time + animation.active = animation.keydown or Tempo.beat_at_time(time - animation.tstamp, character.bpm) <= 1 + + var prev_active_anim = character.active_anims.front() + character.active_anims = character.anims.filter(func(a): return a.active) + character.active_anims.sort_custom(func(a, b): return a.tstamp > b.tstamp) + var active_anim = character.active_anims.front() + if active_anim and active_anim != prev_active_anim: + active_anim.tstamp = time + +func _osd_opacity_change(): + osd.show_osd("Character opacity", str(character_opacity) + "%", [ + { + title = "", + icon = "remove", + icon_pos = osd.ICON_POSITION.CENTER, + toggles = false, + action = (func(): + _run_opacitychange(-bpm_snap)) + }, + { + title = "", + icon = "add", + icon_pos = osd.ICON_POSITION.CENTER, + toggles = false, + action = (func(): + _run_opacitychange(bpm_snap)) + } + ]) + +func _osd_bpm_change(): + osd.show_osd("Tempo", str(character.bpm) + "bpm", [ + { + title = "", + icon = "remove", + icon_pos = osd.ICON_POSITION.CENTER, + toggles = false, + action = (func(): + _run_bpmchange(-bpm_snap) + _run_offset()) + }, + { + title = "", + icon = "touch_app", + icon_pos = osd.ICON_POSITION.CENTER, + toggles = false, + action = (func(): + _run_offset() + _run_tapper()) + }, + { + title = "", + icon = "add", + icon_pos = osd.ICON_POSITION.CENTER, + toggles = false, + action = (func(): + _run_bpmchange(bpm_snap) + _run_offset()) + } + ]) + +func _osd_num_change(): + osd.show_osd("Time signature", str(numerator) + "/4", [ + { + title = "", + icon = "remove", + icon_pos = osd.ICON_POSITION.CENTER, + toggles = false, + action = (func(): + _run_numeratorchange(-1)) + }, + { + title = "", + icon = "add", + icon_pos = osd.ICON_POSITION.CENTER, + toggles = false, + action = (func(): + _run_numeratorchange(1)) + } + ]) + +func _osd_bpm_reset(): + osd.show_osd(str(numerator) + " taps for Tapper", "Sync", [ + { + title = "Reset/Tap", + icon = "touch_app", + icon_pos = osd.ICON_POSITION.RIGHT, + toggles = false, + action = (func(): + _run_offset() + _run_tapper()) + } + ]) + +func _run_opacitychange(delta: float): + if character_opacity + delta < 0.0: + character_opacity = 0.0 + elif character_opacity + delta > 100.0: + character_opacity = 100.0 + else: + character_opacity += delta + _osd_opacity_change() + +func _run_bpmchange(delta: float): + if character.bpm + delta < 60.0: + character.bpm = 60.0 + elif character.bpm + delta > 1000.0: + character.bpm = 1000.0 + else: + character.bpm += delta + beepplayer.play(0) + _osd_bpm_change() + +func _run_numeratorchange(delta: int): + if numerator + delta < 2: + numerator = 2 + elif numerator + delta > 16: + numerator = 16 + else: + numerator += delta + beepplayer.play(0) + _osd_num_change() + +func _run_offset(): + for anim in character.anims: + if anim.type == GlobalConfig.SpritesheetAnimType.IDLE: + anim.tstamp = Time.get_ticks_msec() + +func _run_tapper(): + var time = Time.get_ticks_msec() + if change_array.size() > 0: + var last_time = change_array.back() + var time_offset = time - last_time + if time_offset <= 2000.0: + change_array.append(time) + else: + change_array = [Time.get_ticks_msec()] + else: + change_array = [Time.get_ticks_msec()] + var avg = MathUtils.average(change_array) + if change_array.size() >= numerator: + character.bpm = floor(Tempo.get_bpm(avg) / bpm_snap) * bpm_snap + _osd_bpm_change() + else: _osd_bpm_reset() + + if (change_array.size() - 1) % numerator == 0: + tapplayer.pitch_scale = 1.25 + else: + tapplayer.pitch_scale = 1 + tapplayer.play(0)