You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
11 KiB
352 lines
11 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Babushka.scripts.CSharp.Common.Camera;
|
|
using Babushka.scripts.CSharp.Common.Util;
|
|
using Godot;
|
|
namespace Babushka.scripts.CSharp.Common.Fight;
|
|
|
|
public partial class FightInstance : Node2D //TODO: remake
|
|
{
|
|
[Export(PropertyHint.ArrayType)] private Node2D[] _friendlyFightSpots;
|
|
[Export(PropertyHint.ArrayType)] private Node2D[] _enemyFightSpots;
|
|
[Export] public Node2D camPositionNode;
|
|
[Export] private FightStateManager _fightStateManager;
|
|
[Export] private Label _fightEndText;
|
|
|
|
|
|
[Signal]
|
|
public delegate void FightStartedEventHandler();
|
|
|
|
[Signal]
|
|
public delegate void FightEndedEventHandler();
|
|
|
|
private List<Fighter> _friendlyFighters = new();
|
|
private List<Fighter> _enemyFighters = new();
|
|
|
|
private FightAttack? _stagedAttack = null;
|
|
|
|
public override void _Ready()
|
|
{
|
|
//_fightStateManager.CurrentFightState = FightStateManager.FightState.FightStartAnim;
|
|
_fightStateManager.ExitingTransition += from =>
|
|
{
|
|
switch (from)
|
|
{
|
|
case FightStateManager.FightState.None:
|
|
CaptureCamera();
|
|
Show();
|
|
EmitSignalFightStarted();
|
|
break;
|
|
case FightStateManager.FightState.Input:
|
|
HideAttackButtons();
|
|
break;
|
|
case FightStateManager.FightState.InputTargetSelect:
|
|
HideTargetButtons();
|
|
break;
|
|
case FightStateManager.FightState.FriendAttackAnim:
|
|
_stagedAttack = null;
|
|
break;
|
|
case FightStateManager.FightState.PlayerWinAnim:
|
|
case FightStateManager.FightState.EnemyWinAnim:
|
|
_fightEndText.Text = "";
|
|
break;
|
|
}
|
|
};
|
|
|
|
_fightStateManager.EnteringTransition += to =>
|
|
{
|
|
switch (to)
|
|
{
|
|
case FightStateManager.FightState.None:
|
|
EmitSignalFightEnded();
|
|
CleanUp();
|
|
Hide();
|
|
ReleaseCamera();
|
|
break;
|
|
case FightStateManager.FightState.Input:
|
|
if (CheckWinAndSetState())
|
|
break;
|
|
if (CheckFriendlyActionsLeftAndSetState())
|
|
break;
|
|
ShowAttackButtons();
|
|
break;
|
|
case FightStateManager.FightState.InputTargetSelect:
|
|
ShowTargetButtons();
|
|
break;
|
|
case FightStateManager.FightState.FriendAttackAnim:
|
|
ExecuteAttack();
|
|
GetTree().CreateTimer(1).Timeout += () => _fightStateManager.CurrentFightState = FightStateManager.FightState.Input;
|
|
break;
|
|
case FightStateManager.FightState.Enemy:
|
|
if (CheckWinAndSetState())
|
|
break;
|
|
if (CheckEnemyActionsLeftAndSetState())
|
|
break;
|
|
DecideEnemyAttack();
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.EnemyAttackAnim;
|
|
break;
|
|
case FightStateManager.FightState.EnemyAttackAnim:
|
|
ExecuteAttack();
|
|
GetTree().CreateTimer(1).Timeout += () => _fightStateManager.CurrentFightState = FightStateManager.FightState.Enemy;
|
|
break;
|
|
case FightStateManager.FightState.PlayerWinAnim:
|
|
_fightEndText.Text = "You Win!";
|
|
GetTree().CreateTimer(1.5).Timeout += () => _fightStateManager.CurrentFightState = FightStateManager.FightState.None;
|
|
break;
|
|
case FightStateManager.FightState.EnemyWinAnim:
|
|
_fightEndText.Text = "You Died :(";
|
|
GetTree().CreateTimer(3).Timeout += () => _fightStateManager.CurrentFightState = FightStateManager.FightState.None;
|
|
break;
|
|
case FightStateManager.FightState.ChangeSideToEnemy:
|
|
ResetEnemyActions();
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.Enemy;
|
|
break;
|
|
case FightStateManager.FightState.ChangeSideToFriendly:
|
|
ResetFriendlyActions();
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.Input;
|
|
break;
|
|
case FightStateManager.FightState.Heal:
|
|
Heal();
|
|
GetTree().CreateTimer(1).Timeout += () => _fightStateManager.CurrentFightState = FightStateManager.FightState.Input;
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
private void Heal()
|
|
{
|
|
// TODO: make heal staging system
|
|
_friendlyFighters.Where(f => !f.IsDead()).ForEach(f =>
|
|
{
|
|
f.Health += 50;
|
|
f.HealAnimation();
|
|
f.DecrementActions();
|
|
});
|
|
UpdateHealthVisual();
|
|
}
|
|
private void ResetEnemyActions()
|
|
{
|
|
_enemyFighters.ForEach(f => f.ResetActions());
|
|
}
|
|
|
|
private void ResetFriendlyActions()
|
|
{
|
|
_friendlyFighters.ForEach(f => f.ResetActions());
|
|
}
|
|
|
|
/**
|
|
* <returns>
|
|
* <c>true</c> if the state was changed
|
|
* </returns>
|
|
*/
|
|
private bool CheckFriendlyActionsLeftAndSetState()
|
|
{
|
|
var hasActionsLeft = _friendlyFighters.Where(f => !f.IsDead()).Any(f => f.HasActionsLeft());
|
|
if (hasActionsLeft)
|
|
{
|
|
return false;
|
|
} // else
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.ChangeSideToEnemy;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* <returns>
|
|
* <c>true</c> if the state was changed
|
|
* </returns>
|
|
*/
|
|
private bool CheckEnemyActionsLeftAndSetState()
|
|
{
|
|
var hasActionsLeft = _enemyFighters.Where(f => !f.IsDead()).Any(f => f.HasActionsLeft());
|
|
if (hasActionsLeft)
|
|
{
|
|
return false;
|
|
} // else
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.ChangeSideToFriendly;
|
|
return true;
|
|
}
|
|
|
|
private void CleanUp()
|
|
{
|
|
_enemyFighters.ForEach(f => f.QueueFree());
|
|
_friendlyFighters.ForEach(f => f.QueueFree());
|
|
_enemyFighters = new();
|
|
_friendlyFighters = new();
|
|
}
|
|
private void DecideEnemyAttack()
|
|
{
|
|
var availableEnemyFighters =
|
|
_enemyFighters
|
|
.Where(f => !f.IsDead())
|
|
.Where(f=>f.HasActionsLeft())
|
|
.ToList();
|
|
var aliveFriendlyFighters =
|
|
_friendlyFighters
|
|
.Where(f => !f.IsDead())
|
|
.ToList();
|
|
|
|
if (availableEnemyFighters.Count <= 0)
|
|
throw new InvalidOperationException("No enemy fighters available for attack.");
|
|
|
|
if (aliveFriendlyFighters.Count <= 0)
|
|
throw new InvalidOperationException("No friendly fighters available to target.");
|
|
|
|
var fighter = availableEnemyFighters.Random();
|
|
var target = aliveFriendlyFighters.Random();
|
|
|
|
_stagedAttack = new FightAttack
|
|
{
|
|
attacker = fighter!,
|
|
needsSelectedTarget = true,
|
|
damage = fighter!.attackStrength,
|
|
target = target!
|
|
};
|
|
}
|
|
|
|
private void ExecuteAttack()
|
|
{
|
|
if (_stagedAttack == null)
|
|
throw new InvalidOperationException("No staged attack to execute.");
|
|
|
|
if (!_stagedAttack.needsSelectedTarget)
|
|
throw new NotImplementedException("Non-targeted attacks are not implemented yet.");
|
|
|
|
if (_stagedAttack.needsSelectedTarget && _stagedAttack.target == null)
|
|
throw new InvalidOperationException("No target selected for the staged attack.");
|
|
|
|
_stagedAttack.target!.Health -= _stagedAttack.damage;
|
|
_stagedAttack.attacker.DecrementActions();
|
|
_stagedAttack.attacker.AttackAnimation(_stagedAttack);
|
|
|
|
UpdateHealthVisual();
|
|
}
|
|
|
|
private void UpdateHealthVisual()
|
|
{
|
|
_friendlyFighters
|
|
.Concat(_enemyFighters)
|
|
.ForEach(f => f.UpdateHealthVisual());
|
|
}
|
|
|
|
private void ReleaseCamera()
|
|
{
|
|
CameraController.Instance.fightToShow = null;
|
|
}
|
|
|
|
private void CaptureCamera()
|
|
{
|
|
CameraController.Instance.fightToShow = this;
|
|
}
|
|
|
|
public void Start(FightParty fightParty, PackedScene?[] enemies)
|
|
{
|
|
if (_fightStateManager.IsRunning())
|
|
{
|
|
GD.PushWarning("Can not start a running fight");
|
|
return;
|
|
}
|
|
|
|
if (fightParty.vesna)
|
|
{
|
|
InstantiateFighter(_friendlyFightSpots[1], FightManager.Instance.fightingVesnaScene);
|
|
}
|
|
|
|
for (var i = 0; i < Math.Min(_enemyFightSpots.Length, enemies.Length); i++)
|
|
{
|
|
var enemy = enemies[i];
|
|
if (enemy == null)
|
|
continue;
|
|
|
|
InstantiateFighter(_enemyFightSpots[i], enemy, true);
|
|
}
|
|
|
|
_fightStateManager.ToStartAnim();
|
|
}
|
|
|
|
private void InstantiateFighter(Node2D parent, PackedScene fighterScene, bool isEnemy = false)
|
|
{
|
|
var fighter = fighterScene.Instantiate<Fighter>();
|
|
fighter.fightInstance = this;
|
|
parent.AddChild(fighter);
|
|
|
|
if (isEnemy)
|
|
{
|
|
_enemyFighters.Add(fighter);
|
|
}
|
|
else
|
|
{
|
|
_friendlyFighters.Add(fighter);
|
|
}
|
|
}
|
|
|
|
public void SelectAttack(Fighter fighter)
|
|
{
|
|
_stagedAttack = new FightAttack
|
|
{
|
|
attacker = fighter,
|
|
damage = fighter.attackStrength,
|
|
needsSelectedTarget = true
|
|
};
|
|
|
|
if (_stagedAttack.needsSelectedTarget)
|
|
{
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.InputTargetSelect;
|
|
}
|
|
else
|
|
{
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.FriendAttackAnim;
|
|
}
|
|
}
|
|
|
|
private void HideAttackButtons()
|
|
{
|
|
_friendlyFighters.ForEach(f => f.HideAttackButton());
|
|
}
|
|
|
|
private void ShowAttackButtons()
|
|
{
|
|
_friendlyFighters.ForEach(f => f.ShowAttackButton());
|
|
}
|
|
|
|
private void HideTargetButtons()
|
|
{
|
|
_enemyFighters.ForEach(f => f.HideTargetButtons());
|
|
}
|
|
|
|
private void ShowTargetButtons()
|
|
{
|
|
_enemyFighters.Where(f => !f.IsDead()).ForEach(f => f.ShowTargetButtons());
|
|
}
|
|
|
|
public void SelectTargetAndAttack(Fighter fighter)
|
|
{
|
|
if (_stagedAttack == null)
|
|
throw new InvalidOperationException("No staged attack to select target for.");
|
|
|
|
_stagedAttack.target = fighter;
|
|
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.FriendAttackAnim;
|
|
}
|
|
|
|
public void SelectHeal(Fighter fighter)
|
|
{
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.Heal;
|
|
}
|
|
|
|
public bool CheckWinAndSetState()
|
|
{
|
|
if (_enemyFighters.All(f => f.IsDead()))
|
|
{
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.PlayerWinAnim;
|
|
return true;
|
|
}
|
|
if (_friendlyFighters.All(f => f.IsDead()))
|
|
{
|
|
_fightStateManager.CurrentFightState = FightStateManager.FightState.EnemyWinAnim;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|