Made fight fightable
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight.ActionDetails;
|
||||
|
||||
public class TargetSelectActionDetail : FighterAction.FighterActionDetail
|
||||
{
|
||||
public enum VisualRange
|
||||
{
|
||||
Single
|
||||
}
|
||||
|
||||
// settings
|
||||
public required bool selectEnemy;
|
||||
public required bool selectAlly;
|
||||
public VisualRange visualRange = VisualRange.Single;
|
||||
|
||||
// result
|
||||
private FightWorld.Fighter? target;
|
||||
|
||||
public override bool DetailComplete()
|
||||
{
|
||||
return target != null;
|
||||
}
|
||||
|
||||
public void ResetResult()
|
||||
{
|
||||
target = null;
|
||||
}
|
||||
|
||||
public void SetTarget(FightWorld.Fighter fighter)
|
||||
{
|
||||
target = fighter;
|
||||
}
|
||||
|
||||
public FightWorld.Fighter GetTarget()
|
||||
{
|
||||
return target ?? throw new InvalidOperationException("No target selected");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://e8c8ym0fyprn
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Babushka.scripts.CSharp.Common.Fight.ActionDetails;
|
||||
using Babushka.scripts.CSharp.Common.Util;
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight.Actions;
|
||||
|
||||
public class AllyAttackAction : FighterAction
|
||||
{
|
||||
// details
|
||||
public TargetSelectActionDetail targetSelect = new()
|
||||
{
|
||||
selectEnemy = true,
|
||||
selectAlly = false
|
||||
};
|
||||
|
||||
public override Variant<float, Func<bool>> GetAnimationEnd()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public override bool NextDetail()
|
||||
{
|
||||
return !targetSelect.DetailComplete();
|
||||
}
|
||||
|
||||
public override FighterActionDetail CurrentDetail()
|
||||
{
|
||||
return targetSelect;
|
||||
}
|
||||
|
||||
public override AllyActionButton BindToActionButton()
|
||||
{
|
||||
return AllyActionButton.Attack;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
targetSelect.ResetResult();
|
||||
}
|
||||
|
||||
public override void ExecuteAction()
|
||||
{
|
||||
targetSelect.GetTarget().AddHealth(-5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://c8c4t80bqsja5
|
||||
@@ -1,34 +1,125 @@
|
||||
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;
|
||||
|
||||
public partial class AllFightersVisual : Node
|
||||
{
|
||||
[Export] private Node2D _allyFighters;
|
||||
[Export] private Node2D _enemyFighters;
|
||||
[ExportCategory("References")] [Export]
|
||||
private Node2D _allyFighters = null!;
|
||||
|
||||
[Export] private PackedScene _blobFighterVisual;
|
||||
[Export] private PackedScene _bigBlobFighterVisual;
|
||||
[Export] private PackedScene _mavkaFighterVisual;
|
||||
[Export] private PackedScene _yourMomFighterVisual;
|
||||
[Export] private PackedScene _vesnaFighterVisual;
|
||||
|
||||
public void EnterFighter(FightWorld.Fighter fighter, bool isEnemy)
|
||||
[Export] private Node2D _enemyFighters = null!;
|
||||
|
||||
[ExportCategory("Fighter Visual Scenes")]
|
||||
[Export] private PackedScene _blobFighterVisual = null!;
|
||||
[Export] private PackedScene _bigBlobFighterVisual = null!;
|
||||
[Export] private PackedScene _mavkaFighterVisual = null!;
|
||||
[Export] private PackedScene _yourMomFighterVisual = null!;
|
||||
[Export] private PackedScene _vesnaFighterVisual = null!;
|
||||
|
||||
[ExportCategory("Settings")]
|
||||
[Export(PropertyHint.ArrayType)] private float[] _positionDistanceFromCenter = [10, 20, 30];
|
||||
|
||||
private Dictionary<FightWorld.Fighter, FighterVisual> _fighterVisuals = new();
|
||||
|
||||
#region Shortcuts
|
||||
|
||||
private FightWorld.FightHappeningData HappeningData =>
|
||||
FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region State Reactions
|
||||
|
||||
public void FightHappeningStateChange(FightHappening.FightState from, FightHappening.FightState to)
|
||||
{
|
||||
var parent = isEnemy ? _enemyFighters : _allyFighters;
|
||||
|
||||
var packedScene = fighter.type switch
|
||||
if (to == FightHappening.FightState.FightersEnterAnim)
|
||||
{
|
||||
FightWorld.Fighter.Type.Blob => _blobFighterVisual,
|
||||
FightWorld.Fighter.Type.BigBlob => _bigBlobFighterVisual,
|
||||
FightWorld.Fighter.Type.Mavka => _mavkaFighterVisual,
|
||||
FightWorld.Fighter.Type.YourMom => _yourMomFighterVisual,
|
||||
FightWorld.Fighter.Type.Vesna => _vesnaFighterVisual,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
EnterFighter();
|
||||
}
|
||||
|
||||
var fighterVisual = packedScene.Instantiate<FighterVisual>();
|
||||
fighterVisual.Initialize(fighter);
|
||||
parent.AddChild(fighterVisual);
|
||||
if (to == FightHappening.FightState.InputActionDetail)
|
||||
{
|
||||
if (HappeningData.actionStaging!.CurrentDetail() is TargetSelectActionDetail targetDetail)
|
||||
{
|
||||
ShowTargetSelect(targetDetail);
|
||||
}
|
||||
}
|
||||
|
||||
if (from == FightHappening.FightState.InputActionDetail)
|
||||
{
|
||||
HideTargetSelect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EnterFighter()
|
||||
{
|
||||
if (HappeningData.fightersEnterStaging == null)
|
||||
return;
|
||||
|
||||
if (!HappeningData.fightersEnterStaging.HasAnyToExecute())
|
||||
return;
|
||||
|
||||
|
||||
foreach (var fighter in HappeningData.fightersEnterStaging.enteringEnemyFighters)
|
||||
{
|
||||
var packedScene = fighter.type switch
|
||||
{
|
||||
FightWorld.Fighter.Type.Blob => _blobFighterVisual,
|
||||
FightWorld.Fighter.Type.BigBlob => _bigBlobFighterVisual,
|
||||
FightWorld.Fighter.Type.Mavka => _mavkaFighterVisual,
|
||||
FightWorld.Fighter.Type.YourMom => _yourMomFighterVisual,
|
||||
FightWorld.Fighter.Type.Vesna => _vesnaFighterVisual,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
var fighterVisual = packedScene.Instantiate<FighterVisual>();
|
||||
fighterVisual.Initialize(fighter);
|
||||
_enemyFighters.AddChild(fighterVisual);
|
||||
fighterVisual.Position = new Vector2(_positionDistanceFromCenter[_enemyFighters.GetChildCount() - 1], 0);
|
||||
_fighterVisuals.Add(fighter, fighterVisual);
|
||||
}
|
||||
|
||||
foreach (var fighter in HappeningData.fightersEnterStaging.enteringAllyFighters)
|
||||
{
|
||||
var packedScene = fighter.type switch
|
||||
{
|
||||
FightWorld.Fighter.Type.Blob => _blobFighterVisual,
|
||||
FightWorld.Fighter.Type.BigBlob => _bigBlobFighterVisual,
|
||||
FightWorld.Fighter.Type.Mavka => _mavkaFighterVisual,
|
||||
FightWorld.Fighter.Type.YourMom => _yourMomFighterVisual,
|
||||
FightWorld.Fighter.Type.Vesna => _vesnaFighterVisual,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
var fighterVisual = packedScene.Instantiate<FighterVisual>();
|
||||
fighterVisual.Initialize(fighter);
|
||||
_allyFighters.AddChild(fighterVisual);
|
||||
fighterVisual.Position = new Vector2(-_positionDistanceFromCenter[_allyFighters.GetChildCount() - 1], 0);
|
||||
_fighterVisuals.Add(fighter, fighterVisual);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowTargetSelect(TargetSelectActionDetail targetDetail)
|
||||
{
|
||||
if (targetDetail.selectEnemy)
|
||||
_fighterVisuals.Where(kv => kv.Key.isEnemy).ForEach(kv => kv.Value.SetTargetSelectionActive(true));
|
||||
|
||||
if (targetDetail.selectAlly)
|
||||
_fighterVisuals.Where(kv => !kv.Key.isEnemy).ForEach(kv => kv.Value.SetTargetSelectionActive(true));
|
||||
}
|
||||
|
||||
private void HideTargetSelect()
|
||||
{
|
||||
foreach (var visual in _fighterVisuals.Values)
|
||||
{
|
||||
visual.SetTargetSelectionActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Babushka.scripts.CSharp.Common.Fight.Actions;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public class AllyFighters
|
||||
{
|
||||
public FightWorld.Fighter vesnaFighter = new()
|
||||
{
|
||||
type = FightWorld.Fighter.Type.Vesna,
|
||||
maxHealth = 20,
|
||||
isEnemy = false,
|
||||
availableActions =
|
||||
[
|
||||
new AllyAttackAction()
|
||||
]
|
||||
};
|
||||
public FightWorld.Fighter chuhaFighter = new()
|
||||
{
|
||||
type = FightWorld.Fighter.Type.Chuha,
|
||||
maxHealth = 15,
|
||||
isEnemy = false,
|
||||
availableActions =
|
||||
[
|
||||
new FighterAction.Skip()
|
||||
]
|
||||
};
|
||||
|
||||
public bool IsAlive()
|
||||
{
|
||||
return vesnaFighter.IsAlive();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dst8xcyiw18uc
|
||||
@@ -9,7 +9,7 @@ using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public class FightHappening
|
||||
public partial class FightHappening : Node
|
||||
{
|
||||
/*
|
||||
To get a visual overview of the FightHappening state machine, refer to the graph on miro:
|
||||
@@ -36,14 +36,14 @@ public class FightHappening
|
||||
EnemyWin,
|
||||
}
|
||||
|
||||
private class FightersEnterStaging
|
||||
public class FightersEnterStaging
|
||||
{
|
||||
public required List<FightWorld.Fighter> enteringAllyFighters;
|
||||
public required List<FightWorld.Fighter> enteringEnemyFighters;
|
||||
|
||||
public bool HasAnyToExecute()
|
||||
{
|
||||
return enteringAllyFighters.Count != 0 || enteringEnemyFighters.Count != 0;
|
||||
return enteringAllyFighters.Any() || enteringEnemyFighters.Any();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class FightHappening
|
||||
|
||||
#endregion
|
||||
|
||||
#region ShortCuts
|
||||
#region Shortcuts
|
||||
|
||||
private static FightWorld.FightHappeningData HappeningData =>
|
||||
FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException();
|
||||
@@ -67,18 +67,33 @@ public class FightHappening
|
||||
|
||||
#region Events
|
||||
|
||||
public event Action<FightState>? transitionFromState;
|
||||
public event Action<FightState, FightState>? transitionState;
|
||||
public event Action<FightState>? transitionToState;
|
||||
[Signal]
|
||||
public delegate void SignalTransitionFromStateEventHandler(FightState state);
|
||||
|
||||
[Signal]
|
||||
public delegate void SignalTransitionStateEventHandler(FightState from, FightState to);
|
||||
|
||||
[Signal]
|
||||
public delegate void SignalTransitionToStateEventHandler(FightState state);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Staging
|
||||
#region Singleton
|
||||
|
||||
private FightersEnterStaging? _fightersEnterStaging;
|
||||
private FighterAction? _actionStaging;
|
||||
public static FightHappening Instance = null!;
|
||||
|
||||
private void SetupInstance()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
SetupInstance();
|
||||
StartFight();
|
||||
}
|
||||
|
||||
#region Public Methods
|
||||
|
||||
@@ -88,17 +103,51 @@ public class FightHappening
|
||||
ChangeState(FightState.FightStartAnim);
|
||||
}
|
||||
|
||||
public void ActionSelect(FighterAction action)
|
||||
{
|
||||
RequireState(FightState.InputActionSelect);
|
||||
HappeningData.actionStaging = action;
|
||||
action.Reset();
|
||||
ChangeState(FightState.ActionCheckDetails);
|
||||
}
|
||||
|
||||
public void DetailFilled()
|
||||
{
|
||||
RequireState(FightState.InputActionDetail);
|
||||
ChangeState(FightState.ActionCheckDetails);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region State Machine
|
||||
|
||||
private bool _inTransition = false;
|
||||
private FightState? _changeToAfterTransition = null;
|
||||
|
||||
private void ChangeState(FightState nextState)
|
||||
{
|
||||
_changeToAfterTransition = null;
|
||||
if (_inTransition)
|
||||
{
|
||||
_changeToAfterTransition = nextState;
|
||||
return;
|
||||
}
|
||||
|
||||
_inTransition = true;
|
||||
TransitionFromState();
|
||||
var lastState = HappeningData.fightState;
|
||||
HappeningData.fightState = nextState;
|
||||
TransitionFromToState(nextState, lastState);
|
||||
TransitionToState(nextState);
|
||||
|
||||
EmitSignalSignalTransitionFromState(lastState);
|
||||
EmitSignalSignalTransitionState(lastState, nextState);
|
||||
EmitSignalSignalTransitionToState(nextState);
|
||||
_inTransition = false;
|
||||
|
||||
if (_changeToAfterTransition.HasValue)
|
||||
{
|
||||
ChangeState(_changeToAfterTransition.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void TransitionFromState()
|
||||
@@ -108,21 +157,10 @@ public class FightHappening
|
||||
{
|
||||
default: break;
|
||||
}
|
||||
|
||||
// notify everyone else
|
||||
transitionFromState?.Invoke(HappeningData.fightState);
|
||||
}
|
||||
|
||||
private void TransitionFromToState(FightState nextState, FightState lastState)
|
||||
{
|
||||
transitionState?.Invoke(lastState, nextState);
|
||||
}
|
||||
|
||||
private void TransitionToState(FightState nextState)
|
||||
{
|
||||
// notify everyone else
|
||||
transitionToState?.Invoke(nextState);
|
||||
|
||||
// fixed behaviour
|
||||
switch (HappeningData.fightState)
|
||||
{
|
||||
@@ -130,8 +168,8 @@ public class FightHappening
|
||||
AdvanceToStateInSeconds(FightState.FightersEnter, StartAnimationTime);
|
||||
break;
|
||||
case FightState.FightersEnter:
|
||||
_fightersEnterStaging = StageFightersEnter();
|
||||
if (_fightersEnterStaging.HasAnyToExecute())
|
||||
HappeningData.fightersEnterStaging = StageFightersEnter();
|
||||
if (HappeningData.fightersEnterStaging.HasAnyToExecute())
|
||||
{
|
||||
ExecuteFightersEnter();
|
||||
ChangeState(FightState.FightersEnterAnim);
|
||||
@@ -150,10 +188,11 @@ public class FightHappening
|
||||
ChangeState(FightState.StateCheck);
|
||||
break;
|
||||
case FightState.StateCheck:
|
||||
// restest action staging
|
||||
_actionStaging = null;
|
||||
// restest action staging and fighter enter staging
|
||||
HappeningData.actionStaging = null;
|
||||
HappeningData.fightersEnterStaging = null;
|
||||
|
||||
if ( /*TODO: are all allys dead*/ false)
|
||||
if (!FightWorld.Instance.allyFighters.IsAlive())
|
||||
{
|
||||
ChangeState(FightState.EnemyWin);
|
||||
}
|
||||
@@ -161,7 +200,7 @@ public class FightHappening
|
||||
{
|
||||
ChangeState(FightState.PlayerWin);
|
||||
}
|
||||
else if (CurrentFighter.actionsLeft <= 0)
|
||||
else if (CurrentFighter.actionPointsLeft <= 0)
|
||||
{
|
||||
ChangeState(FightState.FightersEnter);
|
||||
}
|
||||
@@ -179,9 +218,11 @@ public class FightHappening
|
||||
// wait for player input
|
||||
break;
|
||||
case FightState.ActionCheckDetails:
|
||||
RequireNotNull(HappeningData.actionStaging);
|
||||
|
||||
if (ActionAbort())
|
||||
ChangeState(FightState.InputActionSelect);
|
||||
else if (ActionNeededDetail() != null)
|
||||
else if (ActionNeededDetail())
|
||||
ChangeState(FightState.InputActionDetail);
|
||||
else
|
||||
ChangeState(FightState.ActionExecute);
|
||||
@@ -190,7 +231,7 @@ public class FightHappening
|
||||
// wait for player input
|
||||
break;
|
||||
case FightState.EnemyActionSelect:
|
||||
_actionStaging = CurrentFighter.AutoSelectAction();
|
||||
HappeningData.actionStaging = CurrentFighter.AutoSelectAction();
|
||||
ChangeState(FightState.ActionExecute);
|
||||
break;
|
||||
case FightState.ActionExecute:
|
||||
@@ -212,7 +253,6 @@ public class FightHappening
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Game Logic
|
||||
@@ -221,38 +261,33 @@ public class FightHappening
|
||||
{
|
||||
// ally
|
||||
var enteringAllyFighters = new List<FightWorld.Fighter>();
|
||||
//TODO
|
||||
var allyFighters = FightWorld.Instance.allyFighters;
|
||||
if (!allyFighters.vesnaFighter.entered)
|
||||
{
|
||||
enteringAllyFighters.Add(allyFighters.vesnaFighter);
|
||||
}
|
||||
|
||||
// enemy
|
||||
const int totalEnemySpace = 3;
|
||||
var enemySpaceLeft = totalEnemySpace - HappeningData.enemyGroup.GetEnteredAmount();
|
||||
var enterEnemyFighters = new List<FightWorld.Fighter>();
|
||||
|
||||
for (var i = 0; i < enemySpaceLeft; i++)
|
||||
{
|
||||
if (HappeningData.enemyGroup.TryGetFirstUnenteredFighter(out var fighter))
|
||||
{
|
||||
enterEnemyFighters.Add(fighter);
|
||||
}
|
||||
}
|
||||
|
||||
return new FightersEnterStaging
|
||||
{
|
||||
enteringAllyFighters = enteringAllyFighters,
|
||||
enteringEnemyFighters = enterEnemyFighters
|
||||
enteringEnemyFighters = HappeningData.enemyGroup.GetUptoUnenteredFighters(enemySpaceLeft).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private void ExecuteFightersEnter()
|
||||
{
|
||||
Debug.Assert(_fightersEnterStaging != null);
|
||||
foreach (var fighter in _fightersEnterStaging.enteringAllyFighters)
|
||||
Debug.Assert(HappeningData.fightersEnterStaging != null);
|
||||
foreach (var fighter in HappeningData.fightersEnterStaging.enteringAllyFighters)
|
||||
{
|
||||
fighter.entered = true;
|
||||
HappeningData.fighterStack.AddAsLast(fighter);
|
||||
}
|
||||
|
||||
foreach (var fighter in _fightersEnterStaging.enteringEnemyFighters)
|
||||
foreach (var fighter in HappeningData.fightersEnterStaging.enteringEnemyFighters)
|
||||
{
|
||||
fighter.entered = true;
|
||||
HappeningData.fighterStack.AddAsLast(fighter);
|
||||
@@ -262,30 +297,32 @@ public class FightHappening
|
||||
private void ExecuteNextFighter()
|
||||
{
|
||||
HappeningData.fighterStack.Next();
|
||||
CurrentFighter.actionPointsLeft = CurrentFighter.maxActionPoints;
|
||||
}
|
||||
|
||||
private void ExecuteAction()
|
||||
{
|
||||
Debug.Assert(_actionStaging != null);
|
||||
_actionStaging.ExecuteAction();
|
||||
Debug.Assert(HappeningData.actionStaging != null);
|
||||
HappeningData.actionStaging.ExecuteAction();
|
||||
CurrentFighter.actionPointsLeft -= HappeningData.actionStaging.GetActionPointCost();
|
||||
}
|
||||
|
||||
private Variant<float, Func<bool>> GetActionAnimationEnd()
|
||||
{
|
||||
Debug.Assert(_actionStaging != null);
|
||||
return _actionStaging.GetAnimationEnd();
|
||||
Debug.Assert(HappeningData.actionStaging != null);
|
||||
return HappeningData.actionStaging.GetAnimationEnd();
|
||||
}
|
||||
|
||||
private bool ActionAbort()
|
||||
{
|
||||
Debug.Assert(_actionStaging != null);
|
||||
return _actionStaging.MarkedForAbort();
|
||||
Debug.Assert(HappeningData.actionStaging != null);
|
||||
return HappeningData.actionStaging.MarkedForAbort();
|
||||
}
|
||||
|
||||
private FighterAction.FighterActionDetail? ActionNeededDetail()
|
||||
private bool ActionNeededDetail()
|
||||
{
|
||||
Debug.Assert(_actionStaging != null);
|
||||
return _actionStaging.NeededDetail();
|
||||
Debug.Assert(HappeningData.actionStaging != null);
|
||||
return HappeningData.actionStaging.NextDetail();
|
||||
}
|
||||
|
||||
#endregion // Game Logic
|
||||
@@ -300,6 +337,14 @@ public class FightHappening
|
||||
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)
|
||||
return;
|
||||
|
||||
throw new Exception("Object must not be null to call this method");
|
||||
}
|
||||
|
||||
private void AdvanceToStateInSeconds(FightState nextState, float seconds)
|
||||
{
|
||||
@@ -317,4 +362,6 @@ public class FightHappening
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class FightHappeningStateDebugger : Node
|
||||
{
|
||||
[Export] private Label _label;
|
||||
|
||||
private FightWorld.FightHappeningData Data => FightWorld.Instance.fightHappeningData!;
|
||||
|
||||
public void StateChange(FightHappening.FightState from, FightHappening.FightState to)
|
||||
{
|
||||
_label.Text += $"State changed from {from} to {to}\n";
|
||||
switch (to)
|
||||
{
|
||||
case FightHappening.FightState.None:
|
||||
break;
|
||||
case FightHappening.FightState.FightStartAnim:
|
||||
break;
|
||||
case FightHappening.FightState.FightersEnter:
|
||||
break;
|
||||
case FightHappening.FightState.FightersEnterAnim:
|
||||
_label.Text +=
|
||||
$" {Data.fightersEnterStaging!.enteringAllyFighters.Count} allies " +
|
||||
$"and {Data.fightersEnterStaging.enteringEnemyFighters.Count} enemies are entering the fight.\n";
|
||||
break;
|
||||
case FightHappening.FightState.NextFighter:
|
||||
break;
|
||||
case FightHappening.FightState.StateCheck:
|
||||
break;
|
||||
case FightHappening.FightState.InputActionSelect:
|
||||
break;
|
||||
case FightHappening.FightState.ActionCheckDetails:
|
||||
break;
|
||||
case FightHappening.FightState.InputActionDetail:
|
||||
break;
|
||||
case FightHappening.FightState.ActionExecute:
|
||||
_label.Text += $" Executing action: {Data.actionStaging!.GetType()}\n";
|
||||
break;
|
||||
case FightHappening.FightState.ActionAnim:
|
||||
break;
|
||||
case FightHappening.FightState.EnemyActionSelect:
|
||||
break;
|
||||
case FightHappening.FightState.PlayerWin:
|
||||
break;
|
||||
case FightHappening.FightState.EnemyWin:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(to), to, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://d2ugtb3dalrg3
|
||||
@@ -0,0 +1,27 @@
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class FightHappeningStateReaction : Node
|
||||
{
|
||||
[Export] private FightHappening.FightState _fightState;
|
||||
|
||||
[Signal]
|
||||
public delegate void OnStateEnteredEventHandler();
|
||||
|
||||
[Signal]
|
||||
public delegate void OnStateExitedEventHandler();
|
||||
|
||||
public void FightHappeningStateTransitioned(FightHappening.FightState fromState, FightHappening.FightState toState)
|
||||
{
|
||||
if (fromState == _fightState)
|
||||
{
|
||||
EmitSignalOnStateExited();
|
||||
}
|
||||
|
||||
if (toState == _fightState)
|
||||
{
|
||||
EmitSignalOnStateEntered();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://buiwuf7pjfq8
|
||||
@@ -1,21 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using Babushka.scripts.CSharp.Common.Util;
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class FightRoomSceneSetup : Node
|
||||
{
|
||||
[Export] private Label debugLabel;
|
||||
[Export(PropertyHint.ArrayType)] private Node2D[] _enemyGroupSpawns;
|
||||
[Export] private PackedScene _roamingEnemyGroupPrefab;
|
||||
[Export] private FightSceneSwitcher _fightSceneSwitcher;
|
||||
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
var room = FightWorld.Instance.currentRoom!;
|
||||
debugLabel.Text = $"Room Debug:\n{room.paths.Count} paths out of this room\n{room.enemyGroups.Count} enemy groups:\n";
|
||||
foreach (var enemyGroup in room.enemyGroups)
|
||||
|
||||
var i = 0;
|
||||
foreach (var availableParent in _enemyGroupSpawns.Shuffle())
|
||||
{
|
||||
debugLabel.Text += $" {enemyGroup.enemies.Count} enemies:\n";
|
||||
foreach (var enemy in enemyGroup.enemies)
|
||||
{
|
||||
debugLabel.Text += $" {enemy.type}\n";
|
||||
}
|
||||
var enemyGroup = room.enemyGroups[i];
|
||||
var roamingEnemyGroup = _roamingEnemyGroupPrefab.Instantiate<RoamingEnemyGroup>();
|
||||
roamingEnemyGroup.Initialize(enemyGroup, _fightSceneSwitcher);
|
||||
availableParent.AddChild(roamingEnemyGroup);
|
||||
if (i >= room.enemyGroups.Count - 1) break;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public partial class FightSceneSwitcher : Node
|
||||
private async void UnloadAfterDelay()
|
||||
{
|
||||
await ToSignal(GetTree().CreateTimer(1.0f), "timeout"); // 1.0f seconds
|
||||
sceneRoot.QueueFree();
|
||||
//sceneRoot.QueueFree();
|
||||
}
|
||||
|
||||
public void SwitchRoom(int pathIndex)
|
||||
@@ -38,4 +38,16 @@ public partial class FightSceneSwitcher : Node
|
||||
FightWorld.Instance.currentRoom = nextRoom;
|
||||
LoadNext();
|
||||
}
|
||||
|
||||
public void SwitchToFight(FightWorld.FighterGroup enemyGroup)
|
||||
{
|
||||
if (FightWorld.Instance.fightHappeningData != null)
|
||||
throw new Exception("Trying to start a fight while already in a fight");
|
||||
|
||||
FightWorld.Instance.fightHappeningData = new FightWorld.FightHappeningData
|
||||
{
|
||||
enemyGroup = enemyGroup,
|
||||
};
|
||||
LoadNext();
|
||||
}
|
||||
}
|
||||
@@ -5,41 +5,46 @@ namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public static class FightUtils
|
||||
{
|
||||
public static int GetEnteredAmount(this FightWorld.EnemyGroup self)
|
||||
public static int GetEnteredAmount(this FightWorld.FighterGroup self)
|
||||
{
|
||||
return self.enemies.Count(e => e.IsAlive() && e.entered);
|
||||
}
|
||||
|
||||
public static bool TryGetFirstUnenteredFighter(this FightWorld.EnemyGroup self, out FightWorld.Fighter fighter)
|
||||
public static IEnumerable<FightWorld.Fighter> GetUptoUnenteredFighters(
|
||||
this FightWorld.FighterGroup self,
|
||||
int maxFighters)
|
||||
{
|
||||
foreach (var f in self.enemies.Where(e=>!e.entered && e.IsAlive()))
|
||||
{
|
||||
fighter = f;
|
||||
return true;
|
||||
}
|
||||
if (maxFighters <= self.enemies.Count)
|
||||
return self.enemies
|
||||
.Where(e => !e.entered && e.IsAlive());
|
||||
|
||||
fighter = null!;
|
||||
return false;
|
||||
return self.enemies
|
||||
.Where(e => !e.entered && e.IsAlive())
|
||||
.Take(maxFighters);
|
||||
}
|
||||
|
||||
public static bool IsAlive(this FightWorld.Fighter self)
|
||||
{
|
||||
return self.GetHealth() >= 0;
|
||||
}
|
||||
|
||||
|
||||
public static bool IsDead(this FightWorld.Fighter self)
|
||||
{
|
||||
return !self.IsAlive();
|
||||
}
|
||||
|
||||
|
||||
public static int GetHealth(this FightWorld.Fighter self)
|
||||
{
|
||||
return self.health ?? self.maxHealth;
|
||||
}
|
||||
|
||||
public static bool AreAllDead(this FightWorld.EnemyGroup self)
|
||||
public static void AddHealth(this FightWorld.Fighter self, int addHealth)
|
||||
{
|
||||
self.health = self.GetHealth() + addHealth;
|
||||
}
|
||||
|
||||
public static bool AreAllDead(this FightWorld.FighterGroup self)
|
||||
{
|
||||
return self.enemies.All(e => e.IsDead());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,10 +14,10 @@ public partial class FightWorld : Node
|
||||
public class Room
|
||||
{
|
||||
public required Dictionary<int, Room> paths;
|
||||
public required List<EnemyGroup> enemyGroups;
|
||||
public required List<FighterGroup> enemyGroups;
|
||||
}
|
||||
|
||||
public class EnemyGroup
|
||||
public class FighterGroup
|
||||
{
|
||||
public required List<Fighter> enemies;
|
||||
}
|
||||
@@ -26,7 +26,9 @@ public partial class FightWorld : Node
|
||||
{
|
||||
public FightHappening.FightState fightState = FightHappening.FightState.None;
|
||||
public FighterStack fighterStack = new();
|
||||
public required EnemyGroup enemyGroup;
|
||||
public required FighterGroup enemyGroup;
|
||||
public FightHappening.FightersEnterStaging? fightersEnterStaging;
|
||||
public FighterAction? actionStaging;
|
||||
}
|
||||
|
||||
public class Fighter
|
||||
@@ -37,16 +39,18 @@ public partial class FightWorld : Node
|
||||
BigBlob,
|
||||
Mavka,
|
||||
YourMom,
|
||||
Vesna
|
||||
Vesna,
|
||||
Chuha
|
||||
}
|
||||
|
||||
public required Type type;
|
||||
public required int maxHealth;
|
||||
public required bool isEnemy;
|
||||
public required List<FighterAction> availableActions;
|
||||
public int maxActionPoints = 1;
|
||||
public int? health = null; // null => initialize to full health on spawn
|
||||
public bool entered = false;
|
||||
public int actionsLeft;
|
||||
public int actionPointsLeft;
|
||||
|
||||
public FighterAction AutoSelectAction()
|
||||
{
|
||||
@@ -69,6 +73,7 @@ public partial class FightWorld : Node
|
||||
public World? world = null;
|
||||
public Room? currentRoom = null;
|
||||
public FightHappeningData? fightHappeningData = null;
|
||||
public AllyFighters allyFighters = new();
|
||||
|
||||
public void MyEnterTree()
|
||||
{
|
||||
@@ -123,9 +128,9 @@ public partial class FightWorld : Node
|
||||
return room;
|
||||
}
|
||||
|
||||
private List<EnemyGroup> GenerateEnemyGroups()
|
||||
private List<FighterGroup> GenerateEnemyGroups()
|
||||
{
|
||||
var enemyGroups = new List<EnemyGroup>();
|
||||
var enemyGroups = new List<FighterGroup>();
|
||||
|
||||
var enemyGroupCount = GD.RandRange(1, 3);
|
||||
|
||||
@@ -137,9 +142,9 @@ public partial class FightWorld : Node
|
||||
return enemyGroups;
|
||||
}
|
||||
|
||||
private EnemyGroup GenerateSingleEnemyGroup()
|
||||
private FighterGroup GenerateSingleEnemyGroup()
|
||||
{
|
||||
var enemyGroup = new EnemyGroup
|
||||
var enemyGroup = new FighterGroup
|
||||
{
|
||||
enemies = []
|
||||
};
|
||||
@@ -158,13 +163,14 @@ public partial class FightWorld : Node
|
||||
{
|
||||
var typeRoll = GD.RandRange(0, 99);
|
||||
|
||||
var type = typeRoll switch
|
||||
{
|
||||
< 50 => Fighter.Type.Blob,
|
||||
< 75 => Fighter.Type.BigBlob,
|
||||
< 90 => Fighter.Type.Mavka,
|
||||
_ => Fighter.Type.YourMom
|
||||
};
|
||||
//var type = typeRoll switch
|
||||
//{
|
||||
// < 50 => Fighter.Type.Blob,
|
||||
// < 75 => Fighter.Type.BigBlob,
|
||||
// < 90 => Fighter.Type.Mavka,
|
||||
// _ => Fighter.Type.YourMom
|
||||
//};
|
||||
var type = Fighter.Type.Blob;
|
||||
|
||||
var enemy = new Fighter
|
||||
{
|
||||
@@ -172,7 +178,8 @@ public partial class FightWorld : Node
|
||||
health = null,
|
||||
isEnemy = true,
|
||||
maxHealth = 12,
|
||||
availableActions = [
|
||||
availableActions =
|
||||
[
|
||||
new FighterAction.Skip()
|
||||
]
|
||||
};
|
||||
|
||||
@@ -7,13 +7,24 @@ namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public abstract class FighterAction
|
||||
{
|
||||
// enum has explicit values, because they are set in godot signals as integers
|
||||
// e.g. here: BabushkaSceneFightHappening => ActionSelect/BottomPanel/VBoxContainer/MarginContainer/HBoxContainer/MarginContainer/AttackButton
|
||||
public enum AllyActionButton
|
||||
{
|
||||
None,
|
||||
Attack = 1,
|
||||
Summon = 2,
|
||||
Talk = 3,
|
||||
Flee = 4,
|
||||
}
|
||||
|
||||
public class TargetSelection
|
||||
{
|
||||
// ReSharper disable once MemberHidesStaticFromOuterClass
|
||||
public static readonly TargetSelection Skip = new() { skipTargetSelection = () => true };
|
||||
public Func<bool> skipTargetSelection = () => false;
|
||||
}
|
||||
|
||||
|
||||
public abstract class FighterActionDetail
|
||||
{
|
||||
public abstract bool DetailComplete();
|
||||
@@ -63,7 +74,46 @@ public abstract class FighterAction
|
||||
return _abort;
|
||||
}
|
||||
|
||||
public abstract FighterActionDetail? NeededDetail();
|
||||
/// <summary>
|
||||
/// Returns the FighterActionDetail, that is currently handled.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual FighterActionDetail CurrentDetail()
|
||||
{
|
||||
throw new Exception("Action has no details to handle");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the next Detail to be handled. Returns false, when there are no more details to handle.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract bool NextDetail();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the action point cost of this action.
|
||||
/// Right now, only the values 1 and 0 make sense.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual int GetActionPointCost()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will be called right after the action is selected by the player. Can be used to reset the state of the details
|
||||
/// </summary>
|
||||
public virtual void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If this action should be bound to an action button in the UI, return the corresponding enum value here.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual AllyActionButton BindToActionButton()
|
||||
{
|
||||
return AllyActionButton.None;
|
||||
}
|
||||
|
||||
public class Skip : FighterAction
|
||||
{
|
||||
@@ -72,9 +122,10 @@ public abstract class FighterAction
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public override FighterActionDetail? NeededDetail()
|
||||
public override bool NextDetail()
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://c60jugfee0bpv
|
||||
@@ -0,0 +1 @@
|
||||
uid://bahm4ukspymm2
|
||||
@@ -1,131 +1,72 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using Babushka.scripts.CSharp.Common.Fight.ActionDetails;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
[Tool]
|
||||
public partial class FighterVisual : Node2D
|
||||
{
|
||||
//[Export] public string name;
|
||||
//[Export] public int maxHealth;
|
||||
//[Export] public int attackStrength;
|
||||
//[Export] public int maxActions = 1;
|
||||
[Export] public FightWorld.Fighter.Type type;
|
||||
#region Shortcuts
|
||||
|
||||
[ExportCategory("References")]
|
||||
[Export] private Node2D _attackButtons;
|
||||
[Export] private Node2D _targetButtons;
|
||||
[Export] private Node2D _targetMarker;
|
||||
[Export] private Label _healthText;
|
||||
[Export] private Node2D _visualSprite;
|
||||
private FightWorld.FightHappeningData HappeningData =>
|
||||
FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException();
|
||||
|
||||
[Signal] public delegate void DamageTakenEventHandler();
|
||||
[Signal] public delegate void AttackingEventHandler();
|
||||
[Signal] public delegate void DyingEventHandler();
|
||||
[Signal] public delegate void HealedEventHandler();
|
||||
#endregion
|
||||
|
||||
[ExportCategory("References")]
|
||||
[Export] private Node2D _visualParent;
|
||||
[Export] private Node2D _targetSelectionParent;
|
||||
|
||||
|
||||
[Signal]
|
||||
public delegate void DamageTakenEventHandler();
|
||||
|
||||
[Signal]
|
||||
public delegate void AttackingEventHandler();
|
||||
|
||||
[Signal]
|
||||
public delegate void DyingEventHandler();
|
||||
|
||||
[Signal]
|
||||
public delegate void HealedEventHandler();
|
||||
|
||||
|
||||
private FightWorld.Fighter _boundFighter;
|
||||
|
||||
//private void Die()
|
||||
//{
|
||||
// _visualSprite.Scale = new Vector2(1, 0.3f);
|
||||
// EmitSignalDying();
|
||||
//}
|
||||
|
||||
//public override void _Ready()
|
||||
//{
|
||||
// UpdateHealthVisual();
|
||||
// ResetActions();
|
||||
//}
|
||||
|
||||
public void Initialize(FightWorld.Fighter fighter)
|
||||
{
|
||||
_boundFighter = fighter;
|
||||
UpdateHealthVisual();
|
||||
UpdateMirrorState();
|
||||
}
|
||||
|
||||
public void Attack()
|
||||
/// <summary>
|
||||
/// fighter visuals should always look to the right in the scene.
|
||||
/// This function flips the sprites horizontally, when the fighter is an enemy.
|
||||
/// </summary>
|
||||
private void UpdateMirrorState()
|
||||
{
|
||||
//FightHappening.SelectAttack(this);
|
||||
_visualParent.Scale = new Vector2(_boundFighter.isEnemy ? -1 : 1, 1);
|
||||
}
|
||||
|
||||
public void HideAttackButton()
|
||||
public void SetTargetSelectionActive(bool value)
|
||||
{
|
||||
_attackButtons.Hide();
|
||||
_targetSelectionParent.Visible = value;
|
||||
_targetSelectionParent.ProcessMode = value ? ProcessModeEnum.Inherit : ProcessModeEnum.Disabled;
|
||||
}
|
||||
|
||||
public void ShowAttackButton()
|
||||
// listen from inside
|
||||
public void ClickedTarget()
|
||||
{
|
||||
_attackButtons.Show();
|
||||
}
|
||||
if (HappeningData.actionStaging!.CurrentDetail() is not TargetSelectActionDetail targetDetail)
|
||||
throw new InvalidOperationException("No target selection needed right now");
|
||||
|
||||
public void HideTargetButtons()
|
||||
{
|
||||
_targetButtons.Hide();
|
||||
targetDetail.SetTarget(_boundFighter);
|
||||
FightHappening.Instance.DetailFilled();
|
||||
}
|
||||
|
||||
public void ShowTargetButtons()
|
||||
{
|
||||
_targetButtons.Show();
|
||||
}
|
||||
|
||||
public void TargetMouseEvent(Node viewport, InputEvent inputEvent, int shapeIdx)
|
||||
{
|
||||
if (inputEvent.IsPressed())
|
||||
ClickedTarget();
|
||||
}
|
||||
|
||||
public void AttackMouseEvent(Node viewport, InputEvent inputEvent, int shapeIdx)
|
||||
{
|
||||
if (inputEvent.IsPressed())
|
||||
ClickedAttack();
|
||||
}
|
||||
|
||||
public void HealMouseEvent(Node viewport, InputEvent inputEvent, int shapeIdx)
|
||||
{
|
||||
if (inputEvent.IsPressed())
|
||||
ClickedHeal();
|
||||
}
|
||||
|
||||
private void ClickedAttack()
|
||||
{
|
||||
//FightHappening.SelectAttack(this);
|
||||
}
|
||||
|
||||
private void ClickedHeal()
|
||||
{
|
||||
//FightHappening.SelectHeal(this);
|
||||
}
|
||||
|
||||
private void ClickedTarget()
|
||||
{
|
||||
//FightHappening.SelectTargetAndAttack(this);
|
||||
}
|
||||
|
||||
public void StartHoverTarget()
|
||||
{
|
||||
_targetMarker.Visible = true;
|
||||
}
|
||||
|
||||
public void EndHoverTarget()
|
||||
{
|
||||
_targetMarker.Visible = false;
|
||||
}
|
||||
|
||||
public void UpdateHealthVisual()
|
||||
{
|
||||
_healthText.Text = $"{_boundFighter.health}";
|
||||
}
|
||||
|
||||
public bool IsDead()
|
||||
{
|
||||
//return Health <= 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetActions()
|
||||
{
|
||||
//_actions = maxActions;
|
||||
}
|
||||
|
||||
|
||||
// Animations
|
||||
public void AttackAnimation(FightAttack attack)
|
||||
{
|
||||
EmitSignalAttacking();
|
||||
@@ -134,7 +75,6 @@ public partial class FighterVisual : Node2D
|
||||
tween.TweenCallback(Callable.From(() => attack.target?.HitAnimation(attack)));
|
||||
tween.TweenProperty(this, "position", new Vector2(0, 0), 0.7)
|
||||
.SetTrans(Tween.TransitionType.Cubic).SetEase(Tween.EaseType.Out);
|
||||
|
||||
}
|
||||
|
||||
private void HitAnimation(FightAttack attack)
|
||||
@@ -154,4 +94,4 @@ public partial class FighterVisual : Node2D
|
||||
tween.TweenProperty(this, "scale", new Vector2(1, 1), 0.4)
|
||||
.SetTrans(Tween.TransitionType.Cubic).SetEase(Tween.EaseType.Out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://b2n37glcxm8wv
|
||||
@@ -0,0 +1,20 @@
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class RoamingEnemyGroup : Node2D
|
||||
{
|
||||
private FightWorld.FighterGroup _boundEnemyGroup;
|
||||
private FightSceneSwitcher _fightSceneSwitcher;
|
||||
|
||||
public void Initialize(FightWorld.FighterGroup enemyGroup, FightSceneSwitcher fightSceneSwitcher)
|
||||
{
|
||||
_boundEnemyGroup = enemyGroup;
|
||||
_fightSceneSwitcher = fightSceneSwitcher;
|
||||
}
|
||||
|
||||
public void StartFight()
|
||||
{
|
||||
_fightSceneSwitcher.SwitchToFight(_boundEnemyGroup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://lequnojtar76
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight.UI;
|
||||
|
||||
public partial class ActionSelectUiSetup : CanvasLayer
|
||||
{
|
||||
// shortcuts
|
||||
private FightWorld.FightHappeningData HappeningData =>
|
||||
FightWorld.Instance.fightHappeningData ?? throw new NoFightHappeningException();
|
||||
private FightWorld.Fighter CurrentFighter => HappeningData.fighterStack.Current;
|
||||
|
||||
// references
|
||||
[Export] private Button _attackActionButton = null!;
|
||||
[Export] private Button _summonActionButton = null!;
|
||||
[Export] private Button _talkActionButton = null!;
|
||||
[Export] private Button _fleeActionButton = null!;
|
||||
|
||||
// gets called from a state reaction enter (InputActionSelect)
|
||||
public void StateEntered()
|
||||
{
|
||||
var actions = CurrentFighter.availableActions;
|
||||
|
||||
_attackActionButton.Visible = actions.Any(a => a.BindToActionButton() == FighterAction.AllyActionButton.Attack);
|
||||
_summonActionButton.Visible = actions.Any(a => a.BindToActionButton() == FighterAction.AllyActionButton.Summon);
|
||||
_talkActionButton.Visible = actions.Any(a => a.BindToActionButton() == FighterAction.AllyActionButton.Talk);
|
||||
_fleeActionButton.Visible = actions.Any(a => a.BindToActionButton() == FighterAction.AllyActionButton.Flee);
|
||||
}
|
||||
|
||||
public void SelectAction(FighterAction.AllyActionButton actionButton)
|
||||
{
|
||||
var action = CurrentFighter.availableActions.First(a => a.BindToActionButton() == actionButton);
|
||||
FightHappening.Instance.ActionSelect(action);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://byf2ywov34g0x
|
||||
@@ -0,0 +1,16 @@
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public partial class TargetSelectionClick : Area2D
|
||||
{
|
||||
[Signal]
|
||||
public delegate void TargetSelectedEventHandler();
|
||||
|
||||
public override void _InputEvent(Viewport viewport, InputEvent @event, int shapeIdx)
|
||||
{
|
||||
if (@event is InputEventMouseButton { Pressed: true, ButtonIndex: MouseButton.Left })
|
||||
{
|
||||
EmitSignalTargetSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://boprnfciqgixf
|
||||
@@ -16,6 +16,16 @@ public static class LinqExtras
|
||||
}
|
||||
}
|
||||
|
||||
public static void ForEach<T>(this IEnumerable<T> self, Action<T, int> action)
|
||||
{
|
||||
var i = 0;
|
||||
foreach (var t in self)
|
||||
{
|
||||
action.Invoke(t, i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public static T? Random<T>(this IEnumerable<T> self)
|
||||
{
|
||||
var selfList = self.ToList();
|
||||
@@ -24,4 +34,16 @@ public static class LinqExtras
|
||||
var randomIndex = new Random().Next(0, selfList.Count);
|
||||
return selfList[randomIndex];
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> self)
|
||||
{
|
||||
var selfList = self.ToList();
|
||||
var random = new Random();
|
||||
for (var i = 0; i < selfList.Count; i++)
|
||||
{
|
||||
var j = random.Next(i, selfList.Count);
|
||||
(selfList[i], selfList[j]) = (selfList[j], selfList[i]);
|
||||
}
|
||||
return selfList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://bxs7sn7j3vd0n
|
||||
Reference in New Issue
Block a user