diff --git a/Audio/335571__magntron__gamemusic_120bpm.mp3 b/Audio/335571__magntron__gamemusic_120bpm.mp3 new file mode 100644 index 00000000..de6a511f Binary files /dev/null and b/Audio/335571__magntron__gamemusic_120bpm.mp3 differ diff --git a/Audio/335571__magntron__gamemusic_120bpm.mp3.import b/Audio/335571__magntron__gamemusic_120bpm.mp3.import new file mode 100644 index 00000000..9c209bfe --- /dev/null +++ b/Audio/335571__magntron__gamemusic_120bpm.mp3.import @@ -0,0 +1,19 @@ +[remap] + +importer="mp3" +type="AudioStreamMP3" +uid="uid://cv6lqjj6lu36h" +path="res://.godot/imported/335571__magntron__gamemusic_120bpm.mp3-a87b357c4b3c9199709863b47f78bd2a.mp3str" + +[deps] + +source_file="res://Audio/335571__magntron__gamemusic_120bpm.mp3" +dest_files=["res://.godot/imported/335571__magntron__gamemusic_120bpm.mp3-a87b357c4b3c9199709863b47f78bd2a.mp3str"] + +[params] + +loop=true +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/Classes/Note.cs b/Classes/Note.cs new file mode 100644 index 00000000..193bbc3f --- /dev/null +++ b/Classes/Note.cs @@ -0,0 +1,18 @@ +using System; +using Godot; + +/** + * @class Note + * @brief Data structure class for holding data and methods for a battle time note. WIP + */ +public partial class Note : Resource +{ + public int Beat; + public NoteArrow.ArrowType Type; + + public Note(NoteArrow.ArrowType type = NoteArrow.ArrowType.Up, int beat = 0) + { + Beat = beat; + Type = type; + } +} diff --git a/Funk Engine.sln.DotSettings.user b/Funk Engine.sln.DotSettings.user new file mode 100644 index 00000000..6125b70c --- /dev/null +++ b/Funk Engine.sln.DotSettings.user @@ -0,0 +1,3 @@ + + ForceIncluded + ForceIncluded \ No newline at end of file diff --git a/README.md b/README.md index 5e6b8873..727c86ea 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,9 @@ Current team members include: +#### Attributions: +Note icon: Next icons created by Pixel perfect - Flaticon + +First Song: gameMusic by Magntron - freesound.org + + diff --git a/project.godot b/project.godot index c0855df0..ba8f8e20 100644 --- a/project.godot +++ b/project.godot @@ -11,10 +11,14 @@ config_version=5 [application] config/name="Funk Engine" -run/main_scene="res://scenes/main.tscn" +run/main_scene="res://scenes/BattleDirector/test_battle_scene.tscn" config/features=PackedStringArray("4.3", "C#", "Forward Plus") config/icon="res://icon.svg" +[autoload] + +TimeKeeper="*res://scripts/TimeKeeper.cs" + [display] window/size/viewport_width=640 @@ -25,3 +29,38 @@ window/stretch/scale_mode="integer" [dotnet] project/assembly_name="Funk Engine" + +[input] + +arrowUp={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +arrowDown={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +arrowLeft={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +arrowRight={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} + +[rendering] + +textures/canvas_textures/default_texture_filter=0 + +[threading] + +worker_pool/canvas_textures/default_texture_filter=1 diff --git a/scenes/BattleDirector/BattleDirector.cs b/scenes/BattleDirector/BattleDirector.cs new file mode 100644 index 00000000..6de6518c --- /dev/null +++ b/scenes/BattleDirector/BattleDirector.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Godot; + +/** + * @class BattleDirector + * @brief Higher priority director to manage battle effects. Can directly access managers, which should signal up to Director WIP + */ +public partial class BattleDirector : Node2D +{ + #region Declarations + private HealthBar Player; + private HealthBar Enemy; + + [Export] + private ChartManager CM; + + [Export] + private InputHandler IH; + + [Export] + private NotePlacementBar NotePlacementBar; + + [Export] + private AudioStreamPlayer Audio; + + private double _timingInterval = .1; //secs + + [Signal] + public delegate void PlayerDamageEventHandler(int damage); + + [Signal] + public delegate void EnemyDamageEventHandler(int damage); + + private SongData _curSong; + + public struct SongData + { + public int Bpm; + public double SongLength; + public int NumLoops; + } + #endregion + + #region Note Handling + //Assume queue structure for notes in each lane. + //Can eventually make this its own structure + private NoteArrow[][] _laneData = Array.Empty(); + private int[] _laneLastBeat = new int[] + { //Temporary (hopefully) measure to bridge from note queue structure to ordered array + 0, + 0, + 0, + 0, + }; + private Note[] _notes = Array.Empty(); + + //Returns first note of lane without modifying lane data + private Note GetNoteAt(NoteArrow.ArrowType dir, int beat) + { + return GetNote(_laneData[(int)dir][beat]); + } + + //Get note of a note arrow + private Note GetNote(NoteArrow arrow) + { + return _notes[arrow.NoteIdx]; + } + + private bool AddNoteToLane(Note note, bool isActive = true) + { + note.Beat %= CM.BeatsPerLoop; + //Don't add dupe notes + if (note.Beat == 0 || _notes.Any(nt => nt.Type == note.Type && nt.Beat == note.Beat)) + { + return false; //Beat at 0 is too messy. + } + _notes = _notes.Append(note).ToArray(); + //Get noteArrow from CM + var arrow = CM.AddArrowToLane(note, _notes.Length - 1); + arrow.IsActive = isActive; + _laneData[(int)note.Type][note.Beat] = arrow; + return true; + } + #endregion + + //Creeate dummy notes + private void AddExampleNotes() + { + GD.Print(CM.BeatsPerLoop); + for (int i = 1; i < 15; i++) + { + Note exampleNote = new Note(NoteArrow.ArrowType.Up, i * 4); + AddNoteToLane(exampleNote); + } + for (int i = 1; i < 15; i++) + { + Note exampleNote = new Note(NoteArrow.ArrowType.Left, 4 * i + 1); + AddNoteToLane(exampleNote); + } + for (int i = 0; i < 10; i++) + { + Note exampleNote = new Note(NoteArrow.ArrowType.Right, 3 * i + 32); + AddNoteToLane(exampleNote); + } + for (int i = 0; i < 3; i++) + { + Note exampleNote = new Note(NoteArrow.ArrowType.Down, 8 * i + 16); + AddNoteToLane(exampleNote); + } + } + + public override void _Ready() + { + _curSong = new SongData + { + Bpm = 120, + SongLength = Audio.Stream.GetLength(), + NumLoops = 5, + }; + + var timer = GetTree().CreateTimer(AudioServer.GetTimeToNextMix()); + timer.Timeout += Begin; + } + + private void Begin() + { + CM.PrepChart(_curSong); + _laneData = new NoteArrow[][] + { + new NoteArrow[CM.BeatsPerLoop], + new NoteArrow[CM.BeatsPerLoop], + new NoteArrow[CM.BeatsPerLoop], + new NoteArrow[CM.BeatsPerLoop], + }; + AddExampleNotes(); + + Player = GetNode("PlayerHP"); + Player.GetNode("Sprite2D").Scale *= .5f; //TEMP + Player.GetNode("Sprite2D").Position += Vector2.Down * 30; //TEMP + Enemy = GetNode("EnemyHP"); + + //TEMP + var enemTween = CreateTween(); + enemTween + .TweenProperty(Enemy.GetNode("Sprite2D"), "position", Vector2.Down * 5, 1f) + .AsRelative(); + enemTween + .TweenProperty(Enemy.GetNode("Sprite2D"), "position", Vector2.Up * 5, 1f) + .AsRelative(); + enemTween.SetTrans(Tween.TransitionType.Spring); + enemTween.SetEase(Tween.EaseType.In); + enemTween.SetLoops(); + enemTween.Play(); + + CM.Connect(nameof(InputHandler.NotePressed), new Callable(this, nameof(OnNotePressed))); + CM.Connect(nameof(InputHandler.NoteReleased), new Callable(this, nameof(OnNoteReleased))); + + Audio.Play(); + } + + public override void _Process(double delta) + { + TimeKeeper.CurrentTime = Audio.GetPlaybackPosition(); + CheckMiss(); + } + + #region Input&Timing + private void OnNotePressed(NoteArrow.ArrowType type) + { + CheckNoteTiming(type); + } + + private void OnNoteReleased(NoteArrow.ArrowType arrowType) { } + + //Check all lanes for misses from missed inputs + private void CheckMiss() + { + //On current beat, if prev beat is active and not inputted + double realBeat = TimeKeeper.CurrentTime / (60 / (double)_curSong.Bpm) % CM.BeatsPerLoop; + for (int i = 0; i < _laneData.Length; i++) + { + if ( + _laneLastBeat[i] < Math.Floor(realBeat) + || (_laneLastBeat[i] == CM.BeatsPerLoop - 1 && Math.Floor(realBeat) == 0) + ) + { //If above, a note has been missed + //GD.Print("Last beat " + _laneLastBeat[i]); + if ( + _laneData[i][_laneLastBeat[i]] == null + || !_laneData[i][_laneLastBeat[i]].IsActive + ) + { + _laneLastBeat[i] = (_laneLastBeat[i] + 1) % CM.BeatsPerLoop; + continue; + } + //Note exists and has been missed + _laneData[i][_laneLastBeat[i]].NoteHit(); + HandleTiming((NoteArrow.ArrowType)i, 1); + _laneLastBeat[i] = (_laneLastBeat[i] + 1) % CM.BeatsPerLoop; + } + } + } + + private void CheckNoteTiming(NoteArrow.ArrowType type) + { + double realBeat = TimeKeeper.CurrentTime / (60 / (double)_curSong.Bpm) % CM.BeatsPerLoop; + int curBeat = (int)Math.Round(realBeat); + GD.Print("Cur beat " + curBeat + "Real: " + realBeat.ToString("#.###")); + if ( + _laneData[(int)type][curBeat % CM.BeatsPerLoop] == null + || !_laneData[(int)type][curBeat % CM.BeatsPerLoop].IsActive + ) + { + _laneLastBeat[(int)type] = (curBeat) % CM.BeatsPerLoop; + PlayerAddNote(type, curBeat); + return; + } + double beatDif = Math.Abs(realBeat - curBeat); + _laneData[(int)type][curBeat % CM.BeatsPerLoop].NoteHit(); + _laneLastBeat[(int)type] = (curBeat) % CM.BeatsPerLoop; + HandleTiming(type, beatDif); + } + + private void HandleTiming(NoteArrow.ArrowType type, double beatDif) + { + if (beatDif < _timingInterval * 1) + { + GD.Print("Perfect"); + Enemy.TakeDamage(3); + NotePlacementBar.HitNote(); + NotePlacementBar.ComboText("Perfect!"); + } + else if (beatDif < _timingInterval * 2) + { + GD.Print("Good"); + Enemy.TakeDamage(1); + NotePlacementBar.HitNote(); + NotePlacementBar.ComboText("Good"); + } + else if (beatDif < _timingInterval * 3) + { + GD.Print("Ok"); + Player.TakeDamage(1); + NotePlacementBar.HitNote(); + NotePlacementBar.ComboText("Okay"); + } + else + { + GD.Print("Miss"); + Player.TakeDamage(2); + NotePlacementBar.MissNote(); + NotePlacementBar.ComboText("Miss"); + } + } + #endregion + + private void PlayerAddNote(NoteArrow.ArrowType type, int beat) + { + // can also add some sort of keybind here to also have pressed + // in case the user just presses the note too early and spawns a note + GD.Print( + $"Player trying to place {type} typed note at beat: " + + beat + + " Verdict: " + + NotePlacementBar.CanPlaceNote() + ); + if (NotePlacementBar.CanPlaceNote()) + { + Note exampleNote = new Note(type, beat % CM.BeatsPerLoop); + if (AddNoteToLane(exampleNote, false)) + NotePlacementBar.PlacedNote(); + } + } +} diff --git a/scenes/BattleDirector/HealthBar.cs b/scenes/BattleDirector/HealthBar.cs new file mode 100644 index 00000000..c0ad3e2e --- /dev/null +++ b/scenes/BattleDirector/HealthBar.cs @@ -0,0 +1,48 @@ +using System; +using Godot; + +public partial class HealthBar : Control +{ + const int MaxHealth = 100; + int _health = MaxHealth; + + [Export] + public TextureProgressBar PlayerHealthBar; + + [Export] + public Texture2D SpriteText; + + //we can change this to a Texture Progress bar once we have art assets for it + + + public override void _Ready() + { + if (PlayerHealthBar != null) + { + SetHealth(MaxHealth, MaxHealth); + } + GetNode("Sprite2D").Texture = SpriteText; + } + + public void SetHealth(int max, int current) + { + PlayerHealthBar.MaxValue = max; + PlayerHealthBar.Value = current; + _updateHealthBar(); + } + + private void _updateHealthBar() + { + PlayerHealthBar.Value = _health; + } + + public void TakeDamage(int damage) + { + _health -= damage; + if (_health <= 0) + { + GD.Print("You are dead"); + } + _updateHealthBar(); + } +} diff --git a/scenes/BattleDirector/HealthBar.tscn b/scenes/BattleDirector/HealthBar.tscn new file mode 100644 index 00000000..0f72abd7 --- /dev/null +++ b/scenes/BattleDirector/HealthBar.tscn @@ -0,0 +1,47 @@ +[gd_scene load_steps=8 format=3 uid="uid://bgomxovxs7sr8"] + +[ext_resource type="Script" path="res://scenes/BattleDirector/HealthBar.cs" id="1_b1t4i"] + +[sub_resource type="Gradient" id="Gradient_ve5ki"] +colors = PackedColorArray(0, 0, 0, 1, 0, 0, 0, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_ti0cv"] +gradient = SubResource("Gradient_ve5ki") +width = 150 +height = 20 + +[sub_resource type="Gradient" id="Gradient_soqhm"] +colors = PackedColorArray(0, 1, 0.0999999, 1, 1, 1, 1, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_r4hau"] +gradient = SubResource("Gradient_soqhm") +width = 146 +height = 16 + +[sub_resource type="Gradient" id="Gradient_58kj0"] +offsets = PackedFloat32Array(1) +colors = PackedColorArray(1, 1, 1, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_wwca1"] +gradient = SubResource("Gradient_58kj0") + +[node name="Control" type="Control" node_paths=PackedStringArray("PlayerHealthBar")] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("1_b1t4i") +PlayerHealthBar = NodePath("ProgressBar") + +[node name="ProgressBar" type="TextureProgressBar" parent="."] +layout_mode = 0 +offset_right = 150.0 +offset_bottom = 20.0 +texture_under = SubResource("GradientTexture2D_ti0cv") +texture_progress = SubResource("GradientTexture2D_r4hau") +texture_progress_offset = Vector2(2, 2) + +[node name="Sprite2D" type="Sprite2D" parent="."] +position = Vector2(75, 86) +scale = Vector2(2, 2) +texture = SubResource("GradientTexture2D_wwca1") diff --git a/scenes/BattleDirector/NotePlacementBar.cs b/scenes/BattleDirector/NotePlacementBar.cs new file mode 100644 index 00000000..d86a3c9a --- /dev/null +++ b/scenes/BattleDirector/NotePlacementBar.cs @@ -0,0 +1,82 @@ +using System; +using Godot; + +public partial class NotePlacementBar : Node +{ + const int MaxValue = 80; + int currentBarValue; + int currentCombo; + int comboMult; + int notesToIncreaseCombo; + + [Export] + TextureProgressBar notePlacementBar; + + [Export] + TextEdit currentComboMultText; + + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + notePlacementBar.MaxValue = MaxValue; + currentBarValue = 0; + currentCombo = 0; + comboMult = 1; + notesToIncreaseCombo = 4; + } + + public void ComboText(string text) + { + var feedbackScene = ResourceLoader.Load( + "res://scenes/BattleDirector/TextParticle.tscn" + ); + TextParticle newText = feedbackScene.Instantiate(); + AddChild(newText); + newText.Text = text + $" {currentCombo}"; + } + + // Hitting a note increases combo, combo mult, and note placement bar + public void HitNote() + { + currentCombo++; + DetermineComboMult(); + currentBarValue += comboMult; + UpdateNotePlacementBar(currentBarValue); + UpdateComboMultText(); + } + + // Missing a note resets combo + public void MissNote() + { + currentCombo = 0; + DetermineComboMult(); + UpdateComboMultText(); + } + + // Placing a note resets the note placement bar + public void PlacedNote() + { + currentBarValue = 0; + UpdateNotePlacementBar(currentBarValue); + } + + public bool CanPlaceNote() + { + return currentBarValue >= MaxValue; + } + + private void DetermineComboMult() + { + comboMult = currentCombo / notesToIncreaseCombo + 1; + } + + public void UpdateNotePlacementBar(int newValue) + { + notePlacementBar.Value = newValue; + } + + public void UpdateComboMultText() + { + currentComboMultText.Text = $"x{comboMult.ToString()}"; + } +} diff --git a/scenes/BattleDirector/NotePlacementBar.tscn b/scenes/BattleDirector/NotePlacementBar.tscn new file mode 100644 index 00000000..c8dc9577 --- /dev/null +++ b/scenes/BattleDirector/NotePlacementBar.tscn @@ -0,0 +1,56 @@ +[gd_scene load_steps=6 format=3 uid="uid://duhiilcv4tat3"] + +[ext_resource type="Script" path="res://scenes/BattleDirector/NotePlacementBar.cs" id="1_456es"] + +[sub_resource type="Gradient" id="Gradient_0u6yv"] +colors = PackedColorArray(0, 0, 0, 1, 0, 0, 0, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_hhds4"] +gradient = SubResource("Gradient_0u6yv") +width = 26 +height = 100 + +[sub_resource type="Gradient" id="Gradient_lyd0l"] +offsets = PackedFloat32Array(0, 0.485915, 0.739437, 1) +colors = PackedColorArray(0, 1, 0, 1, 0.68, 0, 0.272, 1, 0, 0.64, 0.608, 1, 0.4, 0, 0, 1) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_k7kvy"] +gradient = SubResource("Gradient_lyd0l") +width = 24 +height = 98 +fill_to = Vector2(0, 1) + +[node name="NotePlacementBar" type="Control" node_paths=PackedStringArray("notePlacementBar", "currentComboMultText")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_456es") +notePlacementBar = NodePath("ProgressBar") +currentComboMultText = NodePath("TextEdit") + +[node name="ProgressBar" type="TextureProgressBar" parent="."] +layout_mode = 0 +offset_right = 26.0 +offset_bottom = 100.0 +fill_mode = 3 +texture_under = SubResource("GradientTexture2D_hhds4") +texture_progress = SubResource("GradientTexture2D_k7kvy") +texture_progress_offset = Vector2(1, 1) + +[node name="TextEdit" type="TextEdit" parent="."] +layout_mode = 0 +offset_top = -41.0 +offset_right = 50.0 +offset_bottom = -6.0 +mouse_filter = 2 +text = "x1" +context_menu_enabled = false +shortcut_keys_enabled = false +selecting_enabled = false +deselect_on_focus_loss_enabled = false +drag_and_drop_selection_enabled = false +virtual_keyboard_enabled = false +middle_mouse_paste_enabled = false diff --git a/scenes/BattleDirector/TextParticle.cs b/scenes/BattleDirector/TextParticle.cs new file mode 100644 index 00000000..273d4967 --- /dev/null +++ b/scenes/BattleDirector/TextParticle.cs @@ -0,0 +1,23 @@ +using System; +using Godot; + +public partial class TextParticle : Label +{ + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + Tween tween = GetTree().CreateTween(); + Position += Vector2.Left * (GD.Randf() * 40 - 20); + tween.SetTrans(Tween.TransitionType.Quad); + tween.SetEase(Tween.EaseType.Out); + tween.TweenProperty(this, "position", Position + Vector2.Up * 10, .5f); + tween.TweenProperty(this, "position", Position + Vector2.Down * 20, .5f); + tween.SetParallel(); + tween.TweenProperty(this, "modulate:a", 0, 1f); + tween.SetParallel(false); + tween.TweenCallback(Callable.From(QueueFree)); + } + + // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(double delta) { } +} diff --git a/scenes/BattleDirector/TextParticle.tscn b/scenes/BattleDirector/TextParticle.tscn new file mode 100644 index 00000000..6491a671 --- /dev/null +++ b/scenes/BattleDirector/TextParticle.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=3 uid="uid://bd23wwbv7i4gg"] + +[ext_resource type="Script" path="res://scenes/BattleDirector/TextParticle.cs" id="1_j0ufq"] + +[node name="Control" type="Label"] +anchors_preset = -1 +anchor_right = 0.08 +text = "900000" +script = ExtResource("1_j0ufq") diff --git a/scenes/BattleDirector/assets/Character1.png b/scenes/BattleDirector/assets/Character1.png new file mode 100644 index 00000000..2175831d Binary files /dev/null and b/scenes/BattleDirector/assets/Character1.png differ diff --git a/scenes/BattleDirector/assets/Character1.png.import b/scenes/BattleDirector/assets/Character1.png.import new file mode 100644 index 00000000..8adcfe54 --- /dev/null +++ b/scenes/BattleDirector/assets/Character1.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6fkei0i83vte" +path="res://.godot/imported/Character1.png-d73d4a980ad79d80867b72bd7a029fb4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/BattleDirector/assets/Character1.png" +dest_files=["res://.godot/imported/Character1.png-d73d4a980ad79d80867b72bd7a029fb4.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/BattleDirector/assets/CoolBG.jpg b/scenes/BattleDirector/assets/CoolBG.jpg new file mode 100644 index 00000000..60734eb2 Binary files /dev/null and b/scenes/BattleDirector/assets/CoolBG.jpg differ diff --git a/scenes/BattleDirector/assets/CoolBG.jpg.import b/scenes/BattleDirector/assets/CoolBG.jpg.import new file mode 100644 index 00000000..e4f537c4 --- /dev/null +++ b/scenes/BattleDirector/assets/CoolBG.jpg.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ci0g72j8q4ec2" +path="res://.godot/imported/CoolBG.jpg-3bad5dc436fec2c6453223cfa1feeeb8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/BattleDirector/assets/CoolBG.jpg" +dest_files=["res://.godot/imported/CoolBG.jpg-3bad5dc436fec2c6453223cfa1feeeb8.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/BattleDirector/assets/Enemy1.png b/scenes/BattleDirector/assets/Enemy1.png new file mode 100644 index 00000000..5a844a04 Binary files /dev/null and b/scenes/BattleDirector/assets/Enemy1.png differ diff --git a/scenes/BattleDirector/assets/Enemy1.png.import b/scenes/BattleDirector/assets/Enemy1.png.import new file mode 100644 index 00000000..a6b8781d --- /dev/null +++ b/scenes/BattleDirector/assets/Enemy1.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://veedngaorx3l" +path="res://.godot/imported/Enemy1.png-fb95639d1545a326b6c39640343f3786.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/BattleDirector/assets/Enemy1.png" +dest_files=["res://.godot/imported/Enemy1.png-fb95639d1545a326b6c39640343f3786.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/BattleDirector/test_battle_scene.tscn b/scenes/BattleDirector/test_battle_scene.tscn new file mode 100644 index 00000000..772d4487 --- /dev/null +++ b/scenes/BattleDirector/test_battle_scene.tscn @@ -0,0 +1,61 @@ +[gd_scene load_steps=9 format=3 uid="uid://b0mrgr7h0ty1y"] + +[ext_resource type="Script" path="res://scenes/BattleDirector/BattleDirector.cs" id="1_cwqqr"] +[ext_resource type="PackedScene" uid="uid://dfevfib11kou1" path="res://scenes/ChartViewport/ChartViewport.tscn" id="2_cupb3"] +[ext_resource type="PackedScene" uid="uid://bgomxovxs7sr8" path="res://scenes/BattleDirector/HealthBar.tscn" id="3_pp0u0"] +[ext_resource type="Texture2D" uid="uid://ci0g72j8q4ec2" path="res://scenes/BattleDirector/assets/CoolBG.jpg" id="4_13o87"] +[ext_resource type="Texture2D" uid="uid://b6fkei0i83vte" path="res://scenes/BattleDirector/assets/Character1.png" id="5_elveq"] +[ext_resource type="Texture2D" uid="uid://veedngaorx3l" path="res://scenes/BattleDirector/assets/Enemy1.png" id="6_0k4pw"] +[ext_resource type="PackedScene" uid="uid://duhiilcv4tat3" path="res://scenes/BattleDirector/NotePlacementBar.tscn" id="7_3ko4g"] +[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", "IH", "NotePlacementBar", "Audio")] +script = ExtResource("1_cwqqr") +CM = NodePath("SubViewport") +IH = NodePath("SubViewport/SubViewport/noteManager") +NotePlacementBar = NodePath("NotePlacementBar") +Audio = NodePath("AudioStreamPlayer") + +[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] +stream = ExtResource("8_caqms") + +[node name="SubViewport" parent="." instance=ExtResource("2_cupb3")] +offset_left = 80.0 +offset_top = 160.0 +offset_right = 560.0 +offset_bottom = 360.0 + +[node name="ColorRect" type="TextureRect" parent="."] +z_index = -1 +offset_right = 640.0 +offset_bottom = 360.0 +texture = ExtResource("4_13o87") + +[node name="ColorRect2" type="ColorRect" parent="."] +self_modulate = Color(0.36, 0.36, 0.36, 0.780392) +z_index = -1 +offset_left = -70.0 +offset_top = 160.0 +offset_right = 673.0 +offset_bottom = 360.0 +color = Color(0.165656, 0.165656, 0.165656, 1) + +[node name="PlayerHP" parent="." instance=ExtResource("3_pp0u0")] +offset_left = 92.0 +offset_top = 8.0 +offset_right = 132.0 +offset_bottom = 48.0 +SpriteText = ExtResource("5_elveq") + +[node name="EnemyHP" parent="." instance=ExtResource("3_pp0u0")] +offset_left = 403.0 +offset_top = 8.0 +offset_right = 443.0 +offset_bottom = 52.0 +SpriteText = ExtResource("6_0k4pw") + +[node name="NotePlacementBar" parent="." instance=ExtResource("7_3ko4g")] +offset_left = 16.0 +offset_top = 164.0 +offset_right = 16.0 +offset_bottom = 164.0 diff --git a/scenes/ChartViewport/ChartManager.cs b/scenes/ChartViewport/ChartManager.cs new file mode 100644 index 00000000..4ecae9e4 --- /dev/null +++ b/scenes/ChartViewport/ChartManager.cs @@ -0,0 +1,120 @@ +using System; +using System.Linq; +using Godot; +using ArrowType = NoteArrow.ArrowType; + +/** + * @class ChartManager + * @brief Chart Manager is meant to handle the visual aspects of a battle. Setting up the chart background, initial notes, and handle looping. WIP + */ +public partial class ChartManager : SubViewportContainer +{ + //Nodes from scene + [Export] + public InputHandler IH; + + [Export] + public CanvasGroup ChartLoopables; + + private Node _arrowGroup; + + [Signal] + public delegate void NotePressedEventHandler(ArrowType arrowType); + + [Signal] + public delegate void NoteReleasedEventHandler(ArrowType arrowType); + + //Arbitrary vars, play with these + private double ChartLength = 5000; //Might move this to be song specific? + private double _loopLen; //secs + public int BeatsPerLoop; + + public void OnNotePressed(ArrowType type) + { + EmitSignal(nameof(NotePressed), (int)type); + } + + public void OnNoteReleased(ArrowType type) + { + EmitSignal(nameof(NoteReleased), (int)type); + } + + public void PrepChart(BattleDirector.SongData songData) + { + _loopLen = songData.SongLength / songData.NumLoops; + TimeKeeper.LoopLength = (float)_loopLen; + BeatsPerLoop = (int)(_loopLen / (60f / songData.Bpm)); + ChartLength = (float)_loopLen * (float)Math.Floor(ChartLength / _loopLen); + TimeKeeper.ChartLength = (float)ChartLength; + TimeKeeper.Bpm = songData.Bpm; + + InitBackgrounds(); + _arrowGroup = ChartLoopables.GetNode("ArrowGroup"); + + IH.Connect(nameof(InputHandler.NotePressed), new Callable(this, nameof(OnNotePressed))); + IH.Connect(nameof(InputHandler.NoteReleased), new Callable(this, nameof(OnNoteReleased))); + + //This could be good as a function to call on something, to have many things animated to the beat. + var tween = GetTree().CreateTween(); + tween + .TweenMethod( + Callable.From((Vector2 scale) => TweenArrows(scale)), + new Vector2(0.07f, 0.07f), + new Vector2(0.07f, 0.07f) * 1.25f, + 60f / TimeKeeper.Bpm / 2 + ) + .SetEase(Tween.EaseType.Out) + .SetTrans(Tween.TransitionType.Elastic); + tween.TweenMethod( + Callable.From((Vector2 scale) => TweenArrows(scale)), + new Vector2(0.07f, 0.07f) * 1.25f, + new Vector2(0.07f, 0.07f), + 60f / TimeKeeper.Bpm / 2 + ); + tween.SetLoops().Play(); + } + + private void InitBackgrounds() + { + int i = 0; + foreach (Node child in ChartLoopables.GetChildren()) + { + if (child is not Loopable) + continue; + Loopable loopable = (Loopable)child; + loopable.Position = Vector2.Zero; + loopable.SetSize(new Vector2((float)ChartLength / 2 + 1, Size.Y)); + loopable.Bounds = (float)ChartLength / 2 * i; + i++; + } + } + + private void TweenArrows(Vector2 scale) + { + foreach (var node in _arrowGroup.GetChildren()) + { + NoteArrow arrow = (NoteArrow)node; + arrow.Scale = scale; + } + } + + public NoteArrow AddArrowToLane(Note note, int noteIdx) + { + var newNote = CreateNote(note.Type, note.Beat); + CreateNote(note.Type, note.Beat + BeatsPerLoop); //Create a dummy arrow for looping visuals + newNote.NoteIdx = noteIdx; + return newNote; + } + + private NoteArrow CreateNote(ArrowType arrow, int beat = 0) + { + var noteScene = ResourceLoader.Load("res://scenes/NoteManager/note.tscn"); + NoteArrow newArrow = noteScene.Instantiate(); + newArrow.Init(IH.Arrows[(int)arrow]); + + _arrowGroup.AddChild(newArrow); + newArrow.Bounds = (float)((double)beat / BeatsPerLoop * (ChartLength / 2)); + newArrow.Position += Vector2.Right * newArrow.Bounds * 10; //temporary fix for notes spawning and instantly calling loop from originating at 0,0 + return newArrow; + } +} diff --git a/scenes/ChartViewport/ChartViewport.tscn b/scenes/ChartViewport/ChartViewport.tscn new file mode 100644 index 00000000..f6f9fb6a --- /dev/null +++ b/scenes/ChartViewport/ChartViewport.tscn @@ -0,0 +1,50 @@ +[gd_scene load_steps=5 format=3 uid="uid://dfevfib11kou1"] + +[ext_resource type="Texture2D" uid="uid://b0tvsewgnf2x7" path="res://icon.svg" id="1_0wnka"] +[ext_resource type="Script" path="res://scenes/ChartViewport/ChartManager.cs" id="1_ruh2l"] +[ext_resource type="Script" path="res://scenes/ChartViewport/Loopable.cs" id="3_5u57h"] +[ext_resource type="PackedScene" uid="uid://bn8txx53xlguw" path="res://scenes/NoteManager/note_manager.tscn" id="4_fd5fw"] + +[node name="VPContainer" type="SubViewportContainer" node_paths=PackedStringArray("IH", "ChartLoopables")] +offset_right = 480.0 +offset_bottom = 200.0 +script = ExtResource("1_ruh2l") +IH = NodePath("SubViewport/noteManager") +ChartLoopables = NodePath("SubViewport/ChartLoopables") + +[node name="SubViewport" type="SubViewport" parent="."] +handle_input_locally = false +size = Vector2i(480, 200) +render_target_update_mode = 4 + +[node name="ColorFill" type="ColorRect" parent="SubViewport"] +z_index = -2 +offset_left = -30.0 +offset_right = 610.0 +offset_bottom = 360.0 +color = Color(0.258824, 0.290196, 0.392157, 1) + +[node name="Camera2D" type="Camera2D" parent="SubViewport"] +position = Vector2(-25, 0) +anchor_mode = 0 + +[node name="ChartLoopables" type="CanvasGroup" parent="SubViewport"] +unique_name_in_owner = true + +[node name="ArrowGroup" type="Node" parent="SubViewport/ChartLoopables"] + +[node name="ChartBG2" type="TextureRect" parent="SubViewport/ChartLoopables"] +modulate = Color(2, 2, 2, 1) +offset_right = 701.0 +offset_bottom = 300.0 +texture = ExtResource("1_0wnka") +script = ExtResource("3_5u57h") + +[node name="ChartBG1" type="TextureRect" parent="SubViewport/ChartLoopables"] +modulate = Color(2, 2, 2, 1) +offset_right = 701.0 +offset_bottom = 300.0 +texture = ExtResource("1_0wnka") +script = ExtResource("3_5u57h") + +[node name="noteManager" parent="SubViewport" instance=ExtResource("4_fd5fw")] diff --git a/scenes/ChartViewport/Loopable.cs b/scenes/ChartViewport/Loopable.cs new file mode 100644 index 00000000..45e3487e --- /dev/null +++ b/scenes/ChartViewport/Loopable.cs @@ -0,0 +1,26 @@ +using System; +using Godot; + +/** + * @class Loopable + * @brief A general class fo textures on the chart which should have a position at which point they loop. WIP + */ +public partial class Loopable : TextureRect +{ + [Export] + public float Bounds = 700f; //px Pos to loop or do something at. + + // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(double delta) + { + Vector2 newPos = Position; + //Loop position over the course of time across a loop + newPos.X = + (float)( + (-TimeKeeper.CurrentTime / TimeKeeper.LoopLength * TimeKeeper.ChartLength) + % TimeKeeper.ChartLength + / 2 + ) + Bounds; + Position = newPos; + } +} diff --git a/scenes/NoteManager/assets/right-arrow.png b/scenes/NoteManager/assets/right-arrow.png new file mode 100644 index 00000000..d5c416b7 Binary files /dev/null and b/scenes/NoteManager/assets/right-arrow.png differ diff --git a/scenes/NoteManager/assets/right-arrow.png.import b/scenes/NoteManager/assets/right-arrow.png.import new file mode 100644 index 00000000..5a9baa01 --- /dev/null +++ b/scenes/NoteManager/assets/right-arrow.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bucvj4fquqpkr" +path="res://.godot/imported/right-arrow.png-b3005485b42777a77a9d39fbba48875d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://scenes/NoteManager/assets/right-arrow.png" +dest_files=["res://.godot/imported/right-arrow.png-b3005485b42777a77a9d39fbba48875d.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/note.tscn b/scenes/NoteManager/note.tscn new file mode 100644 index 00000000..e0b2aa76 --- /dev/null +++ b/scenes/NoteManager/note.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=3 format=3 uid="uid://ck3bfqy30rjbq"] + +[ext_resource type="Texture2D" uid="uid://bucvj4fquqpkr" path="res://scenes/NoteManager/assets/right-arrow.png" id="1_kubiy"] +[ext_resource type="Script" path="res://scenes/NoteManager/scripts/NoteArrow.cs" id="2_lbl4b"] + +[node name="Right-arrow" type="Sprite2D"] +scale = Vector2(0.07, 0.07) +texture = ExtResource("1_kubiy") +script = ExtResource("2_lbl4b") diff --git a/scenes/NoteManager/note_manager.tscn b/scenes/NoteManager/note_manager.tscn new file mode 100644 index 00000000..82ab15fd --- /dev/null +++ b/scenes/NoteManager/note_manager.tscn @@ -0,0 +1,60 @@ +[gd_scene load_steps=5 format=3 uid="uid://bn8txx53xlguw"] + +[ext_resource type="Script" path="res://scenes/NoteManager/scripts/InputHandler.cs" id="1_2oeuf"] +[ext_resource type="Texture2D" uid="uid://bucvj4fquqpkr" path="res://scenes/NoteManager/assets/right-arrow.png" id="2_ldmuy"] +[ext_resource type="Script" path="res://scenes/NoteManager/scripts/NoteChecker.cs" id="3_0cioe"] +[ext_resource type="Texture2D" uid="uid://b0tvsewgnf2x7" path="res://icon.svg" id="4_foklt"] + +[node name="noteManager" type="Node2D"] +script = ExtResource("1_2oeuf") + +[node name="noteCheckers" type="Node2D" parent="."] + +[node name="arrowUp" type="Sprite2D" parent="noteCheckers"] +position = Vector2(0, 24) +rotation = -1.5708 +scale = Vector2(0.09, 0.09) +texture = ExtResource("2_ldmuy") +script = ExtResource("3_0cioe") + +[node name="arrowLeft" type="Sprite2D" parent="noteCheckers"] +position = Vector2(0, 76) +rotation = 3.14159 +scale = Vector2(0.09, 0.09) +texture = ExtResource("2_ldmuy") +script = ExtResource("3_0cioe") + +[node name="arrowDown" type="Sprite2D" parent="noteCheckers"] +position = Vector2(0, 129) +rotation = 1.5708 +scale = Vector2(0.09, 0.09) +texture = ExtResource("2_ldmuy") +script = ExtResource("3_0cioe") + +[node name="arrowRight" type="Sprite2D" parent="noteCheckers"] +position = Vector2(0, 181) +scale = Vector2(0.09, 0.09) +texture = ExtResource("2_ldmuy") +script = ExtResource("3_0cioe") + +[node name="ui" type="Node2D" parent="."] + +[node name="dividers" type="Node2D" parent="ui"] + +[node name="DivA" type="Sprite2D" parent="ui/dividers"] +modulate = Color(0, 0, 0, 1) +position = Vector2(295, 53) +scale = Vector2(5.10938, 0.041) +texture = ExtResource("4_foklt") + +[node name="DivB" type="Sprite2D" parent="ui/dividers"] +modulate = Color(0, 0, 0, 1) +position = Vector2(295, 100) +scale = Vector2(5.10938, 0.041) +texture = ExtResource("4_foklt") + +[node name="DivC" type="Sprite2D" parent="ui/dividers"] +modulate = Color(0, 0, 0, 1) +position = Vector2(295, 159) +scale = Vector2(5.10938, 0.041) +texture = ExtResource("4_foklt") diff --git a/scenes/NoteManager/scripts/InputHandler.cs b/scenes/NoteManager/scripts/InputHandler.cs new file mode 100644 index 00000000..466c346a --- /dev/null +++ b/scenes/NoteManager/scripts/InputHandler.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using Godot; +using ArrowType = NoteArrow.ArrowType; + +/** + * @class InputHandler + * @brief InputHandler to handle input, and manage note checkers. WIP + */ +public partial class InputHandler : Node2D +{ + [Signal] + public delegate void NotePressedEventHandler(ArrowType arrowType); + + [Signal] + public delegate void NoteReleasedEventHandler(ArrowType arrowType); + + public struct ArrowData + { + public Color Color; + public string Key; + public NoteChecker Node; + public ArrowType Type; + } + + public ArrowData[] Arrows = new ArrowData[] + { + new ArrowData() + { + Color = Colors.Green, + Key = "arrowUp", + Type = ArrowType.Up, + }, + new ArrowData() + { + Color = Colors.Aqua, + Key = "arrowDown", + Type = ArrowType.Down, + }, + new ArrowData() + { + Color = Colors.HotPink, + Key = "arrowLeft", + Type = ArrowType.Left, + }, + new ArrowData() + { + Color = Colors.Red, + Key = "arrowRight", + Type = ArrowType.Right, + }, + }; + + private void InitializeArrowCheckers() + { + //Set the color of the arrows + for (int i = 0; i < Arrows.Length; i++) + { + Arrows[i].Node = GetNode("noteCheckers/" + Arrows[i].Key); + Arrows[i].Node.SetColor(Arrows[i].Color); + } + } + + public override void _Ready() + { + InitializeArrowCheckers(); + } + + public override void _Process(double delta) + { + foreach (var arrow in Arrows) + { + if (Input.IsActionJustPressed(arrow.Key)) + { + EmitSignal(nameof(NotePressed), (int)arrow.Type); + arrow.Node.SetPressed(true); + } + else if (Input.IsActionJustReleased(arrow.Key)) + { + EmitSignal(nameof(NoteReleased), (int)arrow.Type); + arrow.Node.SetPressed(false); + } + } + } +} diff --git a/scenes/NoteManager/scripts/NoteArrow.cs b/scenes/NoteManager/scripts/NoteArrow.cs new file mode 100644 index 00000000..934ed28b --- /dev/null +++ b/scenes/NoteManager/scripts/NoteArrow.cs @@ -0,0 +1,57 @@ +using Godot; + +/** + * @class NoteArrow + * @brief This class represents a visual note that scrolls across the screen to be played by the player. WIP + */ +public partial class NoteArrow : Sprite2D +{ //TextRect caused issues later :) + public enum ArrowType + { + Up = 0, + Down = 1, + Left = 2, + Right = 3, + } + + public int NoteIdx; + public float Bounds; + public bool IsActive; + + public void Init(InputHandler.ArrowData parentArrowData) + { + ZIndex = 1; + + SelfModulate = parentArrowData.Color; + Position += Vector2.Down * (parentArrowData.Node.GlobalPosition.Y); + RotationDegrees = parentArrowData.Node.RotationDegrees; + } + + public override void _Process(double delta) + { + Vector2 newPos = Position; + newPos.X = + (float)( + (-TimeKeeper.CurrentTime / TimeKeeper.LoopLength * TimeKeeper.ChartLength) + % TimeKeeper.ChartLength + / 2 + ) + Bounds; + if (newPos.X > Position.X) + { + OnLoop(); + } + Position = newPos; + } + + public void OnLoop() + { + Visible = true; + IsActive = true; + } + + public void NoteHit() + { + Visible = false; + IsActive = false; + } +} diff --git a/scenes/NoteManager/scripts/NoteChecker.cs b/scenes/NoteManager/scripts/NoteChecker.cs new file mode 100644 index 00000000..8a32a5dc --- /dev/null +++ b/scenes/NoteManager/scripts/NoteChecker.cs @@ -0,0 +1,31 @@ +using System; +using Godot; + +public partial class NoteChecker : Sprite2D +{ + private bool _isPressed; + private Color _color; + private float _fadeTime = 2.0f; + + public override void _Process(double delta) + { + SelfModulate = _isPressed + ? Modulate.Lerp(_color, _fadeTime) + : SelfModulate.Lerp( + new Color(_color.R * 0.5f, _color.G * 0.5f, _color.B * 0.5f, 1), + (float)delta * _fadeTime + ); + } + + public void SetPressed(bool pressed) + { + _isPressed = pressed; + } + + public void SetColor(Color color) + { + _color = color; + + SelfModulate = new Color(_color.R * 0.5f, _color.G * 0.5f, _color.B * 0.5f, 1); + } +} diff --git a/scripts/Main.cs b/scripts/Main.cs index 54bba923..52278fa9 100644 --- a/scripts/Main.cs +++ b/scripts/Main.cs @@ -1,11 +1,5 @@ using System; using Godot; +using static InputHandler; -public partial class Main : Node2D -{ - // Called when the node enters the scene tree for the first time. - public override void _Ready() { } - - // Called every frame. 'delta' is the elapsed time since the previous frame. - public override void _Process(double delta) { } -} +public partial class Main : Node2D { } diff --git a/scripts/TimeKeeper.cs b/scripts/TimeKeeper.cs new file mode 100644 index 00000000..7f09ddff --- /dev/null +++ b/scripts/TimeKeeper.cs @@ -0,0 +1,10 @@ +using System; +using Godot; + +public partial class TimeKeeper : Node +{ + public static double CurrentTime = 0; + public static float ChartLength; + public static float LoopLength; + public static float Bpm; +}