From aba3e033827b1747d9c09237dc5639ca107c8ce0 Mon Sep 17 00:00:00 2001 From: Rmojarro1 <48000819+Rmojarro1@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:05:24 -0800 Subject: [PATCH 1/8] Added main menu with transition to battle and exit Made a scene with two buttons; one will transition to our test battle scene, the other exits the game. The battle scene now has a button in the upper left that will return to the main menu. Makes use of a SceneChange script, which allows for a path to be set for the button in the inspector(doesn't need quotation marks), or exit to make the button quit the game. I noticed a infinite loop warning when exiting battle scene (the tweens don;t seem to stop if we exit), had to make minor change to ChartManager with an exitTree function to stop the tweens when we leave --- project.godot | 2 +- scenes/BattleDirector/test_battle_scene.tscn | 17 +++++++++- scenes/ChartViewport/ChartManager.cs | 11 ++++++ scenes/TestTransition/testTransition.tscn | 33 ++++++++++++++++++ scripts/SceneChange.cs | 35 ++++++++++++++++++++ 5 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 scenes/TestTransition/testTransition.tscn create mode 100644 scripts/SceneChange.cs diff --git a/project.godot b/project.godot index ba8f8e20..1d9a4983 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="Funk Engine" -run/main_scene="res://scenes/BattleDirector/test_battle_scene.tscn" +run/main_scene="res://scenes/TestTransition/testTransition.tscn" config/features=PackedStringArray("4.3", "C#", "Forward Plus") config/icon="res://icon.svg" diff --git a/scenes/BattleDirector/test_battle_scene.tscn b/scenes/BattleDirector/test_battle_scene.tscn index 772d4487..8c6ffbae 100644 --- a/scenes/BattleDirector/test_battle_scene.tscn +++ b/scenes/BattleDirector/test_battle_scene.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=9 format=3 uid="uid://b0mrgr7h0ty1y"] +[gd_scene load_steps=10 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"] @@ -8,6 +8,7 @@ [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"] +[ext_resource type="Script" path="res://scripts/SceneChange.cs" id="9_bxa6e"] [node name="ProtoBattleDirector" type="Node2D" node_paths=PackedStringArray("CM", "IH", "NotePlacementBar", "Audio")] script = ExtResource("1_cwqqr") @@ -59,3 +60,17 @@ offset_left = 16.0 offset_top = 164.0 offset_right = 16.0 offset_bottom = 164.0 + +[node name="Control" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="Button" type="Button" parent="Control"] +layout_mode = 0 +offset_right = 8.0 +offset_bottom = 8.0 +text = "Return to Title" +script = ExtResource("9_bxa6e") +ScenePath = "res://scenes/TestTransition/testTransition.tscn" diff --git a/scenes/ChartViewport/ChartManager.cs b/scenes/ChartViewport/ChartManager.cs index 4ecae9e4..e6f3840a 100644 --- a/scenes/ChartViewport/ChartManager.cs +++ b/scenes/ChartViewport/ChartManager.cs @@ -117,4 +117,15 @@ private NoteArrow CreateNote(ArrowType arrow, int beat = 0) newArrow.Position += Vector2.Right * newArrow.Bounds * 10; //temporary fix for notes spawning and instantly calling loop from originating at 0,0 return newArrow; } + + public override void _ExitTree() + { + GD.Print("[DEBUG] Stopping tweens before exiting the scene..."); + + foreach (var tween in GetTree().GetProcessedTweens()) + { + tween.Stop(); + GD.Print("[DEBUG] Stopped tween."); + } + } } diff --git a/scenes/TestTransition/testTransition.tscn b/scenes/TestTransition/testTransition.tscn new file mode 100644 index 00000000..6481b551 --- /dev/null +++ b/scenes/TestTransition/testTransition.tscn @@ -0,0 +1,33 @@ +[gd_scene load_steps=2 format=3 uid="uid://dbeplni2du158"] + +[ext_resource type="Script" path="res://scripts/SceneChange.cs" id="1_n6d5u"] + +[node name="Control" type="Control"] +layout_mode = 3 +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="Node2D" type="Node2D" parent="."] + +[node name="StartButton" type="Button" parent="."] +layout_mode = 0 +offset_left = 120.0 +offset_top = 56.0 +offset_right = 128.0 +offset_bottom = 64.0 +scale = Vector2(2.48, 2.48) +text = "start battle" +script = ExtResource("1_n6d5u") +ScenePath = "res://scenes/BattleDirector/test_battle_scene.tscn" + +[node name="ExitButton" type="Button" parent="."] +layout_mode = 0 +offset_left = 471.0 +offset_top = 95.0 +offset_right = 508.0 +offset_bottom = 126.0 +scale = Vector2(2.56, 2.56) +text = "exit" +script = ExtResource("1_n6d5u") +ScenePath = "exit" diff --git a/scripts/SceneChange.cs b/scripts/SceneChange.cs new file mode 100644 index 00000000..daaa9bd7 --- /dev/null +++ b/scripts/SceneChange.cs @@ -0,0 +1,35 @@ +using System; +using Godot; + +public partial class SceneChange : Button +{ + [Export] + public string ScenePath = ""; + + public override void _Ready() + { + Pressed += OnButtonPressed; + GD.Print($"[DEBUG] Scene Path: '{ScenePath}' (Length: {ScenePath.Length})"); + } + + private void OnButtonPressed() + { + ScenePath = ScenePath.Trim('\"'); + if (ScenePath.ToLower() == "exit") + { + GD.Print("Exiting game"); + GetTree().Quit(); + return; + } + + if (string.IsNullOrEmpty(ScenePath) || !ResourceLoader.Exists(ScenePath)) + { + GD.PrintErr($"❌ Scene not found: {ScenePath}"); + GD.Print($"[DEBUG] Trying to load: '{ScenePath}' (Length: {ScenePath.Length})"); + return; + } + + GD.Print($"✅ Loading scene: {ScenePath}"); + GetTree().ChangeSceneToFile(ScenePath); + } +} From abea625027e6f96a91efc8f7d03f4bf7b5156771 Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Fri, 7 Feb 2025 13:53:01 -0800 Subject: [PATCH 2/8] Create Effect Event system for notes and relics, Created Conductor class Created battle event interface and relic handling Moved lane and note handling into conductor BattleDirector is moving towards being more oriented towards handling battle events and handling --- Classes/Note.cs | 21 +- Classes/RelicTemplate.cs | 12 -- Classes/Relics/RelicDict.cs | 24 +++ Classes/Relics/RelicEffect.cs | 27 +++ Classes/Relics/RelicTemplate.cs | 17 ++ Globals/FunkEngineNameSpace.cs | 13 ++ .../BattleDirector/scripts/BattleDirector.cs | 181 +++++------------- scenes/BattleDirector/scripts/Conductor.cs | 149 ++++++++++++++ scenes/BattleDirector/test_battle_scene.tscn | 12 +- scenes/ChartViewport/ChartManager.cs | 2 +- scenes/NoteManager/scripts/NoteArrow.cs | 1 - scenes/Puppets/scripts/PlayerPuppet.cs | 2 +- scenes/Puppets/scripts/PlayerStats.cs | 3 +- 13 files changed, 305 insertions(+), 159 deletions(-) delete mode 100644 Classes/RelicTemplate.cs create mode 100644 Classes/Relics/RelicDict.cs create mode 100644 Classes/Relics/RelicEffect.cs create mode 100644 Classes/Relics/RelicTemplate.cs create mode 100644 scenes/BattleDirector/scripts/Conductor.cs diff --git a/Classes/Note.cs b/Classes/Note.cs index 45007630..9c08f75d 100644 --- a/Classes/Note.cs +++ b/Classes/Note.cs @@ -6,19 +6,26 @@ * @class Note * @brief Data structure class for holding data and methods for a battle time note. WIP */ -public partial class Note : Resource +public partial class Note : Resource, IBattleEvent { - private string _effect; + public PuppetTemplate Owner; + private int _baseVal; + private Action NoteEffect; - //public Puppet_Template Owner; + Note(PuppetTemplate owner, Action noteEffect, int baseVal) + { + Owner = owner; + NoteEffect = noteEffect; + _baseVal = baseVal; + } - public Note(string effect = "") + public string GetTrigger() { - _effect = effect; + return "OnHit"; } - public string GetEffect() + public void OnTrigger(BattleDirector BD) { - return _effect; + NoteEffect(BD, _baseVal); } } diff --git a/Classes/RelicTemplate.cs b/Classes/RelicTemplate.cs deleted file mode 100644 index 66c3b8a9..00000000 --- a/Classes/RelicTemplate.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using Godot; - -public partial class RelicTemplate : Resource -{ - public string[] EffectTags; - public string Name; - - //public Texture2D Texture - //public string Tooltip - public RelicTemplate(string Name = "", string[] EffectTags = null) { } -} diff --git a/Classes/Relics/RelicDict.cs b/Classes/Relics/RelicDict.cs new file mode 100644 index 00000000..ad46bb20 --- /dev/null +++ b/Classes/Relics/RelicDict.cs @@ -0,0 +1,24 @@ +using System; +using FunkEngine; +using Godot; + +public class RelicDict +{ + public static RelicTemplate[] RelicPool = new[] + { + new RelicTemplate( + "Good Vibes", + new RelicEffect[] + { + new RelicEffect( + "NotePlaced", + 5, + (director, val) => + { + director.Player.Heal(val); + } + ), + } + ), + }; +} diff --git a/Classes/Relics/RelicEffect.cs b/Classes/Relics/RelicEffect.cs new file mode 100644 index 00000000..0a60f944 --- /dev/null +++ b/Classes/Relics/RelicEffect.cs @@ -0,0 +1,27 @@ +using System; +using FunkEngine; +using Godot; + +public partial class RelicEffect : IBattleEvent +{ + private string Trigger { get; set; } + public int BaseValue; + private Action OnRelicEffect; + + public RelicEffect(string trigger, int val, Action onRelicEffect) + { + BaseValue = val; + Trigger = trigger; + OnRelicEffect = onRelicEffect; + } + + public void OnTrigger(BattleDirector battleDirector) + { + OnRelicEffect(battleDirector, BaseValue); + } + + public string GetTrigger() + { + return Trigger; + } +} diff --git a/Classes/Relics/RelicTemplate.cs b/Classes/Relics/RelicTemplate.cs new file mode 100644 index 00000000..644372a1 --- /dev/null +++ b/Classes/Relics/RelicTemplate.cs @@ -0,0 +1,17 @@ +using System; +using FunkEngine; +using Godot; + +public partial class RelicTemplate : Resource +{ + public RelicEffect[] Effects; + public string Name; + + //public Texture2D Texture + //public string Tooltip + public RelicTemplate(string Name = "", RelicEffect[] EffectTags = null) + { + Effects = EffectTags; + this.Name = Name; + } +} diff --git a/Globals/FunkEngineNameSpace.cs b/Globals/FunkEngineNameSpace.cs index 1b0eaa9c..ed2f3460 100644 --- a/Globals/FunkEngineNameSpace.cs +++ b/Globals/FunkEngineNameSpace.cs @@ -10,6 +10,13 @@ public enum ArrowType Right = 3, } +public struct SongData +{ + public int Bpm; + public double SongLength; + public int NumLoops; +} + public struct ArrowData { public Color Color; @@ -17,3 +24,9 @@ public struct ArrowData public NoteChecker Node; public ArrowType Type; } + +public interface IBattleEvent +{ + void OnTrigger(BattleDirector BD); + string GetTrigger(); +} diff --git a/scenes/BattleDirector/scripts/BattleDirector.cs b/scenes/BattleDirector/scripts/BattleDirector.cs index c603a2cc..2575bac0 100644 --- a/scenes/BattleDirector/scripts/BattleDirector.cs +++ b/scenes/BattleDirector/scripts/BattleDirector.cs @@ -12,9 +12,9 @@ public partial class BattleDirector : Node2D { //TODO: Maybe move some Director functionality to a sub node. #region Declarations - //private Puppet_Template[] ActivePuppets; - private PuppetTemplate Player; - private PuppetTemplate Enemy; + + public PlayerPuppet Player; + public PuppetTemplate Enemy; [Export] private ChartManager CM; @@ -25,6 +25,9 @@ public partial class BattleDirector : Node2D [Export] private NotePlacementBar NotePlacementBar; + [Export] + private Conductor CD; + [Export] private AudioStreamPlayer Audio; @@ -32,88 +35,24 @@ public partial class BattleDirector : Node2D 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(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 IsNoteActive(ArrowType type, int beat) - { - return _laneData[(int)type][beat] != null && _laneData[(int)type][beat].IsActive; - } - - private bool AddNoteToLane(ArrowType type, int beat, bool isActive = true) + private void PlayerAddNote(ArrowType type, int beat) { - beat %= CM.BeatsPerLoop; - //Don't add dupe notes //Beat at 0 is too messy. - if (beat == 0 || _laneData[(int)type][beat] != null) - { - return false; - } - //Get noteArrow from CM - NoteArrow arrow; - if (isActive) - { - arrow = CM.AddArrowToLane(type, beat, _notes.Length - 1); - } - else + GD.Print($"Player trying to place {type} typed note at beat: " + beat); + if (!NotePlacementBar.CanPlaceNote()) + return; + if (CD.AddNoteToLane(type, beat % CM.BeatsPerLoop, false)) { - arrow = CM.AddArrowToLane(type, beat, _notes.Length - 1, new Color(1, 0.43f, 0.26f)); + NotePlacementBar.PlacedNote(); + NotePlaced?.Invoke(this); + GD.Print("Note Placed."); } - arrow.IsActive = isActive; - _laneData[(int)type][beat] = arrow; - return true; } #endregion - private void AddExampleNotes() - { - GD.Print(CM.BeatsPerLoop); - for (int i = 1; i < 15; i++) - { - AddNoteToLane(ArrowType.Up, i * 4); - } - for (int i = 1; i < 15; i++) - { - AddNoteToLane(ArrowType.Left, 4 * i + 1); - } - for (int i = 0; i < 10; i++) - { - AddNoteToLane(ArrowType.Right, 3 * i + 32); - } - for (int i = 0; i < 3; i++) - { - AddNoteToLane(ArrowType.Down, 8 * i + 16); - } - } + #region Initialization public override void _Ready() { @@ -123,6 +62,7 @@ public override void _Ready() SongLength = Audio.Stream.GetLength(), NumLoops = 5, }; + TimeKeeper.Bpm = _curSong.Bpm; Player = new PlayerPuppet(); AddChild(Player); @@ -132,6 +72,7 @@ public override void _Ready() ); Player.SetPosition(new Vector2(80, 0)); Player.Sprite.Position += Vector2.Down * 30; //TEMP + EventizeRelics(); Enemy = new PuppetTemplate(); Enemy.SetPosition(new Vector2(400, 0)); @@ -147,14 +88,9 @@ public override void _Ready() 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(); + CD.Prep(); + CD.AddExampleNotes(); + CD.TimedInput += OnTimedInput; //TEMP var enemTween = CreateTween(); @@ -174,61 +110,27 @@ private void Begin() public override void _Process(double delta) { TimeKeeper.CurrentTime = Audio.GetPlaybackPosition(); - CheckMiss(); + CD.CheckMiss(); } + #endregion #region Input&Timing private void OnNotePressed(ArrowType type) { - CheckNoteTiming(type); + CD.CheckNoteTiming(type); } private void OnNoteReleased(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) - ) - continue; - 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(1); - _laneLastBeat[i] = (_laneLastBeat[i] + 1) % CM.BeatsPerLoop; - } - } - - private void CheckNoteTiming(ArrowType type) + private void OnTimedInput(ArrowType arrowType, int beat, double beatDif, int flag) { - 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) + GD.Print(arrowType + " " + beat + " difference: " + beatDif); + if (flag == -1) { - PlayerAddNote(type, curBeat); + PlayerAddNote(arrowType, beat); return; } - if (!_laneData[(int)type][curBeat % CM.BeatsPerLoop].IsActive) - return; - double beatDif = Math.Abs(realBeat - curBeat); - _laneData[(int)type][curBeat % CM.BeatsPerLoop].NoteHit(); - _laneLastBeat[(int)type] = (curBeat) % CM.BeatsPerLoop; - HandleTiming(beatDif); - } - - private void HandleTiming(double beatDif) - { + //TODO: Evaluate Timing as a function if (beatDif < _timingInterval * 1) { GD.Print("Perfect"); @@ -258,18 +160,29 @@ private void HandleTiming(double beatDif) NotePlacementBar.ComboText("Miss"); } } + #endregion - private void PlayerAddNote(ArrowType type, int beat) + #region BattleEffect Handling + + public delegate void NotePlacedHandler(BattleDirector BD); + private event NotePlacedHandler NotePlaced; + + private void EventizeRelics() { - // 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); - if (NotePlacementBar.CanPlaceNote()) + GD.Print("Hooking up relics"); + foreach (RelicTemplate relic in Player.Stats.CurRelics) { - if (AddNoteToLane(type, beat % CM.BeatsPerLoop, false)) - NotePlacementBar.PlacedNote(); - GD.Print("Note Placed."); + foreach (RelicEffect effect in relic.Effects) + { + switch (effect.GetTrigger()) //TODO: Look into a way to get eventhandler from string + { + case "NotePlaced": + NotePlaced += effect.OnTrigger; + break; + } + } } } + #endregion } diff --git a/scenes/BattleDirector/scripts/Conductor.cs b/scenes/BattleDirector/scripts/Conductor.cs new file mode 100644 index 00000000..3915da98 --- /dev/null +++ b/scenes/BattleDirector/scripts/Conductor.cs @@ -0,0 +1,149 @@ +using System; +using FunkEngine; +using Godot; + +public partial class Conductor : Node +{ + [Export] + private ChartManager CM; + + public delegate void TimedInputHandler(ArrowType type, int beat, double beatDif, int flag = 0); + public event TimedInputHandler TimedInput; + + //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(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 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) + { + beat %= CM.BeatsPerLoop; + //Don't add dupe notes //Beat at 0 is too messy. + if (beat == 0 || _laneData[(int)type][beat] != null) + { + return false; + } + + //Get noteArrow from CM + NoteArrow arrow; + if (isActive) + { + arrow = CM.AddArrowToLane(type, beat, _notes.Length - 1); + } + else + { + arrow = CM.AddArrowToLane(type, beat, _notes.Length - 1, new Color(1, 0.43f, 0.26f)); + } + + arrow.IsActive = isActive; + _laneData[(int)type][beat] = arrow; + return true; + } + + public void Prep() //TODO: Streamline battle initialization + { + _laneData = new NoteArrow[][] + { + new NoteArrow[CM.BeatsPerLoop], + new NoteArrow[CM.BeatsPerLoop], + new NoteArrow[CM.BeatsPerLoop], + new NoteArrow[CM.BeatsPerLoop], + }; + } + + public void AddExampleNotes() + { + GD.Print(CM.BeatsPerLoop); + for (int i = 1; i < 15; i++) + { + AddNoteToLane(ArrowType.Up, i * 4); + } + + for (int i = 1; i < 15; i++) + { + AddNoteToLane(ArrowType.Left, 4 * i + 1); + } + + for (int i = 0; i < 10; i++) + { + AddNoteToLane(ArrowType.Right, 3 * i + 32); + } + + for (int i = 0; i < 3; i++) + { + AddNoteToLane(ArrowType.Down, 8 * i + 16); + } + } + + //Check all lanes for misses from missed inputs + public void CheckMiss() + { + //On current beat, if prev beat is active and not inputted + double realBeat = TimeKeeper.CurrentTime / (60 / (double)TimeKeeper.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) + ) + continue; + 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(); + TimedInput?.Invoke((ArrowType)i, _laneLastBeat[i], 1); + //HandleTiming(1); + _laneLastBeat[i] = (_laneLastBeat[i] + 1) % CM.BeatsPerLoop; + } + } + + public void CheckNoteTiming(ArrowType type) + { + double realBeat = TimeKeeper.CurrentTime / (60 / (double)TimeKeeper.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) + { + TimedInput?.Invoke(type, curBeat, Math.Abs(realBeat - curBeat), -1); + return; + } + + if (!_laneData[(int)type][curBeat % CM.BeatsPerLoop].IsActive) + return; + double beatDif = Math.Abs(realBeat - curBeat); + _laneData[(int)type][curBeat % CM.BeatsPerLoop].NoteHit(); + _laneLastBeat[(int)type] = (curBeat) % CM.BeatsPerLoop; + TimedInput?.Invoke(type, curBeat, beatDif); + } +} diff --git a/scenes/BattleDirector/test_battle_scene.tscn b/scenes/BattleDirector/test_battle_scene.tscn index a15a2189..14047bb2 100644 --- a/scenes/BattleDirector/test_battle_scene.tscn +++ b/scenes/BattleDirector/test_battle_scene.tscn @@ -1,18 +1,26 @@ -[gd_scene load_steps=6 format=3 uid="uid://b0mrgr7h0ty1y"] +[gd_scene load_steps=7 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="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")] +[node name="ProtoBattleDirector" type="Node2D" node_paths=PackedStringArray("CM", "IH", "NotePlacementBar", "CD", "Audio")] script = ExtResource("1_cwqqr") CM = NodePath("SubViewport") IH = NodePath("SubViewport/SubViewport/noteManager") NotePlacementBar = NodePath("NotePlacementBar") +CD = NodePath("Conductor") Audio = NodePath("AudioStreamPlayer") +[node name="Conductor" type="Node" parent="." node_paths=PackedStringArray("CM", "IH", "NotePlacementBar")] +script = ExtResource("2_pcp76") +CM = NodePath("../SubViewport") +IH = NodePath("../SubViewport/SubViewport/noteManager") +NotePlacementBar = NodePath("../NotePlacementBar") + [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] stream = ExtResource("8_caqms") diff --git a/scenes/ChartViewport/ChartManager.cs b/scenes/ChartViewport/ChartManager.cs index a51ce69a..c6fd814f 100644 --- a/scenes/ChartViewport/ChartManager.cs +++ b/scenes/ChartViewport/ChartManager.cs @@ -39,7 +39,7 @@ public void OnNoteReleased(ArrowType type) EmitSignal(nameof(NoteReleased), (int)type); } - public void PrepChart(BattleDirector.SongData songData) + public void PrepChart(SongData songData) { _loopLen = songData.SongLength / songData.NumLoops; TimeKeeper.LoopLength = (float)_loopLen; diff --git a/scenes/NoteManager/scripts/NoteArrow.cs b/scenes/NoteManager/scripts/NoteArrow.cs index 35130f6f..71105b6b 100644 --- a/scenes/NoteManager/scripts/NoteArrow.cs +++ b/scenes/NoteManager/scripts/NoteArrow.cs @@ -20,7 +20,6 @@ public void Init(ArrowData parentArrowData, int beat) Type = parentArrowData.Type; Beat = beat; - //SelfModulate = parentArrowData.Color; Position += Vector2.Down * (parentArrowData.Node.GlobalPosition.Y); RotationDegrees = parentArrowData.Node.RotationDegrees; } diff --git a/scenes/Puppets/scripts/PlayerPuppet.cs b/scenes/Puppets/scripts/PlayerPuppet.cs index e8fce960..f4b57513 100644 --- a/scenes/Puppets/scripts/PlayerPuppet.cs +++ b/scenes/Puppets/scripts/PlayerPuppet.cs @@ -3,7 +3,7 @@ public partial class PlayerPuppet : PuppetTemplate { - public PlayerStats Stats; + public PlayerStats Stats = new PlayerStats(); public override void _Ready() { diff --git a/scenes/Puppets/scripts/PlayerStats.cs b/scenes/Puppets/scripts/PlayerStats.cs index c4a79f32..5a16943d 100644 --- a/scenes/Puppets/scripts/PlayerStats.cs +++ b/scenes/Puppets/scripts/PlayerStats.cs @@ -5,6 +5,7 @@ public partial class PlayerStats : Resource { public int MaxHealth = 300; public Note[] CurNotes = new Note[5]; - public RelicTemplate[] CurRelics = new RelicTemplate[5]; + + public RelicTemplate[] CurRelics = new[] { RelicDict.RelicPool[0] }; public int Attack = 1; } From b3c0a27e6cace5fd2914dbda73e2951e8a1e45ba Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Fri, 7 Feb 2025 15:25:34 -0800 Subject: [PATCH 3/8] BattleEffectTrigger enum and WIP Note effects Note handling is interesting. --- Classes/Note.cs | 21 ++++++-- Classes/Relics/RelicDict.cs | 5 +- Classes/Relics/RelicEffect.cs | 10 ++-- Globals/FunkEngineNameSpace.cs | 9 +++- .../BattleDirector/scripts/BattleDirector.cs | 50 +++++++++++-------- scenes/BattleDirector/scripts/Conductor.cs | 28 +++++++---- scenes/BattleDirector/test_battle_scene.tscn | 15 ++++-- scenes/Puppets/scripts/PlayerPuppet.cs | 3 ++ scenes/Puppets/scripts/PlayerStats.cs | 2 +- 9 files changed, 96 insertions(+), 47 deletions(-) diff --git a/Classes/Note.cs b/Classes/Note.cs index 9c08f75d..2f4b607f 100644 --- a/Classes/Note.cs +++ b/Classes/Note.cs @@ -12,16 +12,29 @@ public partial class Note : Resource, IBattleEvent private int _baseVal; private Action NoteEffect; - Note(PuppetTemplate owner, Action noteEffect, int baseVal) + //public string Tooltip; + + public Note( + PuppetTemplate owner, + int baseVal = 1, + Action noteEffect = null + ) { Owner = owner; - NoteEffect = noteEffect; + NoteEffect = + noteEffect + ?? ( + (BD, val) => + { + BD.GetTarget(this).TakeDamage(val); + } + ); _baseVal = baseVal; } - public string GetTrigger() + public BattleEffectTrigger GetTrigger() { - return "OnHit"; + return BattleEffectTrigger.SelfNoteHit; } public void OnTrigger(BattleDirector BD) diff --git a/Classes/Relics/RelicDict.cs b/Classes/Relics/RelicDict.cs index ad46bb20..5a0caaab 100644 --- a/Classes/Relics/RelicDict.cs +++ b/Classes/Relics/RelicDict.cs @@ -2,6 +2,9 @@ using FunkEngine; using Godot; +/** + * + */ public class RelicDict { public static RelicTemplate[] RelicPool = new[] @@ -11,7 +14,7 @@ public class RelicDict new RelicEffect[] { new RelicEffect( - "NotePlaced", + BattleEffectTrigger.NotePlaced, 5, (director, val) => { diff --git a/Classes/Relics/RelicEffect.cs b/Classes/Relics/RelicEffect.cs index 0a60f944..93ade4f0 100644 --- a/Classes/Relics/RelicEffect.cs +++ b/Classes/Relics/RelicEffect.cs @@ -4,11 +4,15 @@ public partial class RelicEffect : IBattleEvent { - private string Trigger { get; set; } + private BattleEffectTrigger Trigger { get; set; } public int BaseValue; private Action OnRelicEffect; - public RelicEffect(string trigger, int val, Action onRelicEffect) + public RelicEffect( + BattleEffectTrigger trigger, + int val, + Action onRelicEffect + ) { BaseValue = val; Trigger = trigger; @@ -20,7 +24,7 @@ public void OnTrigger(BattleDirector battleDirector) OnRelicEffect(battleDirector, BaseValue); } - public string GetTrigger() + public BattleEffectTrigger GetTrigger() { return Trigger; } diff --git a/Globals/FunkEngineNameSpace.cs b/Globals/FunkEngineNameSpace.cs index ed2f3460..955b7964 100644 --- a/Globals/FunkEngineNameSpace.cs +++ b/Globals/FunkEngineNameSpace.cs @@ -10,6 +10,13 @@ public enum ArrowType Right = 3, } +public enum BattleEffectTrigger +{ + NotePlaced, + NoteHit, + SelfNoteHit, +} + public struct SongData { public int Bpm; @@ -28,5 +35,5 @@ public struct ArrowData public interface IBattleEvent { void OnTrigger(BattleDirector BD); - string GetTrigger(); + BattleEffectTrigger GetTrigger(); } diff --git a/scenes/BattleDirector/scripts/BattleDirector.cs b/scenes/BattleDirector/scripts/BattleDirector.cs index 2575bac0..dcbe63d4 100644 --- a/scenes/BattleDirector/scripts/BattleDirector.cs +++ b/scenes/BattleDirector/scripts/BattleDirector.cs @@ -19,9 +19,6 @@ public partial class BattleDirector : Node2D [Export] private ChartManager CM; - [Export] - private InputHandler IH; - [Export] private NotePlacementBar NotePlacementBar; @@ -50,6 +47,16 @@ private void PlayerAddNote(ArrowType type, int beat) GD.Print("Note Placed."); } } + + public PuppetTemplate GetTarget(Note note) + { + if (note.Owner == Player) + { + return Enemy; + } + + return Player; + } #endregion #region Initialization @@ -66,13 +73,14 @@ public override void _Ready() Player = new PlayerPuppet(); AddChild(Player); - Player.Init( - GD.Load("res://scenes/BattleDirector/assets/Character1.png"), - "Player" - ); - Player.SetPosition(new Vector2(80, 0)); - Player.Sprite.Position += Vector2.Down * 30; //TEMP EventizeRelics(); + foreach (var note in Player.Stats.CurNotes) + { + note.Owner = Player; + CD.Notes = CD.Notes.Append(note).ToArray(); + } + Note enemNote = new Note(Enemy, 2); + CD.Notes = CD.Notes.Append(enemNote).ToArray(); Enemy = new PuppetTemplate(); Enemy.SetPosition(new Vector2(400, 0)); @@ -89,10 +97,9 @@ private void Begin() { CM.PrepChart(_curSong); CD.Prep(); - CD.AddExampleNotes(); CD.TimedInput += OnTimedInput; - //TEMP + //TEMP TODO: Make enemies, can put this in an enemy subclass var enemTween = CreateTween(); enemTween.TweenProperty(Enemy.Sprite, "position", Vector2.Down * 5, 1f).AsRelative(); enemTween.TweenProperty(Enemy.Sprite, "position", Vector2.Up * 5, 1f).AsRelative(); @@ -122,10 +129,10 @@ private void OnNotePressed(ArrowType type) private void OnNoteReleased(ArrowType arrowType) { } - private void OnTimedInput(ArrowType arrowType, int beat, double beatDif, int flag) + private void OnTimedInput(Note note, ArrowType arrowType, int beat, double beatDif) { GD.Print(arrowType + " " + beat + " difference: " + beatDif); - if (flag == -1) + if (note == null) { PlayerAddNote(arrowType, beat); return; @@ -134,28 +141,28 @@ private void OnTimedInput(ArrowType arrowType, int beat, double beatDif, int fla if (beatDif < _timingInterval * 1) { GD.Print("Perfect"); - Enemy.TakeDamage(3); + note.OnTrigger(this); NotePlacementBar.HitNote(); NotePlacementBar.ComboText("Perfect!"); } else if (beatDif < _timingInterval * 2) { GD.Print("Good"); - Enemy.TakeDamage(1); + note.OnTrigger(this); NotePlacementBar.HitNote(); NotePlacementBar.ComboText("Good"); } else if (beatDif < _timingInterval * 3) { GD.Print("Ok"); - Player.TakeDamage(1); + note.OnTrigger(this); NotePlacementBar.HitNote(); NotePlacementBar.ComboText("Okay"); } else { GD.Print("Miss"); - Player.TakeDamage(2); + note.OnTrigger(this); NotePlacementBar.MissNote(); NotePlacementBar.ComboText("Miss"); } @@ -165,19 +172,20 @@ private void OnTimedInput(ArrowType arrowType, int beat, double beatDif, int fla #region BattleEffect Handling - public delegate void NotePlacedHandler(BattleDirector BD); + private delegate void NotePlacedHandler(BattleDirector BD); private event NotePlacedHandler NotePlaced; private void EventizeRelics() { GD.Print("Hooking up relics"); - foreach (RelicTemplate relic in Player.Stats.CurRelics) + foreach (var relic in Player.Stats.CurRelics) { - foreach (RelicEffect effect in relic.Effects) + GetNode