♻️ Implemented FightersFormation to keep track of the fight entered state of the fighters

pull/22/head
jonathan 3 months ago
parent 0de3bcae22
commit 83dc6bfd56

@ -2,8 +2,7 @@
[ext_resource type="Script" uid="uid://cql8mt5jsmcdl" path="res://scripts/CSharp/Common/Fight/FightSceneSwitcher.cs" id="1_5dt1r"] [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") script = ExtResource("1_5dt1r")
_sceneRoot = NodePath("")
_fightRoomScenePath = "res://scenes/Babushka_scene_fight_world_room.tscn" _fightRoomScenePath = "res://scenes/Babushka_scene_fight_world_room.tscn"
_fightHappeningScene = "res://scenes/Babushka_scene_fight_happening.tscn" _fightHappeningScene = "res://scenes/Babushka_scene_fight_happening.tscn"

@ -52,7 +52,7 @@ public class AllyAttackAction : FighterAction
public override async Task AnimateAction(AllFightersVisual allFightersVisual) public override async Task AnimateAction(AllFightersVisual allFightersVisual)
{ {
var currentFighter = HappeningData.fighterStack.Current; var currentFighter = HappeningData.fighterTurn.Current;
var targetFighter = targetSelect.GetTarget(); var targetFighter = targetSelect.GetTarget();
var currentFighterVisual = allFightersVisual.GetVisualForFighter(currentFighter); var currentFighterVisual = allFightersVisual.GetVisualForFighter(currentFighter);

@ -23,7 +23,7 @@ public class BlobAttackAction : FighterAction
public override async Task AnimateAction(AllFightersVisual allFightersVisual) public override async Task AnimateAction(AllFightersVisual allFightersVisual)
{ {
var currentFighter = HappeningData.fighterStack.Current; var currentFighter = HappeningData.fighterTurn.Current;
var targetFighter = FightWorld.Instance.allyFighters.vesnaFighter; var targetFighter = FightWorld.Instance.allyFighters.vesnaFighter;
var currentFighterVisual = allFightersVisual.GetVisualForFighter(currentFighter); var currentFighterVisual = allFightersVisual.GetVisualForFighter(currentFighter);

@ -1,10 +1,11 @@
using Godot;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Babushka.scripts.CSharp.Common.Fight;
using Babushka.scripts.CSharp.Common.Fight.ActionDetails; using Babushka.scripts.CSharp.Common.Fight.ActionDetails;
using Babushka.scripts.CSharp.Common.Util; using Babushka.scripts.CSharp.Common.Util;
using Godot;
namespace Babushka.scripts.CSharp.Common.Fight;
public partial class AllFightersVisual : Node public partial class AllFightersVisual : Node
{ {
@ -57,7 +58,7 @@ public partial class AllFightersVisual : Node
if (from == FightHappening.FightState.ActionAnim) 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) private void ShowTargetSelect(TargetSelectActionDetail targetDetail)
{ {
if (targetDetail.selectEnemy) 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) 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() private void HideTargetSelect()

@ -8,7 +8,6 @@ public class AllyFighters
{ {
type = FightWorld.Fighter.Type.Vesna, type = FightWorld.Fighter.Type.Vesna,
maxHealth = 20, maxHealth = 20,
isEnemy = false,
availableActions = availableActions =
[ [
new AllyAttackAction() new AllyAttackAction()
@ -18,7 +17,6 @@ public class AllyFighters
{ {
type = FightWorld.Fighter.Type.Chuha, type = FightWorld.Fighter.Type.Chuha,
maxHealth = 15, maxHealth = 15,
isEnemy = false,
availableActions = availableActions =
[ [
new FighterAction.Skip() new FighterAction.Skip()

@ -61,7 +61,7 @@ public partial class FightHappening : Node
private static FightWorld.FightHappeningData HappeningData => private static FightWorld.FightHappeningData HappeningData =>
FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException(); FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException();
private static FightWorld.Fighter CurrentFighter => HappeningData.fighterStack.Current; private static FightWorld.Fighter CurrentFighter => HappeningData.fighterTurn.Current;
#endregion #endregion
@ -88,7 +88,7 @@ public partial class FightHappening : Node
} }
#endregion #endregion
public override void _Ready() public override void _Ready()
{ {
SetupInstance(); SetupInstance();
@ -110,7 +110,7 @@ public partial class FightHappening : Node
action.Reset(); action.Reset();
ChangeState(FightState.ActionCheckDetails); ChangeState(FightState.ActionCheckDetails);
} }
public void DetailFilled() public void DetailFilled()
{ {
RequireState(FightState.InputActionDetail); RequireState(FightState.InputActionDetail);
@ -204,7 +204,7 @@ public partial class FightHappening : Node
{ {
ChangeState(FightState.FightersEnter); ChangeState(FightState.FightersEnter);
} }
else if (CurrentFighter.isEnemy) else if (CurrentFighter.IsInFormation(HappeningData.enemyFighterFormation))
{ {
ChangeState(FightState.EnemyActionSelect); ChangeState(FightState.EnemyActionSelect);
} }
@ -219,7 +219,7 @@ public partial class FightHappening : Node
break; break;
case FightState.ActionCheckDetails: case FightState.ActionCheckDetails:
RequireNotNull(HappeningData.actionStaging); RequireNotNull(HappeningData.actionStaging);
if (ActionAbort()) if (ActionAbort())
ChangeState(FightState.InputActionSelect); ChangeState(FightState.InputActionSelect);
else if (ActionNeededDetail()) else if (ActionNeededDetail())
@ -249,10 +249,15 @@ public partial class FightHappening : Node
_ = AdvanceToStateWhenDone(FightState.StateCheck, actionTime); _ = AdvanceToStateWhenDone(FightState.StateCheck, actionTime);
} }
break;
case FightState.EnemyWin:
// TODO: remove and find proper solution
ReviveVesna();
break; break;
default: break; default: break;
} }
} }
#endregion #endregion
#region Game Logic #region Game Logic
@ -262,19 +267,23 @@ public partial class FightHappening : Node
// ally // ally
var enteringAllyFighters = new List<FightWorld.Fighter>(); var enteringAllyFighters = new List<FightWorld.Fighter>();
var allyFighters = FightWorld.Instance.allyFighters; var allyFighters = FightWorld.Instance.allyFighters;
if (!allyFighters.vesnaFighter.entered) if (!allyFighters.vesnaFighter.IsInFormation(HappeningData.allyFighterFormation))
{ {
enteringAllyFighters.Add(allyFighters.vesnaFighter); enteringAllyFighters.Add(allyFighters.vesnaFighter);
} }
// enemy // enemy
const int totalEnemySpace = 3; const int totalEnemySpace = 3;
var enemySpaceLeft = totalEnemySpace - HappeningData.enemyGroup.GetEnteredAmount(); var enemySpaceLeft = HappeningData.enemyFighterFormation.GetEmptySlotCount();
return new FightersEnterStaging return new FightersEnterStaging
{ {
enteringAllyFighters = enteringAllyFighters, 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); Debug.Assert(HappeningData.fightersEnterStaging != null);
foreach (var fighter in HappeningData.fightersEnterStaging.enteringAllyFighters) foreach (var fighter in HappeningData.fightersEnterStaging.enteringAllyFighters)
{ {
fighter.entered = true; var emptySlotIndex = HappeningData.allyFighterFormation.GetFirstEmptySlot();
HappeningData.fighterStack.AddAsLast(fighter); 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) foreach (var fighter in HappeningData.fightersEnterStaging.enteringEnemyFighters)
{ {
fighter.entered = true; var emptySlotIndex = HappeningData.enemyFighterFormation.GetFirstEmptySlot();
HappeningData.fighterStack.AddAsLast(fighter); 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() private void ExecuteNextFighter()
{ {
HappeningData.fighterStack.Next(); HappeningData.fighterTurn.Next();
CurrentFighter.actionPointsLeft = CurrentFighter.maxActionPoints; CurrentFighter.actionPointsLeft = FightWorld.Fighter.MaxActionPoints;
} }
private void ExecuteAction() private void ExecuteAction()
@ -325,6 +338,14 @@ public partial class FightHappening : Node
return HappeningData.actionStaging.NextDetail(); 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 #endregion // Game Logic
#region Utility #region Utility
@ -337,7 +358,7 @@ public partial class FightHappening : Node
throw new Exception( throw new Exception(
$"Can not call this Method while in state {HappeningData.fightState}. Only available in {string.Join(" ,", states)}"); $"Can not call this Method while in state {HappeningData.fightState}. Only available in {string.Join(" ,", states)}");
} }
private void RequireNotNull(Object? o) private void RequireNotNull(Object? o)
{ {
if (o != null) if (o != null)
@ -362,6 +383,4 @@ public partial class FightHappening : Node
} }
#endregion #endregion
} }

@ -6,22 +6,14 @@ namespace Babushka.scripts.CSharp.Common.Fight;
public static class FightUtils public static class FightUtils
{ {
public static int GetEnteredAmount(this FightWorld.FighterGroup self) public static IEnumerable<FightWorld.Fighter> WhereIsAlive(this IEnumerable<FightWorld.Fighter> self)
{ {
return self.enemies.Count(e => e.IsAlive() && e.entered); return self.Where(e => e.IsAlive());
} }
public static IEnumerable<FightWorld.Fighter> GetUptoUnenteredFighters( public static IEnumerable<FightWorld.Fighter> WhereIsNotInFormation(this IEnumerable<FightWorld.Fighter> self, FighterFormation formation)
this FightWorld.FighterGroup self,
int maxFighters)
{ {
if (maxFighters <= self.enemies.Count) return self.Where(e => !e.IsInFormation(formation));
return self.enemies
.Where(e => !e.entered && e.IsAlive());
return self.enemies
.Where(e => !e.entered && e.IsAlive())
.Take(maxFighters);
} }
public static bool IsAlive(this FightWorld.Fighter self) public static bool IsAlive(this FightWorld.Fighter self)
@ -43,9 +35,14 @@ public static class FightUtils
{ {
self.health = self.GetHealth() + addHealth; 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) public static bool AreAllDead(this FightWorld.FighterGroup self)
{ {
return self.enemies.All(e => e.IsDead()); return self.fighters.All(e => e.IsDead());
} }
} }

@ -20,14 +20,16 @@ public partial class FightWorld : Node
public class FighterGroup public class FighterGroup
{ {
public required List<Fighter> enemies; public required List<Fighter> fighters;
} }
public class FightHappeningData public class FightHappeningData
{ {
public FightHappening.FightState fightState = FightHappening.FightState.None;
public FighterStack fighterStack = new();
public required FighterGroup enemyGroup; 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 FightHappening.FightersEnterStaging? fightersEnterStaging;
public FighterAction? actionStaging; public FighterAction? actionStaging;
} }
@ -46,11 +48,9 @@ public partial class FightWorld : Node
public required Type type; public required Type type;
public required int maxHealth; public required int maxHealth;
public required bool isEnemy;
public required List<FighterAction> availableActions; public required List<FighterAction> availableActions;
public int maxActionPoints = 1; public const int MaxActionPoints = 1;
public int? health = null; // null => initialize to full health on spawn public int? health = null; // null => initialize to full health on spawn
public bool entered = false;
public int actionPointsLeft; public int actionPointsLeft;
public FighterAction AutoSelectAction() public FighterAction AutoSelectAction()
@ -147,14 +147,14 @@ public partial class FightWorld : Node
{ {
var enemyGroup = new FighterGroup var enemyGroup = new FighterGroup
{ {
enemies = [] fighters = []
}; };
var enemyCount = GD.RandRange(1, 3); var enemyCount = GD.RandRange(1, 3);
for (var i = 0; i < enemyCount; i++) for (var i = 0; i < enemyCount; i++)
{ {
enemyGroup.enemies.Add(GenerateSingleEnemy()); enemyGroup.fighters.Add(GenerateSingleEnemy());
} }
return enemyGroup; return enemyGroup;
@ -177,7 +177,6 @@ public partial class FightWorld : Node
{ {
type = type, type = type,
health = null, health = null,
isEnemy = true,
maxHealth = 12, maxHealth = 12,
availableActions = availableActions =
[ [

@ -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<FightWorld.Fighter?> _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);
}
}

@ -3,7 +3,7 @@ using System.Diagnostics;
namespace Babushka.scripts.CSharp.Common.Fight; namespace Babushka.scripts.CSharp.Common.Fight;
public class FighterStack public class FighterTurn
{ {
private class Node private class Node
{ {

@ -38,7 +38,7 @@ public partial class FighterVisual : Node2D
/// </summary> /// </summary>
private void UpdateMirrorState() 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() public void UpdateHealthBar()

@ -8,7 +8,7 @@ public partial class ActionSelectUiSetup : CanvasLayer
// shortcuts // shortcuts
private FightWorld.FightHappeningData HappeningData => private FightWorld.FightHappeningData HappeningData =>
FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException(); FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException();
private FightWorld.Fighter CurrentFighter => HappeningData.fighterStack.Current; private FightWorld.Fighter CurrentFighter => HappeningData.fighterTurn.Current;
// references // references
[Export] private Button _attackActionButton = null!; [Export] private Button _attackActionButton = null!;

Loading…
Cancel
Save