From 0d77e31baad133d3088d455e4d6955a27ce504ba Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Tue, 4 Feb 2025 20:21:53 -0800 Subject: [PATCH 01/11] Created Puppets Initial base template for battle entities Also some file reorganization --- {scripts => Globals}/TimeKeeper.cs | 0 project.godot | 2 +- scenes/BattleDirector/HealthBar.cs | 48 ------------------- scenes/BattleDirector/HealthBar.tscn | 47 ------------------ scenes/BattleDirector/NotePlacementBar.tscn | 2 +- scenes/BattleDirector/TextParticle.tscn | 2 +- .../{ => scripts}/BattleDirector.cs | 28 +++++++---- .../{ => scripts}/NotePlacementBar.cs | 0 .../{ => scripts}/TextParticle.cs | 0 scenes/BattleDirector/test_battle_scene.tscn | 21 +------- scenes/Puppets/HealthBar.tscn | 27 +++++++++++ scenes/Puppets/scripts/HealthBar.cs | 31 ++++++++++++ scenes/Puppets/scripts/Puppet_Template.cs | 46 ++++++++++++++++++ scenes/main.tscn | 3 -- scripts/Main.cs | 5 -- 15 files changed, 127 insertions(+), 135 deletions(-) rename {scripts => Globals}/TimeKeeper.cs (100%) delete mode 100644 scenes/BattleDirector/HealthBar.cs delete mode 100644 scenes/BattleDirector/HealthBar.tscn rename scenes/BattleDirector/{ => scripts}/BattleDirector.cs (91%) rename scenes/BattleDirector/{ => scripts}/NotePlacementBar.cs (100%) rename scenes/BattleDirector/{ => scripts}/TextParticle.cs (100%) create mode 100644 scenes/Puppets/HealthBar.tscn create mode 100644 scenes/Puppets/scripts/HealthBar.cs create mode 100644 scenes/Puppets/scripts/Puppet_Template.cs delete mode 100644 scenes/main.tscn delete mode 100644 scripts/Main.cs diff --git a/scripts/TimeKeeper.cs b/Globals/TimeKeeper.cs similarity index 100% rename from scripts/TimeKeeper.cs rename to Globals/TimeKeeper.cs diff --git a/project.godot b/project.godot index ba8f8e20..9ebf0d8e 100644 --- a/project.godot +++ b/project.godot @@ -17,7 +17,7 @@ config/icon="res://icon.svg" [autoload] -TimeKeeper="*res://scripts/TimeKeeper.cs" +TimeKeeper="*res://Globals/TimeKeeper.cs" [display] diff --git a/scenes/BattleDirector/HealthBar.cs b/scenes/BattleDirector/HealthBar.cs deleted file mode 100644 index c0ad3e2e..00000000 --- a/scenes/BattleDirector/HealthBar.cs +++ /dev/null @@ -1,48 +0,0 @@ -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 deleted file mode 100644 index 0f72abd7..00000000 --- a/scenes/BattleDirector/HealthBar.tscn +++ /dev/null @@ -1,47 +0,0 @@ -[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.tscn b/scenes/BattleDirector/NotePlacementBar.tscn index c8dc9577..2b61556e 100644 --- a/scenes/BattleDirector/NotePlacementBar.tscn +++ b/scenes/BattleDirector/NotePlacementBar.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://duhiilcv4tat3"] -[ext_resource type="Script" path="res://scenes/BattleDirector/NotePlacementBar.cs" id="1_456es"] +[ext_resource type="Script" path="res://scenes/BattleDirector/scripts/NotePlacementBar.cs" id="1_456es"] [sub_resource type="Gradient" id="Gradient_0u6yv"] colors = PackedColorArray(0, 0, 0, 1, 0, 0, 0, 1) diff --git a/scenes/BattleDirector/TextParticle.tscn b/scenes/BattleDirector/TextParticle.tscn index 6491a671..9fffb00b 100644 --- a/scenes/BattleDirector/TextParticle.tscn +++ b/scenes/BattleDirector/TextParticle.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://bd23wwbv7i4gg"] -[ext_resource type="Script" path="res://scenes/BattleDirector/TextParticle.cs" id="1_j0ufq"] +[ext_resource type="Script" path="res://scenes/BattleDirector/scripts/TextParticle.cs" id="1_j0ufq"] [node name="Control" type="Label"] anchors_preset = -1 diff --git a/scenes/BattleDirector/BattleDirector.cs b/scenes/BattleDirector/scripts/BattleDirector.cs similarity index 91% rename from scenes/BattleDirector/BattleDirector.cs rename to scenes/BattleDirector/scripts/BattleDirector.cs index 6de6518c..87e12c1d 100644 --- a/scenes/BattleDirector/BattleDirector.cs +++ b/scenes/BattleDirector/scripts/BattleDirector.cs @@ -11,8 +11,8 @@ public partial class BattleDirector : Node2D { #region Declarations - private HealthBar Player; - private HealthBar Enemy; + private Puppet_Template Player; + private Puppet_Template Enemy; [Export] private ChartManager CM; @@ -120,11 +120,24 @@ public override void _Ready() SongLength = Audio.Stream.GetLength(), NumLoops = 5, }; + + Player = new Puppet_Template(); + 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 + + Enemy = new Puppet_Template(); + Enemy.SetPosition(new Vector2(400, 0)); + AddChild(Enemy); + Enemy.Init(GD.Load("res://scenes/BattleDirector/assets/Enemy1.png"), "Enemy"); + Enemy.Sprite.Scale *= 2; var timer = GetTree().CreateTimer(AudioServer.GetTimeToNextMix()); timer.Timeout += Begin; } + //TODO: This will all change private void Begin() { CM.PrepChart(_curSong); @@ -136,19 +149,14 @@ private void Begin() 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) + .TweenProperty(Enemy.Sprite, "position", Vector2.Down * 5, 1f) .AsRelative(); enemTween - .TweenProperty(Enemy.GetNode("Sprite2D"), "position", Vector2.Up * 5, 1f) + .TweenProperty(Enemy.Sprite, "position", Vector2.Up * 5, 1f) .AsRelative(); enemTween.SetTrans(Tween.TransitionType.Spring); enemTween.SetEase(Tween.EaseType.In); diff --git a/scenes/BattleDirector/NotePlacementBar.cs b/scenes/BattleDirector/scripts/NotePlacementBar.cs similarity index 100% rename from scenes/BattleDirector/NotePlacementBar.cs rename to scenes/BattleDirector/scripts/NotePlacementBar.cs diff --git a/scenes/BattleDirector/TextParticle.cs b/scenes/BattleDirector/scripts/TextParticle.cs similarity index 100% rename from scenes/BattleDirector/TextParticle.cs rename to scenes/BattleDirector/scripts/TextParticle.cs diff --git a/scenes/BattleDirector/test_battle_scene.tscn b/scenes/BattleDirector/test_battle_scene.tscn index 772d4487..a15a2189 100644 --- a/scenes/BattleDirector/test_battle_scene.tscn +++ b/scenes/BattleDirector/test_battle_scene.tscn @@ -1,11 +1,8 @@ -[gd_scene load_steps=9 format=3 uid="uid://b0mrgr7h0ty1y"] +[gd_scene load_steps=6 format=3 uid="uid://b0mrgr7h0ty1y"] -[ext_resource type="Script" path="res://scenes/BattleDirector/BattleDirector.cs" id="1_cwqqr"] +[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="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"] @@ -40,20 +37,6 @@ 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 diff --git a/scenes/Puppets/HealthBar.tscn b/scenes/Puppets/HealthBar.tscn new file mode 100644 index 00000000..203235a8 --- /dev/null +++ b/scenes/Puppets/HealthBar.tscn @@ -0,0 +1,27 @@ +[gd_scene load_steps=6 format=3 uid="uid://bgomxovxs7sr8"] + +[ext_resource type="Script" path="res://scenes/Puppets/scripts/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 + +[node name="ProgressBar" type="TextureProgressBar"] +offset_right = 150.0 +offset_bottom = 20.0 +texture_under = SubResource("GradientTexture2D_ti0cv") +texture_progress = SubResource("GradientTexture2D_r4hau") +texture_progress_offset = Vector2(2, 2) +script = ExtResource("1_b1t4i") diff --git a/scenes/Puppets/scripts/HealthBar.cs b/scenes/Puppets/scripts/HealthBar.cs new file mode 100644 index 00000000..cfd757ba --- /dev/null +++ b/scenes/Puppets/scripts/HealthBar.cs @@ -0,0 +1,31 @@ +using System; +using Godot; + +public partial class HealthBar : TextureProgressBar +{ + public override void _Ready() + { + Value = MaxValue; + } + + //initializes health + public void SetHealth(int max, int current) + { + MaxValue = max; + Value = current; + } + + //For effects changes max hp, and changes hp by a similar amount + public void ChangeMax(int change) + { + MaxValue += change; + Value += change; + } + + //Changes hp value, for damage or heal, returns resulting hp. + public int ChangeHP(int amount) + { + Value += amount; + return (int)Value; + } +} diff --git a/scenes/Puppets/scripts/Puppet_Template.cs b/scenes/Puppets/scripts/Puppet_Template.cs new file mode 100644 index 00000000..e8e1ae3c --- /dev/null +++ b/scenes/Puppets/scripts/Puppet_Template.cs @@ -0,0 +1,46 @@ +using System; +using Godot; + +/** Essentially a battle entity. Has HP and can be healed or damaged. + * TODO: Look into interfaces + */ +public partial class Puppet_Template : Node2D +{ + private HealthBar _healthBar; + public Sprite2D Sprite = new Sprite2D(); + + private int _maxHealth = 100; + private int _currentHealth = 100; + + //Stats would go here. + + public string UniqName = ""; //Eventually make subclasses/scenes/real stuff + + public override void _Ready() + { + _healthBar = GD.Load("res://scenes/Puppets/HealthBar.tscn") + .Instantiate(); + AddChild(_healthBar); + + Sprite.Position = new Vector2(75, 86); + AddChild(Sprite); //TODO: DECIDE Whether to replace with packedscenes/robust subclasses + + _healthBar.SetHealth(_maxHealth, _currentHealth); + } + + public void Init(Texture2D texture, string name) + { + Sprite.Texture = texture; + UniqName = name; + } + + public void TakeDamage(int amount) + { + _healthBar.ChangeHP(-amount); + } + + public void Heal(int amount) + { + _healthBar.ChangeHP(amount); + } +} diff --git a/scenes/main.tscn b/scenes/main.tscn deleted file mode 100644 index 03a5f3fa..00000000 --- a/scenes/main.tscn +++ /dev/null @@ -1,3 +0,0 @@ -[gd_scene format=3 uid="uid://diq2m6va1x52k"] - -[node name="Main" type="Node2D"] diff --git a/scripts/Main.cs b/scripts/Main.cs deleted file mode 100644 index 52278fa9..00000000 --- a/scripts/Main.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System; -using Godot; -using static InputHandler; - -public partial class Main : Node2D { } From fc5174928c1d3e761c2e3a7fc5c6d2b658a5d16d Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Tue, 4 Feb 2025 22:03:24 -0800 Subject: [PATCH 02/11] Refactoring Moved enums and structs to common namespace Removed note resource class needing beat and type (moving forward note data shouldn't hold that.) Added IsNoteActive() Removed unused code and parameters Removed unnecessary textparticle packedscene --- Classes/Note.cs | 14 ++- Globals/FunkEngineNameSpace.cs | 19 +++ scenes/BattleDirector/TextParticle.tscn | 9 -- .../BattleDirector/scripts/BattleDirector.cs | 119 ++++++++---------- .../scripts/NotePlacementBar.cs | 35 +++--- scenes/BattleDirector/scripts/TextParticle.cs | 3 - scenes/ChartViewport/ChartManager.cs | 10 +- scenes/NoteManager/scripts/InputHandler.cs | 10 +- scenes/NoteManager/scripts/NoteArrow.cs | 16 ++- 9 files changed, 106 insertions(+), 129 deletions(-) create mode 100644 Globals/FunkEngineNameSpace.cs delete mode 100644 scenes/BattleDirector/TextParticle.tscn diff --git a/Classes/Note.cs b/Classes/Note.cs index 193bbc3f..e0a82e12 100644 --- a/Classes/Note.cs +++ b/Classes/Note.cs @@ -1,4 +1,5 @@ using System; +using FunkEngine; using Godot; /** @@ -7,12 +8,15 @@ */ public partial class Note : Resource { - public int Beat; - public NoteArrow.ArrowType Type; + private string _effect; - public Note(NoteArrow.ArrowType type = NoteArrow.ArrowType.Up, int beat = 0) + public Note(string effect = "") { - Beat = beat; - Type = type; + _effect = effect; + } + + public string GetEffect() + { + return _effect; } } diff --git a/Globals/FunkEngineNameSpace.cs b/Globals/FunkEngineNameSpace.cs new file mode 100644 index 00000000..1b0eaa9c --- /dev/null +++ b/Globals/FunkEngineNameSpace.cs @@ -0,0 +1,19 @@ +using Godot; + +namespace FunkEngine; + +public enum ArrowType +{ + Up = 0, + Down = 1, + Left = 2, + Right = 3, +} + +public struct ArrowData +{ + public Color Color; + public string Key; + public NoteChecker Node; + public ArrowType Type; +} diff --git a/scenes/BattleDirector/TextParticle.tscn b/scenes/BattleDirector/TextParticle.tscn deleted file mode 100644 index 9fffb00b..00000000 --- a/scenes/BattleDirector/TextParticle.tscn +++ /dev/null @@ -1,9 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bd23wwbv7i4gg"] - -[ext_resource type="Script" path="res://scenes/BattleDirector/scripts/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/scripts/BattleDirector.cs b/scenes/BattleDirector/scripts/BattleDirector.cs index 87e12c1d..29d72e7f 100644 --- a/scenes/BattleDirector/scripts/BattleDirector.cs +++ b/scenes/BattleDirector/scripts/BattleDirector.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using FunkEngine; using Godot; /** @@ -26,13 +27,7 @@ public partial class BattleDirector : Node2D [Export] private AudioStreamPlayer Audio; - private double _timingInterval = .1; //secs - - [Signal] - public delegate void PlayerDamageEventHandler(int damage); - - [Signal] - public delegate void EnemyDamageEventHandler(int damage); + private double _timingInterval = .1; //secs, maybe make somewhat note dependent private SongData _curSong; @@ -58,7 +53,7 @@ public struct SongData private Note[] _notes = Array.Empty(); //Returns first note of lane without modifying lane data - private Note GetNoteAt(NoteArrow.ArrowType dir, int beat) + private Note GetNoteAt(ArrowType dir, int beat) { return GetNote(_laneData[(int)dir][beat]); } @@ -69,19 +64,23 @@ private Note GetNote(NoteArrow arrow) return _notes[arrow.NoteIdx]; } - private bool AddNoteToLane(Note note, bool isActive = true) + 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) { - note.Beat %= CM.BeatsPerLoop; - //Don't add dupe notes - if (note.Beat == 0 || _notes.Any(nt => nt.Type == note.Type && nt.Beat == note.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; //Beat at 0 is too messy. + return false; } - _notes = _notes.Append(note).ToArray(); //Get noteArrow from CM - var arrow = CM.AddArrowToLane(note, _notes.Length - 1); + var arrow = CM.AddArrowToLane(type, beat, _notes.Length - 1); arrow.IsActive = isActive; - _laneData[(int)note.Type][note.Beat] = arrow; + _laneData[(int)type][beat] = arrow; return true; } #endregion @@ -92,23 +91,19 @@ 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); + AddNoteToLane(ArrowType.Up, i * 4); } for (int i = 1; i < 15; i++) { - Note exampleNote = new Note(NoteArrow.ArrowType.Left, 4 * i + 1); - AddNoteToLane(exampleNote); + AddNoteToLane(ArrowType.Left, 4 * i + 1); } for (int i = 0; i < 10; i++) { - Note exampleNote = new Note(NoteArrow.ArrowType.Right, 3 * i + 32); - AddNoteToLane(exampleNote); + AddNoteToLane(ArrowType.Right, 3 * i + 32); } for (int i = 0; i < 3; i++) { - Note exampleNote = new Note(NoteArrow.ArrowType.Down, 8 * i + 16); - AddNoteToLane(exampleNote); + AddNoteToLane(ArrowType.Down, 8 * i + 16); } } @@ -120,13 +115,16 @@ public override void _Ready() SongLength = Audio.Stream.GetLength(), NumLoops = 5, }; - + Player = new Puppet_Template(); AddChild(Player); - Player.Init(GD.Load("res://scenes/BattleDirector/assets/Character1.png"), "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 - + Enemy = new Puppet_Template(); Enemy.SetPosition(new Vector2(400, 0)); AddChild(Enemy); @@ -149,15 +147,11 @@ private void Begin() new NoteArrow[CM.BeatsPerLoop], }; AddExampleNotes(); - + //TEMP var enemTween = CreateTween(); - enemTween - .TweenProperty(Enemy.Sprite, "position", Vector2.Down * 5, 1f) - .AsRelative(); - enemTween - .TweenProperty(Enemy.Sprite, "position", Vector2.Up * 5, 1f) - .AsRelative(); + enemTween.TweenProperty(Enemy.Sprite, "position", Vector2.Down * 5, 1f).AsRelative(); + enemTween.TweenProperty(Enemy.Sprite, "position", Vector2.Up * 5, 1f).AsRelative(); enemTween.SetTrans(Tween.TransitionType.Spring); enemTween.SetEase(Tween.EaseType.In); enemTween.SetLoops(); @@ -176,12 +170,12 @@ public override void _Process(double delta) } #region Input&Timing - private void OnNotePressed(NoteArrow.ArrowType type) + private void OnNotePressed(ArrowType type) { CheckNoteTiming(type); } - private void OnNoteReleased(NoteArrow.ArrowType arrowType) { } + private void OnNoteReleased(ArrowType arrowType) { } //Check all lanes for misses from missed inputs private void CheckMiss() @@ -190,49 +184,39 @@ private void CheckMiss() 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); + if (!(_laneLastBeat[i] < Math.Floor(realBeat))) + continue; + if (!IsNoteActive((ArrowType)i, _laneLastBeat[i])) + { _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(NoteArrow.ArrowType type) + private void CheckNoteTiming(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 - ) + if (_laneData[(int)type][curBeat % CM.BeatsPerLoop] == null) { - _laneLastBeat[(int)type] = (curBeat) % CM.BeatsPerLoop; PlayerAddNote(type, curBeat); 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(type, beatDif); + HandleTiming(beatDif); } - private void HandleTiming(NoteArrow.ArrowType type, double beatDif) + private void HandleTiming(double beatDif) { if (beatDif < _timingInterval * 1) { @@ -265,21 +249,16 @@ private void HandleTiming(NoteArrow.ArrowType type, double beatDif) } #endregion - private void PlayerAddNote(NoteArrow.ArrowType type, int beat) + private void PlayerAddNote(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() - ); + GD.Print($"Player trying to place {type} typed note at beat: " + beat); if (NotePlacementBar.CanPlaceNote()) { - Note exampleNote = new Note(type, beat % CM.BeatsPerLoop); - if (AddNoteToLane(exampleNote, false)) + if (AddNoteToLane(type, beat % CM.BeatsPerLoop, false)) NotePlacementBar.PlacedNote(); + GD.Print("Note Placed."); } } } diff --git a/scenes/BattleDirector/scripts/NotePlacementBar.cs b/scenes/BattleDirector/scripts/NotePlacementBar.cs index d86a3c9a..4313979f 100644 --- a/scenes/BattleDirector/scripts/NotePlacementBar.cs +++ b/scenes/BattleDirector/scripts/NotePlacementBar.cs @@ -4,8 +4,8 @@ public partial class NotePlacementBar : Node { const int MaxValue = 80; - int currentBarValue; - int currentCombo; + private int _currentBarValue; + private int _currentCombo; int comboMult; int notesToIncreaseCombo; @@ -19,36 +19,33 @@ public partial class NotePlacementBar : Node public override void _Ready() { notePlacementBar.MaxValue = MaxValue; - currentBarValue = 0; - currentCombo = 0; + _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(); + TextParticle newText = new TextParticle(); AddChild(newText); - newText.Text = text + $" {currentCombo}"; + newText.Text = text + $" {_currentCombo}"; } // Hitting a note increases combo, combo mult, and note placement bar public void HitNote() { - currentCombo++; + _currentCombo++; DetermineComboMult(); - currentBarValue += comboMult; - UpdateNotePlacementBar(currentBarValue); + _currentBarValue += comboMult; + UpdateNotePlacementBar(_currentBarValue); UpdateComboMultText(); } // Missing a note resets combo public void MissNote() { - currentCombo = 0; + _currentCombo = 0; DetermineComboMult(); UpdateComboMultText(); } @@ -56,26 +53,26 @@ public void MissNote() // Placing a note resets the note placement bar public void PlacedNote() { - currentBarValue = 0; - UpdateNotePlacementBar(currentBarValue); + _currentBarValue = 0; + UpdateNotePlacementBar(_currentBarValue); } public bool CanPlaceNote() { - return currentBarValue >= MaxValue; + return _currentBarValue >= MaxValue; } private void DetermineComboMult() { - comboMult = currentCombo / notesToIncreaseCombo + 1; + comboMult = _currentCombo / notesToIncreaseCombo + 1; } - public void UpdateNotePlacementBar(int newValue) + private void UpdateNotePlacementBar(int newValue) { notePlacementBar.Value = newValue; } - public void UpdateComboMultText() + private void UpdateComboMultText() { currentComboMultText.Text = $"x{comboMult.ToString()}"; } diff --git a/scenes/BattleDirector/scripts/TextParticle.cs b/scenes/BattleDirector/scripts/TextParticle.cs index 273d4967..905c93a2 100644 --- a/scenes/BattleDirector/scripts/TextParticle.cs +++ b/scenes/BattleDirector/scripts/TextParticle.cs @@ -17,7 +17,4 @@ public override void _Ready() 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/ChartViewport/ChartManager.cs b/scenes/ChartViewport/ChartManager.cs index 4ecae9e4..5702a521 100644 --- a/scenes/ChartViewport/ChartManager.cs +++ b/scenes/ChartViewport/ChartManager.cs @@ -1,7 +1,7 @@ using System; using System.Linq; +using FunkEngine; using Godot; -using ArrowType = NoteArrow.ArrowType; /** * @class ChartManager @@ -98,10 +98,10 @@ private void TweenArrows(Vector2 scale) } } - public NoteArrow AddArrowToLane(Note note, int noteIdx) + public NoteArrow AddArrowToLane(ArrowType type, int beat, int noteIdx) { - var newNote = CreateNote(note.Type, note.Beat); - CreateNote(note.Type, note.Beat + BeatsPerLoop); //Create a dummy arrow for looping visuals + var newNote = CreateNote(type, beat); + CreateNote(type, beat + BeatsPerLoop); //Create a dummy arrow for looping visuals newNote.NoteIdx = noteIdx; return newNote; } @@ -110,7 +110,7 @@ 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]); + newArrow.Init(IH.Arrows[(int)arrow], beat); _arrowGroup.AddChild(newArrow); newArrow.Bounds = (float)((double)beat / BeatsPerLoop * (ChartLength / 2)); diff --git a/scenes/NoteManager/scripts/InputHandler.cs b/scenes/NoteManager/scripts/InputHandler.cs index 466c346a..b1961d4d 100644 --- a/scenes/NoteManager/scripts/InputHandler.cs +++ b/scenes/NoteManager/scripts/InputHandler.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using FunkEngine; using Godot; -using ArrowType = NoteArrow.ArrowType; /** * @class InputHandler @@ -15,14 +15,6 @@ public partial class InputHandler : Node2D [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() diff --git a/scenes/NoteManager/scripts/NoteArrow.cs b/scenes/NoteManager/scripts/NoteArrow.cs index 934ed28b..d5f0b48b 100644 --- a/scenes/NoteManager/scripts/NoteArrow.cs +++ b/scenes/NoteManager/scripts/NoteArrow.cs @@ -1,3 +1,4 @@ +using FunkEngine; using Godot; /** @@ -6,22 +7,19 @@ */ public partial class NoteArrow : Sprite2D { //TextRect caused issues later :) - public enum ArrowType - { - Up = 0, - Down = 1, - Left = 2, - Right = 3, - } - public int NoteIdx; + public ArrowType Type; + public int Beat; public float Bounds; public bool IsActive; - public void Init(InputHandler.ArrowData parentArrowData) + public void Init(ArrowData parentArrowData, int beat) { ZIndex = 1; + Type = parentArrowData.Type; + Beat = beat; + SelfModulate = parentArrowData.Color; Position += Vector2.Down * (parentArrowData.Node.GlobalPosition.Y); RotationDegrees = parentArrowData.Node.RotationDegrees; 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 03/11] 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 450197b6264e3d0565db8096ebebfbcb5d2531e8 Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Thu, 6 Feb 2025 21:52:42 -0800 Subject: [PATCH 04/11] Removed placeholder arrow, fixed loop missing, added player puppet Replaced old arrow graphic with new custom one Player arrow is unique color Reverted CheckMiss(), upon looping, without input no notes would miss Player puppet with WIP player stats object. RelicTemplate class to placehold for inventory --- Classes/Note.cs | 2 ++ Classes/RelicTemplate.cs | 12 ++++++++ README.md | 2 -- scenes/BattleDirector/assets/Character1.png | Bin 2098 -> 1764 bytes .../BattleDirector/scripts/BattleDirector.cs | 29 ++++++++++++------ scenes/ChartViewport/ChartManager.cs | 22 +++++++++---- scenes/NoteManager/assets/outline_white.png | Bin 0 -> 607 bytes ...ow.png.import => outline_white.png.import} | 8 ++--- scenes/NoteManager/assets/right-arrow.png | Bin 14630 -> 0 bytes scenes/NoteManager/note.tscn | 5 ++- scenes/NoteManager/note_manager.tscn | 14 +++------ scenes/NoteManager/scripts/NoteArrow.cs | 2 +- scenes/Puppets/scripts/PlayerPuppet.cs | 12 ++++++++ scenes/Puppets/scripts/PlayerStats.cs | 10 ++++++ .../{Puppet_Template.cs => PuppetTemplate.cs} | 8 ++--- 15 files changed, 88 insertions(+), 38 deletions(-) create mode 100644 Classes/RelicTemplate.cs create mode 100644 scenes/NoteManager/assets/outline_white.png rename scenes/NoteManager/assets/{right-arrow.png.import => outline_white.png.import} (66%) delete mode 100644 scenes/NoteManager/assets/right-arrow.png create mode 100644 scenes/Puppets/scripts/PlayerPuppet.cs create mode 100644 scenes/Puppets/scripts/PlayerStats.cs rename scenes/Puppets/scripts/{Puppet_Template.cs => PuppetTemplate.cs} (86%) diff --git a/Classes/Note.cs b/Classes/Note.cs index e0a82e12..45007630 100644 --- a/Classes/Note.cs +++ b/Classes/Note.cs @@ -10,6 +10,8 @@ public partial class Note : Resource { private string _effect; + //public Puppet_Template Owner; + public Note(string effect = "") { _effect = effect; diff --git a/Classes/RelicTemplate.cs b/Classes/RelicTemplate.cs new file mode 100644 index 00000000..66c3b8a9 --- /dev/null +++ b/Classes/RelicTemplate.cs @@ -0,0 +1,12 @@ +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/README.md b/README.md index 727c86ea..65055df1 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,6 @@ Current team members include: #### Attributions: -Note icon: Next icons created by Pixel perfect - Flaticon - First Song: gameMusic by Magntron - freesound.org diff --git a/scenes/BattleDirector/assets/Character1.png b/scenes/BattleDirector/assets/Character1.png index 2175831dec5802513b4512dde3f20bf281f49ec6..f416d5d2a6a033de79c744b67209672636277710 100644 GIT binary patch delta 1736 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 Dx>C(G diff --git a/scenes/BattleDirector/scripts/BattleDirector.cs b/scenes/BattleDirector/scripts/BattleDirector.cs index 29d72e7f..c603a2cc 100644 --- a/scenes/BattleDirector/scripts/BattleDirector.cs +++ b/scenes/BattleDirector/scripts/BattleDirector.cs @@ -10,10 +10,11 @@ * @brief Higher priority director to manage battle effects. Can directly access managers, which should signal up to Director WIP */ public partial class BattleDirector : Node2D -{ +{ //TODO: Maybe move some Director functionality to a sub node. #region Declarations - private Puppet_Template Player; - private Puppet_Template Enemy; + //private Puppet_Template[] ActivePuppets; + private PuppetTemplate Player; + private PuppetTemplate Enemy; [Export] private ChartManager CM; @@ -78,14 +79,21 @@ private bool AddNoteToLane(ArrowType type, int beat, bool isActive = true) return false; } //Get noteArrow from CM - var arrow = CM.AddArrowToLane(type, beat, _notes.Length - 1); + 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; } #endregion - //Creeate dummy notes private void AddExampleNotes() { GD.Print(CM.BeatsPerLoop); @@ -116,7 +124,7 @@ public override void _Ready() NumLoops = 5, }; - Player = new Puppet_Template(); + Player = new PlayerPuppet(); AddChild(Player); Player.Init( GD.Load("res://scenes/BattleDirector/assets/Character1.png"), @@ -125,7 +133,7 @@ public override void _Ready() Player.SetPosition(new Vector2(80, 0)); Player.Sprite.Position += Vector2.Down * 30; //TEMP - Enemy = new Puppet_Template(); + Enemy = new PuppetTemplate(); Enemy.SetPosition(new Vector2(400, 0)); AddChild(Enemy); Enemy.Init(GD.Load("res://scenes/BattleDirector/assets/Enemy1.png"), "Enemy"); @@ -184,9 +192,12 @@ private void CheckMiss() double realBeat = TimeKeeper.CurrentTime / (60 / (double)_curSong.Bpm) % CM.BeatsPerLoop; for (int i = 0; i < _laneData.Length; i++) { - if (!(_laneLastBeat[i] < Math.Floor(realBeat))) + if ( + !(_laneLastBeat[i] < Math.Floor(realBeat)) + && (_laneLastBeat[i] != CM.BeatsPerLoop - 1 || Math.Floor(realBeat) != 0) + ) continue; - if (!IsNoteActive((ArrowType)i, _laneLastBeat[i])) + if (_laneData[i][_laneLastBeat[i]] == null || !_laneData[i][_laneLastBeat[i]].IsActive) { _laneLastBeat[i] = (_laneLastBeat[i] + 1) % CM.BeatsPerLoop; continue; diff --git a/scenes/ChartViewport/ChartManager.cs b/scenes/ChartViewport/ChartManager.cs index 5702a521..a51ce69a 100644 --- a/scenes/ChartViewport/ChartManager.cs +++ b/scenes/ChartViewport/ChartManager.cs @@ -59,16 +59,16 @@ public void PrepChart(BattleDirector.SongData songData) tween .TweenMethod( Callable.From((Vector2 scale) => TweenArrows(scale)), - new Vector2(0.07f, 0.07f), - new Vector2(0.07f, 0.07f) * 1.25f, + Vector2.One * .8f, + Vector2.One, 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), + Vector2.One, + Vector2.One * .8f, 60f / TimeKeeper.Bpm / 2 ); tween.SetLoops().Play(); @@ -98,10 +98,20 @@ private void TweenArrows(Vector2 scale) } } - public NoteArrow AddArrowToLane(ArrowType type, int beat, int noteIdx) + public NoteArrow AddArrowToLane( + ArrowType type, + int beat, + int noteIdx, + Color colorOverride = default + ) { var newNote = CreateNote(type, beat); - CreateNote(type, beat + BeatsPerLoop); //Create a dummy arrow for looping visuals + var loopArrow = CreateNote(type, beat + BeatsPerLoop); //Create a dummy arrow for looping visuals + if (colorOverride != default) + { + newNote.Modulate = colorOverride; + loopArrow.Modulate = colorOverride; + } newNote.NoteIdx = noteIdx; return newNote; } diff --git a/scenes/NoteManager/assets/outline_white.png b/scenes/NoteManager/assets/outline_white.png new file mode 100644 index 0000000000000000000000000000000000000000..8dfe2e4ed1d52539ba88cc54e0d51878292a4eb8 GIT binary patch literal 607 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 Date: Fri, 7 Feb 2025 13:53:01 -0800 Subject: [PATCH 05/11] 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 06/11] 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