When beetroot is in inventory the player can heal

This commit is contained in:
jonathan
2025-12-11 17:13:28 +01:00
parent f42c2c86b1
commit fef8380a57
16 changed files with 161 additions and 70 deletions
@@ -47,7 +47,7 @@ public class AllyAttackAction : FighterAction
public override void ExecuteAction()
{
var totalDamage = minigameDetail.damageHits!.Sum(dh => dh);
targetSelect.GetTarget().AddHealth(-totalDamage);
targetSelect.GetTarget().ChangeHealth(-totalDamage);
}
public override async Task AnimateAction(AllFightersVisual allFightersVisual)
@@ -63,7 +63,7 @@ public class AllyAttackAction : FighterAction
foreach (var hit in minigameDetail.damageHits!)
{
targetFighterVisual.SpawnDamageIndicatorNumber(hit);
targetFighterVisual.SpawnDamageIndicatorNumber($"-{hit}");
}
await currentFighterVisual.AnimatePosToBase();
@@ -18,7 +18,7 @@ public class BlobAttackAction(int damage = 3) : FighterAction
public override void ExecuteAction()
{
FightWorld.Instance.allyFighters.vesnaFighter.AddHealth(-damage);
FightWorld.Instance.allyFighters.vesnaFighter.ChangeHealth(-damage);
}
public override async Task AnimateAction(AllFightersVisual allFightersVisual)
@@ -31,7 +31,7 @@ public class BlobAttackAction(int damage = 3) : FighterAction
await currentFighterVisual.AnimatePosToTarget(targetFighterVisual);
_ = targetFighterVisual.AnimateHit();
targetFighterVisual.SpawnDamageIndicatorNumber(damage);
targetFighterVisual.SpawnDamageIndicatorNumber($"-{damage}");
await currentFighterVisual.AnimatePosToBase();
}
}
@@ -0,0 +1,46 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Babushka.scripts.CSharp.Common.Inventory;
using Babushka.scripts.CSharp.Common.Util;
namespace Babushka.scripts.CSharp.Common.Fight.Actions;
public class EatBeetrootAction : FighterAction
{
public override Variant<float, Func<bool>> GetAnimationEnd() => 1;
public override bool NextDetail() => false;
private const int HealAmount = 20;
public override bool ShouldAbort()
{
Debug.Assert(FightWorld.Instance.itemBeetrootToEatForHealth != null,
"Item to eat for health has not been set in the FightWorld autoload");
return !InventoryManager.Instance.playerInventory!.HasItems(new ItemInstance
{ blueprint = FightWorld.Instance.itemBeetrootToEatForHealth });
}
public override async Task AnimateAction(AllFightersVisual allFightersVisual)
{
var fighter = HappeningData.fighterTurn.Current;
var fighterVisual = allFightersVisual.GetVisualForFighter(fighter);
fighterVisual.SpawnDamageIndicatorNumber($"+{HealAmount}");
await fighterVisual.AnimateHeal();
}
public override void ExecuteAction()
{
var fighter = HappeningData.fighterTurn.Current;
var result = InventoryManager.Instance.playerInventory!.TryRemoveAllItems(
new ItemInstance { blueprint = FightWorld.Instance.itemBeetrootToEatForHealth! });
if (result != InventoryActionResult.Success)
throw new Exception("No Beetroot in inventory. This case should have been handled earlier");
fighter.ChangeHealth(HealAmount);
}
public override AllyActionButton BindToActionButton() => AllyActionButton.Talk; // Temporarily bound to talk button
}
@@ -0,0 +1 @@
uid://b2463q1waqvdu
+2 -1
View File
@@ -10,7 +10,8 @@ public class AllyFighters
maxHealth = 60,
availableActions =
[
new AllyAttackAction()
new AllyAttackAction(),
new EatBeetrootAction()
]
};
public FightWorld.Fighter chuhaFighter = new()
@@ -222,9 +222,9 @@ public partial class FightHappening : Node
case FightState.ActionCheckDetails:
RequireNotNull(HappeningData.actionStaging);
if (ActionAbort())
if (ShouldActionAbort())
ChangeState(FightState.InputActionSelect);
else if (ActionNeededDetail())
else if (DoesActionNeededDetail())
ChangeState(FightState.InputActionDetail);
else
ChangeState(FightState.ActionExecute);
@@ -328,13 +328,13 @@ public partial class FightHappening : Node
return HappeningData.actionStaging.GetAnimationEnd();
}
private bool ActionAbort()
private bool ShouldActionAbort()
{
Debug.Assert(HappeningData.actionStaging != null);
return HappeningData.actionStaging.MarkedForAbort();
return HappeningData.actionStaging.ShouldAbort();
}
private bool ActionNeededDetail()
private bool DoesActionNeededDetail()
{
Debug.Assert(HappeningData.actionStaging != null);
return HappeningData.actionStaging.NextDetail();
@@ -344,7 +344,7 @@ public partial class FightHappening : Node
private void ReviveVesna()
{
var vesnaFighter = FightWorld.Instance.allyFighters.vesnaFighter;
vesnaFighter.health = vesnaFighter.maxHealth;
vesnaFighter.Health = vesnaFighter.maxHealth;
GD.Print("Vesna has been revived. This is for the current prototype only");
}
+11 -10
View File
@@ -10,15 +10,16 @@ public static class FightUtils
{
return self.Where(e => e.IsAlive());
}
public static IEnumerable<FightWorld.Fighter> WhereIsNotInFormation(this IEnumerable<FightWorld.Fighter> self, FighterFormation formation)
public static IEnumerable<FightWorld.Fighter> WhereIsNotInFormation(this IEnumerable<FightWorld.Fighter> self,
FighterFormation formation)
{
return self.Where(e => !e.IsInFormation(formation));
}
public static bool IsAlive(this FightWorld.Fighter self)
{
return self.GetHealth() > 0;
return self.Health > 0;
}
public static bool IsDead(this FightWorld.Fighter self)
@@ -26,16 +27,16 @@ public static class FightUtils
return !self.IsAlive();
}
public static int GetHealth(this FightWorld.Fighter self)
/// <summary>
/// Changes the health of a fighter
/// </summary>
/// <param name="self">The fighter itself</param>
/// <param name="amount">The amount of health to add. Make negative to remove health</param>
public static void ChangeHealth(this FightWorld.Fighter self, int amount)
{
return Math.Max(self.health ?? self.maxHealth, 0);
self.Health += amount;
}
public static void AddHealth(this FightWorld.Fighter self, int addHealth)
{
self.health = self.GetHealth() + addHealth;
}
public static bool IsInFormation(this FightWorld.Fighter self, FighterFormation formation)
{
return formation.ContainsFighter(self);
+11 -2
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Babushka.scripts.CSharp.Common.Fight.Actions;
using Babushka.scripts.CSharp.Common.Inventory;
@@ -59,9 +60,17 @@ public partial class FightWorld : Node
public required int maxHealth;
public required List<FighterAction> availableActions;
public const int MaxActionPoints = 1;
public int? health = null; // null => initialize to full health on spawn
public int actionPointsLeft;
private int? _healthBacking = null;
public int Health
{
get => _healthBacking ?? maxHealth;
set => _healthBacking = Math.Clamp(value, 0, maxHealth);
}
public FighterAction AutoSelectAction()
{
return availableActions.Random() ?? new FighterAction.Skip();
@@ -86,6 +95,7 @@ public partial class FightWorld : Node
// settings
[Export] private ItemResource? _itemToDropByEnemyGroup;
[Export] public ItemResource? itemBeetrootToEatForHealth;
public void ResetFightWorld()
{
@@ -200,7 +210,6 @@ public partial class FightWorld : Node
var enemy = new Fighter
{
type = type,
health = null,
maxHealth = GD.RandRange(8, 20),
availableActions =
[
+1 -11
View File
@@ -30,8 +30,6 @@ public abstract class FighterAction
public abstract bool DetailComplete();
}
private bool _abort = false;
#region Shortcuts
protected static FightWorld.FightHappeningData HappeningData =>
@@ -65,15 +63,7 @@ public abstract class FighterAction
{
}
public void MarkAbort()
{
_abort = true;
}
public bool MarkedForAbort()
{
return _abort;
}
public virtual bool ShouldAbort() => false;
/// <summary>
/// Returns the FighterActionDetail, that is currently handled.
@@ -7,9 +7,9 @@ public partial class FighterDamageIndicatorFlyingNumber : Node2D
{
[Export] private Label _label;
public void Initialize(int number)
public void Initialize(string text)
{
_label.Text = number.ToString();
_label.Text = text;
var tween = CreateTween();
var xMovement = GD.RandRange(-150, 150);
@@ -7,10 +7,10 @@ public partial class FighterDamageIndicatorVisual : Node2D
[Export] private PackedScene _flyingNumberPrefab;
public void SpawnFlyingNumber(int number)
public void SpawnFlyingNumber(string text)
{
var flyingNumberInstance = _flyingNumberPrefab.Instantiate<FighterDamageIndicatorFlyingNumber>();
AddChild(flyingNumberInstance);
flyingNumberInstance.Initialize(number);
flyingNumberInstance.Initialize(text);
}
}
+20 -13
View File
@@ -42,7 +42,12 @@ public partial class FighterVisual : Node2D
_boundFighter.IsInFormation(HappeningData.enemyFighterFormation) ? -1 : 1,
_boundFighter.IsDead() ? .3f : 1);
healthBarVisual.UpdateHealth(_boundFighter.GetHealth(), _boundFighter.maxHealth);
UpdateHealthBarVisuals();
}
private void UpdateHealthBarVisuals()
{
healthBarVisual.UpdateHealth(_boundFighter.Health, _boundFighter.maxHealth);
}
public void SetTargetSelectionActive(bool value)
@@ -78,24 +83,26 @@ public partial class FighterVisual : Node2D
public async Task AnimateHit()
{
UpdateHealthBarVisuals();
var tween = GetTree().CreateTween();
tween.TweenProperty(_squashParent, "scale", new Vector2(1.4f, 0.6f), 0.15);
tween.TweenProperty(_squashParent, "scale", new Vector2(1, 1), 0.4)
.SetTrans(Tween.TransitionType.Cubic).SetEase(Tween.EaseType.Out);
await ToSignal(tween, "finished");
}
// Keep for reference for new Heal animation
//public void HealAnimation()
//{
// EmitSignalHealed();
// var tween = GetTree().CreateTween();
// tween.TweenProperty(this, "scale", new Vector2(0.6f, 1.4f), 0.15);
// tween.TweenProperty(this, "scale", new Vector2(1, 1), 0.4)
// .SetTrans(Tween.TransitionType.Cubic).SetEase(Tween.EaseType.Out);
//}
public void SpawnDamageIndicatorNumber(int number)
public async Task AnimateHeal()
{
_fighterDamageIndicatorVisual.SpawnFlyingNumber(number);
UpdateHealthBarVisuals();
var tween = GetTree().CreateTween();
tween.TweenProperty(_squashParent, "scale", new Vector2(0.6f, 1.4f), 0.15);
tween.TweenProperty(_squashParent, "scale", new Vector2(1, 1), 0.4)
.SetTrans(Tween.TransitionType.Cubic).SetEase(Tween.EaseType.Out);
await ToSignal(tween, "finished");
}
public void SpawnDamageIndicatorNumber(string text)
{
_fighterDamageIndicatorVisual.SpawnFlyingNumber(text);
}
}