From 83dc6bfd5646d4d051dcbfedc647cb6c04b4a5e9 Mon Sep 17 00:00:00 2001 From: jonathan Date: Tue, 7 Oct 2025 18:08:53 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Implemented=20FightersForm?= =?UTF-8?q?ation=20to=20keep=20track=20of=20the=20fight=20entered=20state?= =?UTF-8?q?=20of=20the=20fighters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prefabs/fight/fight_scene_switcher.tscn | 3 +- .../Common/Fight/Actions/AllyAttackAction.cs | 2 +- .../Common/Fight/Actions/BlobAttackAction.cs | 2 +- .../CSharp/Common/Fight/AllFightersVisual.cs | 15 +++-- scripts/CSharp/Common/Fight/AllyFighters.cs | 2 - scripts/CSharp/Common/Fight/FightHappening.cs | 53 ++++++++++++------ scripts/CSharp/Common/Fight/FightUtils.cs | 25 ++++----- scripts/CSharp/Common/Fight/FightWorld.cs | 17 +++--- .../CSharp/Common/Fight/FighterFormation.cs | 56 +++++++++++++++++++ .../Fight/{FighterStack.cs => FighterTurn.cs} | 2 +- ...FighterStack.cs.uid => FighterTurn.cs.uid} | 0 scripts/CSharp/Common/Fight/FighterVisual.cs | 2 +- .../Common/Fight/UI/ActionSelectUiSetup.cs | 2 +- 13 files changed, 127 insertions(+), 54 deletions(-) create mode 100644 scripts/CSharp/Common/Fight/FighterFormation.cs rename scripts/CSharp/Common/Fight/{FighterStack.cs => FighterTurn.cs} (98%) rename scripts/CSharp/Common/Fight/{FighterStack.cs.uid => FighterTurn.cs.uid} (100%) diff --git a/prefabs/fight/fight_scene_switcher.tscn b/prefabs/fight/fight_scene_switcher.tscn index 0449045..8680ba4 100644 --- a/prefabs/fight/fight_scene_switcher.tscn +++ b/prefabs/fight/fight_scene_switcher.tscn @@ -2,8 +2,7 @@ [ext_resource type="Script" uid="uid://cql8mt5jsmcdl" path="res://scripts/CSharp/Common/Fight/FightSceneSwitcher.cs" id="1_5dt1r"] -[node name="FightSceneSwitcher" type="Node" node_paths=PackedStringArray("_sceneRoot")] +[node name="FightSceneSwitcher" type="Node"] script = ExtResource("1_5dt1r") -_sceneRoot = NodePath("") _fightRoomScenePath = "res://scenes/Babushka_scene_fight_world_room.tscn" _fightHappeningScene = "res://scenes/Babushka_scene_fight_happening.tscn" diff --git a/scripts/CSharp/Common/Fight/Actions/AllyAttackAction.cs b/scripts/CSharp/Common/Fight/Actions/AllyAttackAction.cs index 36c1062..788661b 100644 --- a/scripts/CSharp/Common/Fight/Actions/AllyAttackAction.cs +++ b/scripts/CSharp/Common/Fight/Actions/AllyAttackAction.cs @@ -52,7 +52,7 @@ public class AllyAttackAction : FighterAction public override async Task AnimateAction(AllFightersVisual allFightersVisual) { - var currentFighter = HappeningData.fighterStack.Current; + var currentFighter = HappeningData.fighterTurn.Current; var targetFighter = targetSelect.GetTarget(); var currentFighterVisual = allFightersVisual.GetVisualForFighter(currentFighter); diff --git a/scripts/CSharp/Common/Fight/Actions/BlobAttackAction.cs b/scripts/CSharp/Common/Fight/Actions/BlobAttackAction.cs index 2fcbb73..bb37064 100644 --- a/scripts/CSharp/Common/Fight/Actions/BlobAttackAction.cs +++ b/scripts/CSharp/Common/Fight/Actions/BlobAttackAction.cs @@ -23,7 +23,7 @@ public class BlobAttackAction : FighterAction public override async Task AnimateAction(AllFightersVisual allFightersVisual) { - var currentFighter = HappeningData.fighterStack.Current; + var currentFighter = HappeningData.fighterTurn.Current; var targetFighter = FightWorld.Instance.allyFighters.vesnaFighter; var currentFighterVisual = allFightersVisual.GetVisualForFighter(currentFighter); diff --git a/scripts/CSharp/Common/Fight/AllFightersVisual.cs b/scripts/CSharp/Common/Fight/AllFightersVisual.cs index cc046f3..31cd711 100644 --- a/scripts/CSharp/Common/Fight/AllFightersVisual.cs +++ b/scripts/CSharp/Common/Fight/AllFightersVisual.cs @@ -1,10 +1,11 @@ -using Godot; using System; using System.Collections.Generic; using System.Linq; -using Babushka.scripts.CSharp.Common.Fight; using Babushka.scripts.CSharp.Common.Fight.ActionDetails; using Babushka.scripts.CSharp.Common.Util; +using Godot; + +namespace Babushka.scripts.CSharp.Common.Fight; public partial class AllFightersVisual : Node { @@ -57,7 +58,7 @@ public partial class AllFightersVisual : Node if (from == FightHappening.FightState.ActionAnim) { - _fighterVisuals.Values.ForEach(fv=>fv.UpdateHealthBar()); + _fighterVisuals.Values.ForEach(fv => fv.UpdateHealthBar()); } } @@ -112,10 +113,14 @@ public partial class AllFightersVisual : Node private void ShowTargetSelect(TargetSelectActionDetail targetDetail) { if (targetDetail.selectEnemy) - _fighterVisuals.Where(kv => kv.Key.isEnemy).ForEach(kv => kv.Value.SetTargetSelectionActive(true)); + _fighterVisuals + .Where(kv => kv.Key.IsInFormation(HappeningData.enemyFighterFormation)) + .ForEach(kv => kv.Value.SetTargetSelectionActive(true)); if (targetDetail.selectAlly) - _fighterVisuals.Where(kv => !kv.Key.isEnemy).ForEach(kv => kv.Value.SetTargetSelectionActive(true)); + _fighterVisuals + .Where(kv => kv.Key.IsInFormation(HappeningData.allyFighterFormation)) + .ForEach(kv => kv.Value.SetTargetSelectionActive(true)); } private void HideTargetSelect() diff --git a/scripts/CSharp/Common/Fight/AllyFighters.cs b/scripts/CSharp/Common/Fight/AllyFighters.cs index ce294bd..765a9cb 100644 --- a/scripts/CSharp/Common/Fight/AllyFighters.cs +++ b/scripts/CSharp/Common/Fight/AllyFighters.cs @@ -8,7 +8,6 @@ public class AllyFighters { type = FightWorld.Fighter.Type.Vesna, maxHealth = 20, - isEnemy = false, availableActions = [ new AllyAttackAction() @@ -18,7 +17,6 @@ public class AllyFighters { type = FightWorld.Fighter.Type.Chuha, maxHealth = 15, - isEnemy = false, availableActions = [ new FighterAction.Skip() diff --git a/scripts/CSharp/Common/Fight/FightHappening.cs b/scripts/CSharp/Common/Fight/FightHappening.cs index ea189a8..e1df008 100644 --- a/scripts/CSharp/Common/Fight/FightHappening.cs +++ b/scripts/CSharp/Common/Fight/FightHappening.cs @@ -61,7 +61,7 @@ public partial class FightHappening : Node private static FightWorld.FightHappeningData HappeningData => FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException(); - private static FightWorld.Fighter CurrentFighter => HappeningData.fighterStack.Current; + private static FightWorld.Fighter CurrentFighter => HappeningData.fighterTurn.Current; #endregion @@ -88,7 +88,7 @@ public partial class FightHappening : Node } #endregion - + public override void _Ready() { SetupInstance(); @@ -110,7 +110,7 @@ public partial class FightHappening : Node action.Reset(); ChangeState(FightState.ActionCheckDetails); } - + public void DetailFilled() { RequireState(FightState.InputActionDetail); @@ -204,7 +204,7 @@ public partial class FightHappening : Node { ChangeState(FightState.FightersEnter); } - else if (CurrentFighter.isEnemy) + else if (CurrentFighter.IsInFormation(HappeningData.enemyFighterFormation)) { ChangeState(FightState.EnemyActionSelect); } @@ -219,7 +219,7 @@ public partial class FightHappening : Node break; case FightState.ActionCheckDetails: RequireNotNull(HappeningData.actionStaging); - + if (ActionAbort()) ChangeState(FightState.InputActionSelect); else if (ActionNeededDetail()) @@ -249,10 +249,15 @@ public partial class FightHappening : Node _ = AdvanceToStateWhenDone(FightState.StateCheck, actionTime); } + break; + case FightState.EnemyWin: + // TODO: remove and find proper solution + ReviveVesna(); break; default: break; } } + #endregion #region Game Logic @@ -262,19 +267,23 @@ public partial class FightHappening : Node // ally var enteringAllyFighters = new List(); var allyFighters = FightWorld.Instance.allyFighters; - if (!allyFighters.vesnaFighter.entered) + if (!allyFighters.vesnaFighter.IsInFormation(HappeningData.allyFighterFormation)) { enteringAllyFighters.Add(allyFighters.vesnaFighter); } // enemy const int totalEnemySpace = 3; - var enemySpaceLeft = totalEnemySpace - HappeningData.enemyGroup.GetEnteredAmount(); + var enemySpaceLeft = HappeningData.enemyFighterFormation.GetEmptySlotCount(); return new FightersEnterStaging { enteringAllyFighters = enteringAllyFighters, - enteringEnemyFighters = HappeningData.enemyGroup.GetUptoUnenteredFighters(enemySpaceLeft).ToList() + enteringEnemyFighters = HappeningData.enemyGroup.fighters + .WhereIsAlive() + .WhereIsNotInFormation(HappeningData.enemyFighterFormation) + .Take(enemySpaceLeft) + .ToList() }; } @@ -283,21 +292,25 @@ public partial class FightHappening : Node Debug.Assert(HappeningData.fightersEnterStaging != null); foreach (var fighter in HappeningData.fightersEnterStaging.enteringAllyFighters) { - fighter.entered = true; - HappeningData.fighterStack.AddAsLast(fighter); + var emptySlotIndex = HappeningData.allyFighterFormation.GetFirstEmptySlot(); + if (emptySlotIndex < 0) throw new Exception("No empty slot for ally fighter to enter"); + HappeningData.allyFighterFormation.SetFighterAtPosition(emptySlotIndex, fighter); + HappeningData.fighterTurn.AddAsLast(fighter); } foreach (var fighter in HappeningData.fightersEnterStaging.enteringEnemyFighters) { - fighter.entered = true; - HappeningData.fighterStack.AddAsLast(fighter); + var emptySlotIndex = HappeningData.enemyFighterFormation.GetFirstEmptySlot(); + if (emptySlotIndex < 0) throw new Exception("No empty slot for enemy fighter to enter"); + HappeningData.enemyFighterFormation.SetFighterAtPosition(emptySlotIndex, fighter); + HappeningData.fighterTurn.AddAsLast(fighter); } } private void ExecuteNextFighter() { - HappeningData.fighterStack.Next(); - CurrentFighter.actionPointsLeft = CurrentFighter.maxActionPoints; + HappeningData.fighterTurn.Next(); + CurrentFighter.actionPointsLeft = FightWorld.Fighter.MaxActionPoints; } private void ExecuteAction() @@ -325,6 +338,14 @@ public partial class FightHappening : Node return HappeningData.actionStaging.NextDetail(); } + // TODO: remove + private void ReviveVesna() + { + var vesnaFighter = FightWorld.Instance.allyFighters.vesnaFighter; + vesnaFighter.health = vesnaFighter.maxHealth; + GD.Print("Vesna has been revived. This is for the current prototype only"); + } + #endregion // Game Logic #region Utility @@ -337,7 +358,7 @@ public partial class FightHappening : Node throw new Exception( $"Can not call this Method while in state {HappeningData.fightState}. Only available in {string.Join(" ,", states)}"); } - + private void RequireNotNull(Object? o) { if (o != null) @@ -362,6 +383,4 @@ public partial class FightHappening : Node } #endregion - - } \ No newline at end of file diff --git a/scripts/CSharp/Common/Fight/FightUtils.cs b/scripts/CSharp/Common/Fight/FightUtils.cs index 823dcc7..52d33af 100644 --- a/scripts/CSharp/Common/Fight/FightUtils.cs +++ b/scripts/CSharp/Common/Fight/FightUtils.cs @@ -6,22 +6,14 @@ namespace Babushka.scripts.CSharp.Common.Fight; public static class FightUtils { - public static int GetEnteredAmount(this FightWorld.FighterGroup self) + public static IEnumerable WhereIsAlive(this IEnumerable self) { - return self.enemies.Count(e => e.IsAlive() && e.entered); + return self.Where(e => e.IsAlive()); } - - public static IEnumerable GetUptoUnenteredFighters( - this FightWorld.FighterGroup self, - int maxFighters) + + public static IEnumerable WhereIsNotInFormation(this IEnumerable self, FighterFormation formation) { - if (maxFighters <= self.enemies.Count) - return self.enemies - .Where(e => !e.entered && e.IsAlive()); - - return self.enemies - .Where(e => !e.entered && e.IsAlive()) - .Take(maxFighters); + return self.Where(e => !e.IsInFormation(formation)); } public static bool IsAlive(this FightWorld.Fighter self) @@ -43,9 +35,14 @@ public static class FightUtils { self.health = self.GetHealth() + addHealth; } + + public static bool IsInFormation(this FightWorld.Fighter self, FighterFormation formation) + { + return formation.ContainsFighter(self); + } public static bool AreAllDead(this FightWorld.FighterGroup self) { - return self.enemies.All(e => e.IsDead()); + return self.fighters.All(e => e.IsDead()); } } \ No newline at end of file diff --git a/scripts/CSharp/Common/Fight/FightWorld.cs b/scripts/CSharp/Common/Fight/FightWorld.cs index 0596ff7..39929e8 100644 --- a/scripts/CSharp/Common/Fight/FightWorld.cs +++ b/scripts/CSharp/Common/Fight/FightWorld.cs @@ -20,14 +20,16 @@ public partial class FightWorld : Node public class FighterGroup { - public required List enemies; + public required List fighters; } public class FightHappeningData { - public FightHappening.FightState fightState = FightHappening.FightState.None; - public FighterStack fighterStack = new(); public required FighterGroup enemyGroup; + public FightHappening.FightState fightState = FightHappening.FightState.None; + public readonly FighterTurn fighterTurn = new(); + public readonly FighterFormation allyFighterFormation = new(); + public readonly FighterFormation enemyFighterFormation = new(); public FightHappening.FightersEnterStaging? fightersEnterStaging; public FighterAction? actionStaging; } @@ -46,11 +48,9 @@ public partial class FightWorld : Node public required Type type; public required int maxHealth; - public required bool isEnemy; public required List availableActions; - public int maxActionPoints = 1; + public const int MaxActionPoints = 1; public int? health = null; // null => initialize to full health on spawn - public bool entered = false; public int actionPointsLeft; public FighterAction AutoSelectAction() @@ -147,14 +147,14 @@ public partial class FightWorld : Node { var enemyGroup = new FighterGroup { - enemies = [] + fighters = [] }; var enemyCount = GD.RandRange(1, 3); for (var i = 0; i < enemyCount; i++) { - enemyGroup.enemies.Add(GenerateSingleEnemy()); + enemyGroup.fighters.Add(GenerateSingleEnemy()); } return enemyGroup; @@ -177,7 +177,6 @@ public partial class FightWorld : Node { type = type, health = null, - isEnemy = true, maxHealth = 12, availableActions = [ diff --git a/scripts/CSharp/Common/Fight/FighterFormation.cs b/scripts/CSharp/Common/Fight/FighterFormation.cs new file mode 100644 index 0000000..57b7931 --- /dev/null +++ b/scripts/CSharp/Common/Fight/FighterFormation.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Babushka.scripts.CSharp.Common.Fight; + +public class FighterFormation +{ + private readonly List _fighters; + + public FighterFormation(int slots = 3) + { + _fighters = []; + for (var i = 0; i < slots; i++) + { + _fighters.Add(null); + } + } + + public FightWorld.Fighter? GetFighterAtPosition(int position) + { + Debug.Assert(position >= 0, "position>=0"); + Debug.Assert(position < _fighters.Count, "position does not exist"); + + return _fighters[position]; + } + + public void SetFighterAtPosition(int position, FightWorld.Fighter value) + { + Debug.Assert(position >= 0, "position>=0"); + Debug.Assert(position < _fighters.Count, "position does not exist"); + + _fighters[position] = value; + } + + public bool ContainsFighter(FightWorld.Fighter fighter) + { + return _fighters.Contains(fighter); + } + + public int GetFirstEmptySlot() + { + for (var i = 0; i < _fighters.Count; i++) + { + if (_fighters[i] == null) + return i; + } + + return -1; + } + + public int GetEmptySlotCount() + { + return _fighters.Count(fighter => fighter == null); + } +} \ No newline at end of file diff --git a/scripts/CSharp/Common/Fight/FighterStack.cs b/scripts/CSharp/Common/Fight/FighterTurn.cs similarity index 98% rename from scripts/CSharp/Common/Fight/FighterStack.cs rename to scripts/CSharp/Common/Fight/FighterTurn.cs index 8d2049e..4f546d8 100644 --- a/scripts/CSharp/Common/Fight/FighterStack.cs +++ b/scripts/CSharp/Common/Fight/FighterTurn.cs @@ -3,7 +3,7 @@ using System.Diagnostics; namespace Babushka.scripts.CSharp.Common.Fight; -public class FighterStack +public class FighterTurn { private class Node { diff --git a/scripts/CSharp/Common/Fight/FighterStack.cs.uid b/scripts/CSharp/Common/Fight/FighterTurn.cs.uid similarity index 100% rename from scripts/CSharp/Common/Fight/FighterStack.cs.uid rename to scripts/CSharp/Common/Fight/FighterTurn.cs.uid diff --git a/scripts/CSharp/Common/Fight/FighterVisual.cs b/scripts/CSharp/Common/Fight/FighterVisual.cs index 94b2da5..04b29ba 100644 --- a/scripts/CSharp/Common/Fight/FighterVisual.cs +++ b/scripts/CSharp/Common/Fight/FighterVisual.cs @@ -38,7 +38,7 @@ public partial class FighterVisual : Node2D /// private void UpdateMirrorState() { - _visualParent.Scale = new Vector2(_boundFighter.isEnemy ? -1 : 1, 1); + _visualParent.Scale = new Vector2(_boundFighter.IsInFormation(HappeningData.enemyFighterFormation) ? -1 : 1, 1); } public void UpdateHealthBar() diff --git a/scripts/CSharp/Common/Fight/UI/ActionSelectUiSetup.cs b/scripts/CSharp/Common/Fight/UI/ActionSelectUiSetup.cs index 5f3610c..538a630 100644 --- a/scripts/CSharp/Common/Fight/UI/ActionSelectUiSetup.cs +++ b/scripts/CSharp/Common/Fight/UI/ActionSelectUiSetup.cs @@ -8,7 +8,7 @@ public partial class ActionSelectUiSetup : CanvasLayer // shortcuts private FightWorld.FightHappeningData HappeningData => FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException(); - private FightWorld.Fighter CurrentFighter => HappeningData.fighterStack.Current; + private FightWorld.Fighter CurrentFighter => HappeningData.fighterTurn.Current; // references [Export] private Button _attackActionButton = null!;