diff --git a/Funk Engine.csproj b/Funk Engine.csproj index 7051324c..f8d51ba9 100644 --- a/Funk Engine.csproj +++ b/Funk Engine.csproj @@ -6,6 +6,9 @@ true FunkEngine + + + diff --git a/Globals/Scribe.cs b/Globals/Scribe.cs index 51e57382..95da2aa7 100644 --- a/Globals/Scribe.cs +++ b/Globals/Scribe.cs @@ -28,6 +28,17 @@ public partial class Scribe : Node director.Enemy.TakeDamage((int)timing); } ), + new Note( + "PlayerDouble", + null, + 1, + (director, note, timing) => + { + // can change later, but I want it like this instead of changing base + // in case we have some relic that messes with timing + director.Enemy.TakeDamage(2 * (int)timing); + } + ), }; public static readonly RelicTemplate[] RelicDictionary = new[] diff --git a/SaveData/SaveData.json b/SaveData/SaveData.json new file mode 100644 index 00000000..1aeaf3cc --- /dev/null +++ b/SaveData/SaveData.json @@ -0,0 +1,9 @@ +{ + "AccountName": null, + "Notes": { + "PlayerBase": 2, + "PlayerDouble": 1 + }, + "Relics": {}, + "Settings": {} +} \ No newline at end of file diff --git a/SaveData/SaveSystem.cs b/SaveData/SaveSystem.cs new file mode 100644 index 00000000..7cc7af03 --- /dev/null +++ b/SaveData/SaveSystem.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Godot; + +// TODO: implement saving + +public static class SaveSystem +{ + private static string SavePath => "res://SaveData/SaveData.json"; // Update if needed + + // Loads only the notes section + public static Dictionary LoadNotes() + { + var saveData = LoadSaveData(); + if (saveData != null && saveData.Notes != null) + { + return saveData.Notes; + } + else + { + return new Dictionary(); + } + } + + // This method loads the entire save data + public static SaveData LoadSaveData() + { + string path = ProjectSettings.GlobalizePath(SavePath); + if (!File.Exists(path)) + { + GD.PrintErr("Can't load save game"); + return null; + } + + string json = File.ReadAllText(path); + SaveData data = JsonSerializer.Deserialize(json); + return data; + } +} + +public class SaveData +{ + public string AccountName { get; set; } + public Dictionary Notes { get; set; } = new Dictionary(); + public Dictionary Relics { get; set; } = new Dictionary(); + public Dictionary Settings { get; set; } = new Dictionary(); +} diff --git a/scenes/BattleDirector/scripts/BattleDirector.cs b/scenes/BattleDirector/scripts/BattleDirector.cs index 4adef04c..350ae6f0 100644 --- a/scenes/BattleDirector/scripts/BattleDirector.cs +++ b/scenes/BattleDirector/scripts/BattleDirector.cs @@ -32,19 +32,31 @@ public partial class BattleDirector : Node2D private SongData _curSong; + [Export] + private NoteQueue NQ; + #endregion #region Note Handling private void PlayerAddNote(ArrowType type, int beat) { - GD.Print($"Player trying to place {type} typed note at beat: " + beat); + //TODO: note that should be added from the queue + Note note = NQ.GetCurrentNote(); + if (note == null) + { + GD.Print("No notes in queue"); + return; + } + + GD.Print($"Player trying to place {note.Name}:{type} typed note at beat: " + beat); if (!NotePlacementBar.CanPlaceNote()) return; - if (CD.AddNoteToLane(type, beat % CM.BeatsPerLoop, false)) + if (CD.AddNoteToLane(type, beat % CM.BeatsPerLoop, note, false)) { NotePlacementBar.PlacedNote(); NotePlaced?.Invoke(this); GD.Print("Note Placed."); + NQ.DequeueNote(); } } diff --git a/scenes/BattleDirector/scripts/Conductor.cs b/scenes/BattleDirector/scripts/Conductor.cs index e62ece9e..4d033b17 100644 --- a/scenes/BattleDirector/scripts/Conductor.cs +++ b/scenes/BattleDirector/scripts/Conductor.cs @@ -42,7 +42,7 @@ private bool IsNoteActive(ArrowType type, int beat) return _laneData[(int)type][beat] != null && _laneData[(int)type][beat].IsActive; } - public bool AddNoteToLane(ArrowType type, int beat, bool isActive = true) + public bool AddNoteToLane(ArrowType type, int beat, Note note, bool isActive = true) { beat %= CM.BeatsPerLoop; //Don't add dupe notes //Beat at 0 is too messy. @@ -54,12 +54,18 @@ public bool AddNoteToLane(ArrowType type, int beat, bool isActive = true) NoteArrow arrow; if (isActive) { - arrow = CM.AddArrowToLane(type, beat, Notes.Length - 1); + arrow = CM.AddArrowToLane(type, beat, Notes.Length - 1, note); arrow.NoteIdx = 1; } else { - arrow = CM.AddArrowToLane(type, beat, Notes.Length - 1, new Color(1, 0.43f, 0.26f)); + arrow = CM.AddArrowToLane( + type, + beat, + Notes.Length - 1, + note, + new Color(1, 0.43f, 0.26f) + ); arrow.NoteIdx = 0; } @@ -85,22 +91,22 @@ private void AddExampleNotes() GD.Print(CM.BeatsPerLoop); for (int i = 1; i < 15; i++) { - AddNoteToLane(ArrowType.Up, i * 4); + AddNoteToLane(ArrowType.Up, i * 4, Scribe.NoteDictionary[0]); } for (int i = 1; i < 15; i++) { - AddNoteToLane(ArrowType.Left, 4 * i + 1); + AddNoteToLane(ArrowType.Left, 4 * i + 1, Scribe.NoteDictionary[0]); } for (int i = 0; i < 10; i++) { - AddNoteToLane(ArrowType.Right, 3 * i + 32); + AddNoteToLane(ArrowType.Right, 3 * i + 32, Scribe.NoteDictionary[0]); } for (int i = 0; i < 3; i++) { - AddNoteToLane(ArrowType.Down, 8 * i + 16); + AddNoteToLane(ArrowType.Down, 8 * i + 16, Scribe.NoteDictionary[0]); } } diff --git a/scenes/BattleDirector/test_battle_scene.tscn b/scenes/BattleDirector/test_battle_scene.tscn index 945213cf..e7a41c2d 100644 --- a/scenes/BattleDirector/test_battle_scene.tscn +++ b/scenes/BattleDirector/test_battle_scene.tscn @@ -1,19 +1,21 @@ -[gd_scene load_steps=7 format=3 uid="uid://b0mrgr7h0ty1y"] +[gd_scene load_steps=9 format=3 uid="uid://b0mrgr7h0ty1y"] [ext_resource type="Script" path="res://scenes/BattleDirector/scripts/BattleDirector.cs" id="1_cwqqr"] [ext_resource type="PackedScene" uid="uid://dfevfib11kou1" path="res://scenes/ChartViewport/ChartViewport.tscn" id="2_cupb3"] [ext_resource type="Script" path="res://scenes/BattleDirector/scripts/Conductor.cs" id="2_pcp76"] [ext_resource type="Texture2D" uid="uid://ci0g72j8q4ec2" path="res://scenes/BattleDirector/assets/CoolBG.jpg" id="4_13o87"] [ext_resource type="PackedScene" uid="uid://duhiilcv4tat3" path="res://scenes/BattleDirector/NotePlacementBar.tscn" id="7_3ko4g"] +[ext_resource type="PackedScene" uid="uid://bvhpon5liybd1" path="res://scenes/CustomNotes/NoteQueue.tscn" id="8_7wwxo"] [ext_resource type="AudioStream" uid="uid://cv6lqjj6lu36h" path="res://Audio/335571__magntron__gamemusic_120bpm.mp3" id="8_caqms"] -[node name="ProtoBattleDirector" type="Node2D" node_paths=PackedStringArray("CM", "NotePlacementBar", "CD", "Audio")] +[node name="ProtoBattleDirector" type="Node2D" node_paths=PackedStringArray("CM", "NotePlacementBar", "CD", "Audio", "NQ")] process_mode = 1 script = ExtResource("1_cwqqr") CM = NodePath("SubViewport") NotePlacementBar = NodePath("NotePlacementBar") CD = NodePath("Conductor") Audio = NodePath("AudioStreamPlayer") +NQ = NodePath("NoteQueue") [node name="UILayer" type="CanvasLayer" parent="."] @@ -58,3 +60,14 @@ offset_right = 613.0 offset_bottom = 188.0 theme_override_font_sizes/font_size = 10 text = "Relics:" + +[node name="NoteQueue" parent="." instance=ExtResource("8_7wwxo")] +anchors_preset = 0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 100.0 +offset_top = 140.0 +offset_right = 100.0 +offset_bottom = 140.0 +grow_horizontal = 1 +grow_vertical = 1 diff --git a/scenes/ChartViewport/ChartManager.cs b/scenes/ChartViewport/ChartManager.cs index c44c3a2c..6e024ad0 100644 --- a/scenes/ChartViewport/ChartManager.cs +++ b/scenes/ChartViewport/ChartManager.cs @@ -102,11 +102,12 @@ public NoteArrow AddArrowToLane( ArrowType type, int beat, int noteIdx, + Note note, Color colorOverride = default ) { - var newNote = CreateNote(type, beat); - var loopArrow = CreateNote(type, beat + BeatsPerLoop); //Create a dummy arrow for looping visuals + var newNote = CreateNote(type, note, beat); + var loopArrow = CreateNote(type, note, beat + BeatsPerLoop); //Create a dummy arrow for looping visuals if (colorOverride != default) { newNote.Modulate = colorOverride; @@ -116,11 +117,11 @@ public NoteArrow AddArrowToLane( return newNote; } - private NoteArrow CreateNote(ArrowType arrow, int beat = 0) + private NoteArrow CreateNote(ArrowType arrow, Note note, int beat = 0) { var noteScene = ResourceLoader.Load("res://scenes/NoteManager/note.tscn"); NoteArrow newArrow = noteScene.Instantiate(); - newArrow.Init(IH.Arrows[(int)arrow], beat); + newArrow.Init(IH.Arrows[(int)arrow], beat, note); _arrowGroup.AddChild(newArrow); newArrow.Bounds = (float)((double)beat / BeatsPerLoop * (ChartLength / 2)); diff --git a/scenes/CustomNotes/NoteQueue.cs b/scenes/CustomNotes/NoteQueue.cs new file mode 100644 index 00000000..700a6c2f --- /dev/null +++ b/scenes/CustomNotes/NoteQueue.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using Godot; + +public partial class NoteQueue : Node +{ + [Export] + private Sprite2D _currentNote; + + [Export] + private Sprite2D _nextNote; + + private Queue _noteQueue = new Queue(); + private Dictionary _noteSprites = new Dictionary(); + + public override void _Ready() + { + _noteSprites["PlayerBase"] = GD.Load( + "res://scenes/CustomNotes/assets/single_note.png" + ); + _noteSprites["PlayerDouble"] = GD.Load( + "res://scenes/CustomNotes/assets/double_note.png" + ); + + LoadQueueFromSave(); + ScrambleQueue(); + UpdateQueue(); + } + + // Loads the notes from SaveData.json, and adds them to the queue + public void LoadQueueFromSave() + { + Dictionary savedNotes = SaveSystem.LoadNotes(); + foreach (var noteEntry in savedNotes) + { + string noteName = noteEntry.Key; + int numNotes = noteEntry.Value; + + for (int i = 0; i < numNotes; i++) + { + GD.Print($"Creating note from noteName: {noteName}"); + AddNoteToQueue(CreateNoteFromName(noteName)); + } + } + } + + // Creates a note from a string of the note's name. + private Note CreateNoteFromName(string noteName) + { + if (noteName == "PlayerBase") + return Scribe.NoteDictionary[1]; + + if (noteName == "PlayerDouble") + return Scribe.NoteDictionary[2]; + + GD.Print($"Failed to create not from noteName: {noteName}"); + return null; + } + + public void AddNoteToQueue(Note noteType) + { + _noteQueue.Enqueue(noteType); + UpdateQueue(); + } + + // Returns current note, and removes it from the queue + public Note GetCurrentNote() + { + if (_noteQueue.Count > 0) + { + return _noteQueue.Peek(); + } + return null; + } + + public void DequeueNote() + { + _noteQueue.Dequeue(); + UpdateQueue(); + } + + // Updates the queue's graphics + private void UpdateQueue() + { + if (_noteQueue.Count > 0 && _noteSprites.ContainsKey(_noteQueue.Peek().Name)) + _currentNote.Texture = _noteSprites[_noteQueue.Peek().Name]; + else + _currentNote.Texture = null; + + if (_noteQueue.Count > 1) + { + Note[] notes = _noteQueue.ToArray(); + if (_noteSprites.ContainsKey(notes[1].Name)) + _nextNote.Texture = _noteSprites[notes[1].Name]; + else + _nextNote.Texture = null; + } + else + { + _nextNote.Texture = null; + } + } + + // Fisher-Yates shuffle from: https://stackoverflow.com/a/1262619 + public void ScrambleQueue() + { + List tempList = new List(_noteQueue); + Random rng = new Random(); + + int n = tempList.Count; + while (n > 1) + { + n--; + int k = rng.Next(n + 1); + (tempList[k], tempList[n]) = (tempList[n], tempList[k]); + } + + _noteQueue = new Queue(tempList); + } + + //TODO: MAYBE? implement saving the notequeue to savedata +} diff --git a/scenes/CustomNotes/NoteQueue.tscn b/scenes/CustomNotes/NoteQueue.tscn new file mode 100644 index 00000000..d5e4b570 --- /dev/null +++ b/scenes/CustomNotes/NoteQueue.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=5 format=3 uid="uid://bvhpon5liybd1"] + +[ext_resource type="Script" path="res://scenes/CustomNotes/NoteQueue.cs" id="1_jeqam"] +[ext_resource type="Texture2D" uid="uid://cnyr5usjdv0ni" path="res://scenes/CustomNotes/assets/temp_note_queue.png" id="2_0p21a"] +[ext_resource type="Texture2D" uid="uid://c3chrsxrulapd" path="res://scenes/CustomNotes/assets/single_note.png" id="3_ewo1s"] +[ext_resource type="Texture2D" uid="uid://caw70lr5e1yiq" path="res://scenes/CustomNotes/assets/double_note.png" id="4_7sgy6"] + +[node name="NoteQueue" type="Control" node_paths=PackedStringArray("_currentNote", "_nextNote")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_jeqam") +_currentNote = NodePath("CurrentNote") +_nextNote = NodePath("NextNote") + +[node name="NoteQueueSprite" type="Sprite2D" parent="."] +texture = ExtResource("2_0p21a") + +[node name="CurrentNote" type="Sprite2D" parent="."] +position = Vector2(-14, -1) +texture = ExtResource("3_ewo1s") + +[node name="NextNote" type="Sprite2D" parent="."] +position = Vector2(16, -2) +texture = ExtResource("4_7sgy6") diff --git a/scenes/CustomNotes/assets/double_note.png b/scenes/CustomNotes/assets/double_note.png new file mode 100644 index 00000000..f9f8afc6 Binary files /dev/null and b/scenes/CustomNotes/assets/double_note.png differ diff --git a/scenes/CustomNotes/assets/double_note.png.import b/scenes/CustomNotes/assets/double_note.png.import new file mode 100644 index 00000000..cabfbea9 --- /dev/null +++ b/scenes/CustomNotes/assets/double_note.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://caw70lr5e1yiq" +path="res://.godot/imported/double_note.png-b077a9744f626fbc60d331cfc2e231a0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/CustomNotes/assets/double_note.png" +dest_files=["res://.godot/imported/double_note.png-b077a9744f626fbc60d331cfc2e231a0.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/scenes/CustomNotes/assets/single_note.png b/scenes/CustomNotes/assets/single_note.png new file mode 100644 index 00000000..4f62a191 Binary files /dev/null and b/scenes/CustomNotes/assets/single_note.png differ diff --git a/scenes/CustomNotes/assets/single_note.png.import b/scenes/CustomNotes/assets/single_note.png.import new file mode 100644 index 00000000..4b35b8b2 --- /dev/null +++ b/scenes/CustomNotes/assets/single_note.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c3chrsxrulapd" +path="res://.godot/imported/single_note.png-7542bb1712899b622105d29cce049bce.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/CustomNotes/assets/single_note.png" +dest_files=["res://.godot/imported/single_note.png-7542bb1712899b622105d29cce049bce.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/scenes/CustomNotes/assets/temp_note_queue.png b/scenes/CustomNotes/assets/temp_note_queue.png new file mode 100644 index 00000000..c6a556d0 Binary files /dev/null and b/scenes/CustomNotes/assets/temp_note_queue.png differ diff --git a/scenes/CustomNotes/assets/temp_note_queue.png.import b/scenes/CustomNotes/assets/temp_note_queue.png.import new file mode 100644 index 00000000..5fd17896 --- /dev/null +++ b/scenes/CustomNotes/assets/temp_note_queue.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cnyr5usjdv0ni" +path="res://.godot/imported/temp_note_queue.png-e563d517d16e6739ec71ecb1d9b4ade7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/CustomNotes/assets/temp_note_queue.png" +dest_files=["res://.godot/imported/temp_note_queue.png-e563d517d16e6739ec71ecb1d9b4ade7.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/scenes/NoteManager/scripts/NoteArrow.cs b/scenes/NoteManager/scripts/NoteArrow.cs index 71105b6b..220141d6 100644 --- a/scenes/NoteManager/scripts/NoteArrow.cs +++ b/scenes/NoteManager/scripts/NoteArrow.cs @@ -12,8 +12,9 @@ public partial class NoteArrow : Sprite2D public int Beat; public float Bounds; public bool IsActive; + public Note NoteRef; - public void Init(ArrowData parentArrowData, int beat) + public void Init(ArrowData parentArrowData, int beat, Note note) { ZIndex = 1;