From 42ae22f2da4500cc9656fb146824748111f0ba1e Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Sat, 5 Apr 2025 15:27:34 -0700 Subject: [PATCH 1/4] Implemented Harbinger event system Singleton instance with new eventargs classes to better pass information where accessible for battle events --- Classes/Relics/RelicEffect.cs | 10 ++- Globals/FunkEngineNameSpace.cs | 8 +- Globals/Scribe.cs | 14 ++-- .../BattleDirector/Scripts/BattleDirector.cs | 73 +++++++++++++++++-- .../Puppets/Enemies/BossBlood/P_BossBlood.cs | 2 +- Scenes/Puppets/Enemies/EnemyEffect.cs | 8 +- 6 files changed, 91 insertions(+), 24 deletions(-) diff --git a/Classes/Relics/RelicEffect.cs b/Classes/Relics/RelicEffect.cs index bb2cedf3..f71bf462 100644 --- a/Classes/Relics/RelicEffect.cs +++ b/Classes/Relics/RelicEffect.cs @@ -10,19 +10,21 @@ public partial class RelicEffect : IBattleEvent private BattleEffectTrigger Trigger { get; set; } private int _baseValue; public int Value; - private Action _onRelicEffect; + private Action _onRelicEffect; private bool _effectPersists = false; public RelicEffect( BattleEffectTrigger trigger, int val, - Action onRelicEffect + Action onRelicEffect, + bool persists = false ) { _baseValue = val; Value = _baseValue; Trigger = trigger; _onRelicEffect = onRelicEffect; + _effectPersists = persists; } public void OnBattleEnd() @@ -31,9 +33,9 @@ public void OnBattleEnd() Value = _baseValue; } - public void OnTrigger(BattleDirector battleDirector) + public void OnTrigger(BattleEventArgs e) { - _onRelicEffect(battleDirector, this, Value); + _onRelicEffect(e, this, Value); } public BattleEffectTrigger GetTrigger() diff --git a/Globals/FunkEngineNameSpace.cs b/Globals/FunkEngineNameSpace.cs index 34c03684..9901b057 100644 --- a/Globals/FunkEngineNameSpace.cs +++ b/Globals/FunkEngineNameSpace.cs @@ -372,12 +372,18 @@ private void AddBossRoom(int width, int height) } #region Interfaces + +public class BattleEventArgs(BattleDirector director) : EventArgs +{ + public BattleDirector BD = director; +} + /** * A BattleDirector driven battle event. Needs an enum defined trigger. */ public interface IBattleEvent { - void OnTrigger(BattleDirector BD); + void OnTrigger(BattleEventArgs e); BattleEffectTrigger GetTrigger(); } diff --git a/Globals/Scribe.cs b/Globals/Scribe.cs index 526a60a0..b148aab8 100644 --- a/Globals/Scribe.cs +++ b/Globals/Scribe.cs @@ -108,7 +108,7 @@ public partial class Scribe : Node new RelicEffect( BattleEffectTrigger.OnPickup, 10, - (director, self, val) => + (e, self, val) => { StageProducer.PlayerStats.MaxHealth += val; StageProducer.PlayerStats.CurrentHealth += val; @@ -126,9 +126,9 @@ public partial class Scribe : Node new RelicEffect( BattleEffectTrigger.NotePlaced, 2, - (director, self, val) => + (e, self, val) => { - director.Player.Heal(val); + e.BD.Player.Heal(val); } ), } @@ -143,9 +143,9 @@ public partial class Scribe : Node new RelicEffect( BattleEffectTrigger.OnLoop, 1, - (director, self, val) => + (e, self, val) => { - director.NPB.IncreaseBonusMult(val); + e.BD.NPB.IncreaseBonusMult(val); self.Value++; } ), @@ -161,9 +161,9 @@ public partial class Scribe : Node new RelicEffect( BattleEffectTrigger.OnLoop, 10, - (director, self, val) => + (e, self, val) => { - director.NPB.IncreaseCharge(val); + e.BD.NPB.IncreaseCharge(val); self.Value += 5; } ), diff --git a/Scenes/BattleDirector/Scripts/BattleDirector.cs b/Scenes/BattleDirector/Scripts/BattleDirector.cs index 329f7d13..b3b6b5f2 100644 --- a/Scenes/BattleDirector/Scripts/BattleDirector.cs +++ b/Scenes/BattleDirector/Scripts/BattleDirector.cs @@ -1,6 +1,7 @@ using System; using FunkEngine; using Godot; +using Melanchall.DryWetMidi.Interaction; /**BattleDirector: Higher priority director to manage battle effects. Can directly access managers, which should signal up to Director WIP */ @@ -42,8 +43,9 @@ private bool PlayerAddNote(ArrowType type, Beat beat) Note noteToPlace = NPB.NotePlaced(); noteToPlace.OnHit(this, Timing.Okay); + CD.AddPlayerNote(noteToPlace, type, beat); - NotePlaced?.Invoke(this); + Harbinger.Instance.InvokeNotePlaced(new ArrowData(type, beat, noteToPlace)); return true; } @@ -84,6 +86,7 @@ public override void _Ready() } TimeKeeper.InitVals(curSong.Bpm); + Harbinger.Init(this); InitPlayer(); InitEnemies(); CD.Initialize(curSong); @@ -128,7 +131,7 @@ private void UpdateBeat(Beat beat) } if (beat.Loop > TimeKeeper.LastBeat.Loop) { - ChartLooped?.Invoke(this); + Harbinger.Instance.InvokeChartLoop(beat.Loop); } TimeKeeper.LastBeat = beat; } @@ -243,21 +246,21 @@ private void TransitionOutOfBattle() #region BattleEffect Handling - private delegate void NotePlacedHandler(BattleDirector BD); + /*private delegate void NotePlacedHandler(BattleDirector BD); private event NotePlacedHandler NotePlaced; private delegate void ChartLoopHandler(BattleDirector BD); - private event ChartLoopHandler ChartLooped; + private event ChartLoopHandler ChartLooped;*/ private void AddEvent(IBattleEvent bEvent) { switch (bEvent.GetTrigger()) //TODO: Look into a way to get eventhandler from string { case BattleEffectTrigger.NotePlaced: - NotePlaced += bEvent.OnTrigger; + Harbinger.Instance.NotePlaced += bEvent.OnTrigger; break; case BattleEffectTrigger.OnLoop: - ChartLooped += bEvent.OnTrigger; + Harbinger.Instance.ChartLooped += bEvent.OnTrigger; break; } } @@ -293,8 +296,64 @@ private void CleanUpRelics() } #endregion + public partial class Harbinger : Resource + { + private static Harbinger _instance; + public static Harbinger Instance => _instance; + + private BattleDirector _curDirector; + + static Harbinger() { } + + private Harbinger(BattleDirector BD) + { + _curDirector = BD; + } + + internal static void Init(BattleDirector BD) + { + _instance = new Harbinger(BD); + } + + /// + /// Event Args to handle event types triggering from the action of a note, without timing. + /// + /// The BattleDirector calling the event. + /// The note data of the passing note. + public class NoteEventArgs(BattleDirector bd, ArrowData data) : BattleEventArgs(bd) + { + public ArrowData Data = data; + } + + /// + /// Event Args to handle event types triggering from the start of a new loop. + /// + /// The BattleDirector calling the event. + /// The loop starting. + public class LoopEventArgs(BattleDirector bd, int incomingLoop) : BattleEventArgs(bd) + { + public int Loop = incomingLoop; + } + + internal delegate void NotePlacedHandler(BattleEventArgs e); + internal event NotePlacedHandler NotePlaced; + + public void InvokeNotePlaced(ArrowData data) + { + NotePlaced?.Invoke(new NoteEventArgs(_curDirector, data)); + } + + internal delegate void ChartLoopHandler(BattleEventArgs e); + internal event ChartLoopHandler ChartLooped; + + public void InvokeChartLoop(int incLoop) + { + ChartLooped?.Invoke(new LoopEventArgs(_curDirector, incLoop)); + } + } + private void DebugKillEnemy() { - //Enemy.TakeDamage(1000); + Enemy.TakeDamage(1000); } } diff --git a/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs b/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs index 10f2ef92..b37a8ecb 100644 --- a/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs +++ b/Scenes/Puppets/Enemies/BossBlood/P_BossBlood.cs @@ -25,7 +25,7 @@ public override void _Ready() this, BattleEffectTrigger.OnLoop, 5, - (director, eff, val) => + (e, eff, val) => { eff.Owner.Heal(val); } diff --git a/Scenes/Puppets/Enemies/EnemyEffect.cs b/Scenes/Puppets/Enemies/EnemyEffect.cs index 70e0171b..8fb1bc1d 100644 --- a/Scenes/Puppets/Enemies/EnemyEffect.cs +++ b/Scenes/Puppets/Enemies/EnemyEffect.cs @@ -7,13 +7,13 @@ public class EnemyEffect : IBattleEvent public EnemyPuppet Owner; private int _baseValue; public int Value; - private Action _onEnemyEffect; + private Action _onEnemyEffect; public EnemyEffect( EnemyPuppet owner, BattleEffectTrigger trigger, int val, - Action onEnemyEffect + Action onEnemyEffect ) { Owner = owner; @@ -23,9 +23,9 @@ Action onEnemyEffect _onEnemyEffect = onEnemyEffect; } - public void OnTrigger(BattleDirector battleDirector) + public void OnTrigger(BattleEventArgs e) { - _onEnemyEffect(battleDirector, this, Value); + _onEnemyEffect(e, this, Value); } public BattleEffectTrigger GetTrigger() From b435cda28a74288092488c67ace308ffd47ba154 Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Sat, 5 Apr 2025 18:56:50 -0700 Subject: [PATCH 2/4] Make NPB fill with input color Could still use some tweaking and juice --- Scenes/BattleDirector/NotePlacementBar.tscn | 27 ++++++------- .../BattleDirector/Scripts/BattleDirector.cs | 4 +- .../Scripts/NotePlacementBar.cs | 38 +++++++++++++++++-- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/Scenes/BattleDirector/NotePlacementBar.tscn b/Scenes/BattleDirector/NotePlacementBar.tscn index 34f29a29..8376f287 100644 --- a/Scenes/BattleDirector/NotePlacementBar.tscn +++ b/Scenes/BattleDirector/NotePlacementBar.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=12 format=3 uid="uid://duhiilcv4tat3"] -[ext_resource type="Script" path="res://Scenes/BattleDirector/Scripts/NotePlacementBar.cs" id="1_456es"] +[ext_resource type="Script" uid="uid://gj666xe815py" path="res://Scenes/BattleDirector/Scripts/NotePlacementBar.cs" id="1_456es"] [ext_resource type="Texture2D" uid="uid://cnyr5usjdv0ni" path="res://Scenes/BattleDirector/Assets/NoteQueue_Frame.png" id="2_3tw16"] [ext_resource type="Texture2D" uid="uid://c3chrsxrulapd" path="res://Classes/Notes/Assets/Note_PlayerBasic.png" id="3_6ylx6"] [ext_resource type="Texture2D" uid="uid://caw70lr5e1yiq" path="res://Classes/Notes/Assets/Note_PlayerDouble.png" id="4_6w8ha"] @@ -14,14 +14,23 @@ width = 34 height = 100 [sub_resource type="Gradient" id="Gradient_xvck1"] -offsets = PackedFloat32Array(0, 0.373239, 0.690141, 1) -colors = PackedColorArray(0, 1, 0, 1, 1, 0, 0.4, 1, 0, 1, 0.95, 1, 1, 0, 0, 1) +offsets = PackedFloat32Array(0) +colors = PackedColorArray(0, 0, 0, 1) [sub_resource type="GradientTexture2D" id="GradientTexture2D_0bqho"] gradient = SubResource("Gradient_xvck1") width = 32 height = 98 -fill_to = Vector2(0, 1) +fill_from = Vector2(0, 1) +fill_to = Vector2(0, 0) + +[sub_resource type="Gradient" id="Gradient_2uknl"] +offsets = PackedFloat32Array(0) +colors = PackedColorArray(0.46, 0.2162, 0.39905, 1) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_d62c7"] +gradient = SubResource("Gradient_2uknl") +width = 1 [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_fy2uu"] lifetime_randomness = 0.32 @@ -41,14 +50,6 @@ turbulence_noise_scale = 5.0 turbulence_influence_min = 0.0 turbulence_influence_max = 0.018 -[sub_resource type="Gradient" id="Gradient_2uknl"] -offsets = PackedFloat32Array(0) -colors = PackedColorArray(0.46, 0.2162, 0.39905, 1) - -[sub_resource type="GradientTexture1D" id="GradientTexture1D_d62c7"] -gradient = SubResource("Gradient_2uknl") -width = 1 - [node name="NotePlacementBar" type="Control" node_paths=PackedStringArray("_notePlacementBar", "_particles", "_fullBarParticles", "_currentComboMultText", "_currentNote", "_nextNote")] layout_mode = 3 anchors_preset = 15 @@ -80,12 +81,12 @@ z_index = 1 position = Vector2(-1, -32) emitting = false amount = 22 -process_material = SubResource("ParticleProcessMaterial_fy2uu") texture = SubResource("GradientTexture1D_d62c7") lifetime = 2.0 preprocess = 0.1 explosiveness = 0.3 randomness = 0.05 +process_material = SubResource("ParticleProcessMaterial_fy2uu") [node name="FullBarParticles" type="CPUParticles2D" parent="PlacementBar"] position = Vector2(14, 98) diff --git a/Scenes/BattleDirector/Scripts/BattleDirector.cs b/Scenes/BattleDirector/Scripts/BattleDirector.cs index b3b6b5f2..60771af1 100644 --- a/Scenes/BattleDirector/Scripts/BattleDirector.cs +++ b/Scenes/BattleDirector/Scripts/BattleDirector.cs @@ -167,13 +167,13 @@ private void OnTimedInput(ArrowData data, double beatDif) Timing timed = CheckTiming(beatDif); data.NoteRef.OnHit(this, timed); - NPB.HandleTiming(timed); + NPB.HandleTiming(timed, data.Type); CM.ComboText(timed, data.Type, NPB.GetCurrentCombo()); } private void ForceMiss(ArrowType type) { - NPB.HandleTiming(Timing.Miss); + NPB.HandleTiming(Timing.Miss, type); CM.ComboText(Timing.Miss, type, NPB.GetCurrentCombo()); Player.TakeDamage(4); } diff --git a/Scenes/BattleDirector/Scripts/NotePlacementBar.cs b/Scenes/BattleDirector/Scripts/NotePlacementBar.cs index f9fdca1f..2650895b 100644 --- a/Scenes/BattleDirector/Scripts/NotePlacementBar.cs +++ b/Scenes/BattleDirector/Scripts/NotePlacementBar.cs @@ -28,6 +28,7 @@ private double CurrentBarValue [Export] private TextureProgressBar _notePlacementBar; + private Gradient _gradTex; [Export] private GpuParticles2D _particles; @@ -59,6 +60,10 @@ public override void _Ready() _notesToIncreaseCombo = 4; _barInitPosition = _notePlacementBar.Position; + + if (_notePlacementBar.TextureProgress is GradientTexture2D gradientTexture) + _gradTex = gradientTexture.Gradient; + GD.Print(_gradTex); } public override void _Process(double delta) @@ -151,6 +156,30 @@ private Note GetNote(bool getNextNote = false) } #endregion + #region Helpers + private Color[] _fillColors = [Colors.Green, Colors.Aqua, Colors.Pink, Colors.Red]; + + private void FillWithColor(ArrowType type, Color overrideCol = default) + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (CurrentBarValue == MaxValue) + return; + Color color = overrideCol == default ? _fillColors[(int)type] : overrideCol; + + if (_gradTex.GetPointCount() == 1) + _gradTex.SetColor(0, color); + + float offset = (float)((CurrentBarValue) / MaxValue); + _gradTex.AddPoint(offset, color); + } + + private void ClearColors() + { + for (int i = _gradTex.GetPointCount() - 1; i > 0; i--) + _gradTex.RemovePoint(i); + } + #endregion + #region Public Functions public int GetCurrentCombo() { @@ -166,6 +195,7 @@ public void IncreaseBonusMult(int amount = 1) public void IncreaseCharge(int amount = 1) { + FillWithColor(default, Colors.DarkViolet); CurrentBarValue += amount; } @@ -181,10 +211,11 @@ public Note NotePlaced() GD.PushWarning("Note is attempting placement without a full bar!"); Note placedNote = GetNote(Input.IsActionPressed("Secondary")); CurrentBarValue -= placedNote.CostModifier * MaxValue; + ClearColors(); return placedNote; } - public void HandleTiming(Timing timed) + public void HandleTiming(Timing timed, ArrowType type) { if (timed == Timing.Miss) { @@ -192,14 +223,15 @@ public void HandleTiming(Timing timed) } else { - HitNote(); + HitNote(type); } } // Hitting a note increases combo, combo mult, and note placement bar - private void HitNote() + private void HitNote(ArrowType type) { _currentCombo++; + FillWithColor(type); CurrentBarValue += ComboMult; UpdateComboMultText(); } From 1ac5fac9750909fd9eb59fa41f4adac70da101f6 Mon Sep 17 00:00:00 2001 From: LifeHckr Date: Sun, 6 Apr 2025 19:33:50 -0700 Subject: [PATCH 3/4] Refactored Reward Selection Refactored split functions Left selection array functions as distinct because those will need to be slightly different anyways Added button group to Reward Select Descriptions change on focus change without setting _selection --- Globals/Translations/Translations.csv | 2 +- .../Scripts/NotePlacementBar.cs | 1 - Scenes/UI/RewardSelectionUI.tscn | 2 +- Scenes/UI/Scripts/DisplayButton.cs | 13 ++- Scenes/UI/Scripts/Inventory.cs | 2 +- Scenes/UI/Scripts/RewardSelect.cs | 79 +++++++++++-------- 6 files changed, 61 insertions(+), 38 deletions(-) diff --git a/Globals/Translations/Translations.csv b/Globals/Translations/Translations.csv index 36684621..9de830cf 100644 --- a/Globals/Translations/Translations.csv +++ b/Globals/Translations/Translations.csv @@ -17,7 +17,7 @@ CONTROLS_RETURN_BUTTON,Return,返回 ESCAPE_MENU_RESUME,Resume,继续 ESCAPE_MENU_QUIT,Quit,退出 ESCAPE_MENU_TITLE,Quit to Title,返回标题 -CHEST_ROOM_REWARDS,Rewards!,奖励! +CHEST_ROOM_REWARDS,Reward Selection!,奖励! CHEST_ROOM_SKIP,Skip,跳过 CHEST_ROOM_ACCEPT,Accept,接受 BATTLE_ROOM_BEGIN_BUTTON,"Begin Battle [Enter]","开始战斗 [Enter]" diff --git a/Scenes/BattleDirector/Scripts/NotePlacementBar.cs b/Scenes/BattleDirector/Scripts/NotePlacementBar.cs index 2650895b..60d3ed0f 100644 --- a/Scenes/BattleDirector/Scripts/NotePlacementBar.cs +++ b/Scenes/BattleDirector/Scripts/NotePlacementBar.cs @@ -63,7 +63,6 @@ public override void _Ready() if (_notePlacementBar.TextureProgress is GradientTexture2D gradientTexture) _gradTex = gradientTexture.Gradient; - GD.Print(_gradTex); } public override void _Process(double delta) diff --git a/Scenes/UI/RewardSelectionUI.tscn b/Scenes/UI/RewardSelectionUI.tscn index 3a13753d..5aa2ad24 100644 --- a/Scenes/UI/RewardSelectionUI.tscn +++ b/Scenes/UI/RewardSelectionUI.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://c6icx2yriud6y"] -[ext_resource type="Script" path="res://Scenes/UI/Scripts/RewardSelect.cs" id="1_1m6an"] +[ext_resource type="Script" uid="uid://dnex3l6lt3yr4" path="res://Scenes/UI/Scripts/RewardSelect.cs" id="1_1m6an"] [node name="CanvasLayer" type="CanvasLayer" node_paths=PackedStringArray("ButtonContainer", "_description", "_acceptButton", "_skipButton")] process_mode = 2 diff --git a/Scenes/UI/Scripts/DisplayButton.cs b/Scenes/UI/Scripts/DisplayButton.cs index 76e160bb..54936e61 100644 --- a/Scenes/UI/Scripts/DisplayButton.cs +++ b/Scenes/UI/Scripts/DisplayButton.cs @@ -17,17 +17,24 @@ public partial class DisplayButton : Button [Export] public string DisplayName; - public void Display(Texture2D texture, string description, string name) + public void Display( + Texture2D texture, + string description, + string name, + bool focusHandling = false + ) { Texture = texture; Description = description; DisplayName = name; Icon = Texture; - FocusEntered += Selected; + if (focusHandling) + FocusEntered += Selected; } - private void Selected() //TODO: Button groups + //Signal pressed on focus enter, if changing focus is equivalent to pressing + private void Selected() { EmitSignal(BaseButton.SignalName.Pressed); SetPressed(true); diff --git a/Scenes/UI/Scripts/Inventory.cs b/Scenes/UI/Scripts/Inventory.cs index f26864ec..bf5f1278 100644 --- a/Scenes/UI/Scripts/Inventory.cs +++ b/Scenes/UI/Scripts/Inventory.cs @@ -34,7 +34,7 @@ private void AddDisplayButtons(IDisplayable[] displayables, Node parentNode) { var newButton = GD.Load(DisplayButton.LoadPath) .Instantiate(); - newButton.Display(item.Texture, item.Tooltip, item.Name); + newButton.Display(item.Texture, item.Tooltip, item.Name, true); newButton.Pressed += () => { DoDescription(newButton); diff --git a/Scenes/UI/Scripts/RewardSelect.cs b/Scenes/UI/Scripts/RewardSelect.cs index 4b0ec2f0..58a7ece5 100644 --- a/Scenes/UI/Scripts/RewardSelect.cs +++ b/Scenes/UI/Scripts/RewardSelect.cs @@ -17,19 +17,21 @@ public partial class RewardSelect : CanvasLayer [Export] private Button _skipButton; + private ButtonGroup _rewardGroup; + public delegate void SelectionMadeHandler(); public event SelectionMadeHandler Selected; private PlayerStats _player; - private RelicTemplate[] _rChoices; //TODO: look into typed functions - private RelicTemplate _rSelection; + private RelicTemplate[] _rChoices; private Note[] _nChoices; - private Note _nSelection; + private IDisplayable _selection; private void Initialize(PlayerStats player, int amount, Stages type) { _player = player; + _rewardGroup = new ButtonGroup(); if (type == Stages.Battle) { GenerateNoteChoices(amount); @@ -40,12 +42,24 @@ private void Initialize(PlayerStats player, int amount, Stages type) } _acceptButton.Pressed += OnSelect; + _acceptButton.FocusEntered += () => ChangeDescription(_selection); _skipButton.Pressed += OnSkip; } public override void _Process(double delta) { - _acceptButton.Visible = (_nSelection != null) || (_rSelection != null); + _acceptButton.Visible = (_selection != null); + } + + private void AddButton(IDisplayable displayable) + { + var button = new DisplayButton(); + button.SetButtonGroup(_rewardGroup); + button.ToggleMode = true; + button.Display(displayable.Texture, displayable.Tooltip, displayable.Name); + button.Pressed += () => SetSelection(displayable); + button.FocusEntered += () => ChangeDescription(displayable); + ButtonContainer.AddChild(button); } private void GenerateRelicChoices(int amount = 1) @@ -56,10 +70,7 @@ private void GenerateRelicChoices(int amount = 1) foreach (var relic in _rChoices) { - var button = new DisplayButton(); - button.Display(relic.Texture, relic.Tooltip, relic.Name); - button.Pressed += () => OnRelicSelected(relic); - ButtonContainer.AddChild(button); + AddButton(relic); } ButtonContainer.GetChild