Merge pull request 'Connect farming and fighting by making them codependent' (#44) from feature/farm_fight_loop into develop
Reviewed-on: #44
This commit was merged in pull request #44.
This commit is contained in:
@@ -11,11 +11,11 @@ public partial class ActionAnimationController : Node
|
||||
#endregion
|
||||
|
||||
[Export] private AllFightersVisual _allFightersVisual = null!;
|
||||
|
||||
[Export] private FightHappeningAnimationContext _animationContext = null!;
|
||||
|
||||
public void StateEnter()
|
||||
{
|
||||
_ = HappeningData.actionStaging!.AnimateAction(_allFightersVisual);
|
||||
_ = HappeningData.actionStaging!.AnimateAction(_allFightersVisual,_animationContext);
|
||||
}
|
||||
|
||||
public void StateExit()
|
||||
|
||||
@@ -48,10 +48,11 @@ 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)
|
||||
public override async Task AnimateAction(AllFightersVisual allFightersVisual,
|
||||
FightHappeningAnimationContext animationContext)
|
||||
{
|
||||
var currentFighter = HappeningData.fighterTurn.Current;
|
||||
var targetFighter = targetSelect.GetTarget();
|
||||
@@ -64,7 +65,7 @@ public class AllyAttackAction : FighterAction
|
||||
|
||||
foreach (var hit in minigameDetail.damageHits!)
|
||||
{
|
||||
targetFighterVisual.SpawnDamageIndicatorNumber(hit);
|
||||
targetFighterVisual.SpawnDamageIndicatorNumber($"-{hit}");
|
||||
}
|
||||
|
||||
await currentFighterVisual.AnimatePosToBase();
|
||||
|
||||
@@ -18,10 +18,11 @@ 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)
|
||||
public override async Task AnimateAction(AllFightersVisual allFightersVisual,
|
||||
FightHappeningAnimationContext animationContext)
|
||||
{
|
||||
var currentFighter = HappeningData.fighterTurn.Current;
|
||||
var targetFighter = FightWorld.Instance.allyFighters.vesnaFighter;
|
||||
@@ -31,7 +32,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,48 @@
|
||||
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,
|
||||
FightHappeningAnimationContext animationContext)
|
||||
{
|
||||
var fighter = HappeningData.fighterTurn.Current;
|
||||
var fighterVisual = allFightersVisual.GetVisualForFighter(fighter);
|
||||
fighterVisual.SpawnDamageIndicatorNumber($"+{HealAmount}");
|
||||
animationContext.useHealItemIndicator.SpawnIndicator();
|
||||
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
|
||||
@@ -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);
|
||||
@@ -333,13 +333,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();
|
||||
@@ -349,7 +349,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");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class FightHappeningAnimationContext : Node
|
||||
{
|
||||
[Export] public UsedItemIndicatorVisual useHealItemIndicator = null!;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://cdrjvgm82pxoj
|
||||
@@ -1,15 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Babushka.scripts.CSharp.Common.Util;
|
||||
using Babushka.scripts.CSharp.Common.Inventory;
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class FightRoomSceneSetup : Node
|
||||
{
|
||||
[Export(PropertyHint.ArrayType)] private Node2D[] _enemyGroupSpawns;
|
||||
[Export] private PackedScene _roamingEnemyGroupPrefab;
|
||||
[Export] private FightSceneSwitcher _fightSceneSwitcher;
|
||||
[Export(PropertyHint.ArrayType)] private Node2D[] _enemyGroupSpawns = null!;
|
||||
[Export] private PackedScene _roamingEnemyGroupPrefab = null!;
|
||||
[Export] private PackedScene _itemOnGroundPrefab = null!;
|
||||
[Export] private FightSceneSwitcher _fightSceneSwitcher = null!;
|
||||
|
||||
|
||||
public override void _Ready()
|
||||
@@ -19,11 +20,30 @@ public partial class FightRoomSceneSetup : Node
|
||||
foreach (var (parent, group) in _enemyGroupSpawns.Zip(room.enemyGroups))
|
||||
{
|
||||
if (group.AreAllDead())
|
||||
continue;
|
||||
|
||||
var roamingEnemyGroup = _roamingEnemyGroupPrefab.Instantiate<RoamingEnemyGroup>();
|
||||
roamingEnemyGroup.Initialize(group, _fightSceneSwitcher);
|
||||
parent.AddChild(roamingEnemyGroup);
|
||||
{
|
||||
SpawnLoot(group, parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
SpawnEnemies(group, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnEnemies(FightWorld.FighterGroup group, Node2D parent)
|
||||
{
|
||||
var roamingEnemyGroup = _roamingEnemyGroupPrefab.Instantiate<RoamingEnemyGroup>();
|
||||
roamingEnemyGroup.Initialize(group, _fightSceneSwitcher);
|
||||
parent.AddChild(roamingEnemyGroup);
|
||||
}
|
||||
|
||||
private void SpawnLoot(FightWorld.FighterGroup group, Node2D parent)
|
||||
{
|
||||
if (group.lootToDrop == null)
|
||||
return;
|
||||
|
||||
var onGroundInstance = _itemOnGroundPrefab.Instantiate<ItemOnGround2D>();
|
||||
onGroundInstance.itemInstance = group.lootToDrop;
|
||||
parent.AddChild(onGroundInstance);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Babushka.scripts.CSharp.Common.Fight.Actions;
|
||||
using Babushka.scripts.CSharp.Common.Inventory;
|
||||
using Babushka.scripts.CSharp.Common.Util;
|
||||
using Godot;
|
||||
|
||||
@@ -19,6 +21,7 @@ public partial class FightWorld : Node
|
||||
None,
|
||||
EndOfNight
|
||||
}
|
||||
|
||||
public required Dictionary<int, Room> paths;
|
||||
public required List<FighterGroup> enemyGroups;
|
||||
public Special specialRoom = Special.None;
|
||||
@@ -27,6 +30,7 @@ public partial class FightWorld : Node
|
||||
public class FighterGroup
|
||||
{
|
||||
public required List<Fighter> fighters;
|
||||
public ItemInstance? lootToDrop = null;
|
||||
}
|
||||
|
||||
public class FightHappeningData
|
||||
@@ -56,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();
|
||||
@@ -81,6 +93,10 @@ public partial class FightWorld : Node
|
||||
public FightHappeningData? fightHappeningData = null;
|
||||
public AllyFighters allyFighters = new();
|
||||
|
||||
// settings
|
||||
[Export] private ItemResource? _itemToDropByEnemyGroup;
|
||||
[Export] public ItemResource? itemBeetrootToEatForHealth;
|
||||
|
||||
public void ResetFightWorld()
|
||||
{
|
||||
Generate();
|
||||
@@ -89,10 +105,10 @@ public partial class FightWorld : Node
|
||||
|
||||
public void Generate()
|
||||
{
|
||||
world = new Generator().GenerateWorld();
|
||||
world = new Generator(this).GenerateWorld();
|
||||
}
|
||||
|
||||
private class Generator
|
||||
private class Generator(FightWorld fightWorld)
|
||||
{
|
||||
public World GenerateWorld()
|
||||
{
|
||||
@@ -113,7 +129,7 @@ public partial class FightWorld : Node
|
||||
{
|
||||
rooms.Add(GenerateDisconnectedRoom());
|
||||
}
|
||||
|
||||
|
||||
rooms.Add(new Room
|
||||
{
|
||||
paths = [],
|
||||
@@ -162,6 +178,11 @@ public partial class FightWorld : Node
|
||||
fighters = []
|
||||
};
|
||||
|
||||
if (fightWorld._itemToDropByEnemyGroup != null)
|
||||
{
|
||||
enemyGroup.lootToDrop = new ItemInstance { blueprint = fightWorld._itemToDropByEnemyGroup };
|
||||
}
|
||||
|
||||
var enemyCount = GD.RandRange(2, 3);
|
||||
|
||||
for (var i = 0; i < enemyCount; i++)
|
||||
@@ -189,7 +210,6 @@ public partial class FightWorld : Node
|
||||
var enemy = new Fighter
|
||||
{
|
||||
type = type,
|
||||
health = null,
|
||||
maxHealth = GD.RandRange(8, 20),
|
||||
availableActions =
|
||||
[
|
||||
|
||||
@@ -30,8 +30,6 @@ public abstract class FighterAction
|
||||
public abstract bool DetailComplete();
|
||||
}
|
||||
|
||||
private bool _abort = false;
|
||||
|
||||
#region Shortcuts
|
||||
|
||||
protected static FightWorld.FightHappeningData HappeningData =>
|
||||
@@ -61,19 +59,13 @@ public abstract class FighterAction
|
||||
/// Animates the action.
|
||||
/// </summary>
|
||||
/// <param name="allFightersVisual"></param>
|
||||
public virtual async Task AnimateAction(AllFightersVisual allFightersVisual)
|
||||
/// <param name="animationContext"></param>
|
||||
public virtual async Task AnimateAction(AllFightersVisual allFightersVisual,
|
||||
FightHappeningAnimationContext animationContext)
|
||||
{
|
||||
}
|
||||
|
||||
public void MarkAbort()
|
||||
{
|
||||
_abort = true;
|
||||
}
|
||||
|
||||
public bool MarkedForAbort()
|
||||
{
|
||||
return _abort;
|
||||
}
|
||||
public virtual bool ShouldAbort() => false;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the FighterActionDetail, that is currently handled.
|
||||
|
||||
@@ -4,13 +4,13 @@ namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class FighterDamageIndicatorVisual : Node2D
|
||||
{
|
||||
[Export] private PackedScene _flyingNumberPrefab;
|
||||
[Export] private PackedScene _flyingNumberPrefab = null!;
|
||||
|
||||
|
||||
public void SpawnFlyingNumber(int number)
|
||||
public void SpawnFlyingNumber(string text)
|
||||
{
|
||||
var flyingNumberInstance = _flyingNumberPrefab.Instantiate<FighterDamageIndicatorFlyingNumber>();
|
||||
var flyingNumberInstance = _flyingNumberPrefab.Instantiate<FlyingIndicator>();
|
||||
AddChild(flyingNumberInstance);
|
||||
flyingNumberInstance.Initialize(number);
|
||||
flyingNumberInstance.Initialize(text);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+11
-5
@@ -3,13 +3,19 @@ using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class FighterDamageIndicatorFlyingNumber : Node2D
|
||||
public partial class FlyingIndicator : Node2D
|
||||
{
|
||||
[Export] private Label _label;
|
||||
|
||||
public void Initialize(int number)
|
||||
[Export] private Label _label = null!;
|
||||
[Export] private TextureRect _sprite = null!;
|
||||
|
||||
|
||||
public void Initialize(string? text = null, Texture2D? icon = null)
|
||||
{
|
||||
_label.Text = number.ToString();
|
||||
_label.Visible = text != null;
|
||||
_sprite.Visible = icon != null;
|
||||
|
||||
if (text != null) _label.Text = text;
|
||||
if (icon != null) _sprite.Texture = icon;
|
||||
|
||||
var tween = CreateTween();
|
||||
var xMovement = GD.RandRange(-150, 150);
|
||||
@@ -0,0 +1,15 @@
|
||||
using Babushka.scripts.CSharp.Common.Inventory;
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight.UI;
|
||||
|
||||
public partial class HealButtonVisual : Button
|
||||
{
|
||||
[Export] private ItemResource _healItemBlueprint = null!;
|
||||
|
||||
public void UpdateText()
|
||||
{
|
||||
var healItemsLeft = InventoryManager.Instance.playerInventory!.TotalItemsOfBlueprint(_healItemBlueprint);
|
||||
Text = $"x{healItemsLeft} - Heal";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://71mdwp2m4rta
|
||||
@@ -0,0 +1,17 @@
|
||||
using Godot;
|
||||
|
||||
namespace Babushka.scripts.CSharp.Common.Fight;
|
||||
|
||||
public partial class UsedItemIndicatorVisual : Node2D
|
||||
{
|
||||
[Export] private PackedScene _flyingIndicatorPrefab = null!;
|
||||
[Export] private Texture2D _itemTexture = null!;
|
||||
|
||||
|
||||
public void SpawnIndicator()
|
||||
{
|
||||
var flyingNumberInstance = _flyingIndicatorPrefab.Instantiate<FlyingIndicator>();
|
||||
AddChild(flyingNumberInstance);
|
||||
flyingNumberInstance.Initialize(icon: _itemTexture);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://6nniwfxye8ss
|
||||
@@ -17,7 +17,7 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
|
||||
[Signal]
|
||||
public delegate void InventoryContentsChangedEventHandler();
|
||||
|
||||
|
||||
public static string ID = "inventoryInstance";
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +52,7 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
SlotAmountChanged += UpdateSaveData;
|
||||
SavegameService.OnSaveGameReset += SaveGameReset;
|
||||
}
|
||||
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
InventoryContentsChanged -= UpdateSaveData;
|
||||
@@ -101,7 +101,8 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
return InventoryActionResult.DestinationFull;
|
||||
}
|
||||
|
||||
var itemInstance = _slots[slotIndex].itemInstance ?? new ItemInstance { blueprint = newItem.blueprint, amount = 0 };
|
||||
var itemInstance = _slots[slotIndex].itemInstance ??
|
||||
new ItemInstance { blueprint = newItem.blueprint, amount = 0 };
|
||||
var maxStack = itemInstance!.blueprint.maxStack;
|
||||
var freeOnStack = maxStack - itemInstance.amount;
|
||||
var moveAmount = Math.Min(freeOnStack, newItem.amount);
|
||||
@@ -130,12 +131,12 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
itemInstance = _slots[inventorySlot].itemInstance;
|
||||
if (itemInstance == null)
|
||||
return InventoryActionResult.SourceDoesNotExist;
|
||||
|
||||
|
||||
itemInstance.amount -= 1;
|
||||
|
||||
if(itemInstance.amount == 0)
|
||||
|
||||
if (itemInstance.amount == 0)
|
||||
_slots[inventorySlot].itemInstance = null;
|
||||
|
||||
|
||||
EmitSignal(SignalName.InventoryContentsChanged);
|
||||
return InventoryActionResult.Success;
|
||||
}
|
||||
@@ -145,6 +146,38 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
return RemoveItem(inventorySlot, out _);
|
||||
}
|
||||
|
||||
public InventoryActionResult TryRemoveAllItems(ItemInstance items)
|
||||
{
|
||||
var hasItemsCount = TotalItemsOfBlueprint(items.blueprint);
|
||||
if (hasItemsCount < items.amount)
|
||||
return InventoryActionResult.SourceDoesNotExist;
|
||||
|
||||
var amountToRemove = items.amount;
|
||||
foreach (var s in _slots)
|
||||
{
|
||||
if (s.IsEmpty() || s.itemInstance!.blueprint != items.blueprint)
|
||||
continue;
|
||||
|
||||
var slotItem = s.itemInstance!;
|
||||
if (slotItem.amount <= amountToRemove)
|
||||
{
|
||||
amountToRemove -= slotItem.amount;
|
||||
s.itemInstance = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
slotItem.amount -= amountToRemove;
|
||||
amountToRemove = 0;
|
||||
}
|
||||
|
||||
if (amountToRemove == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
EmitSignal(SignalName.InventoryContentsChanged);
|
||||
return InventoryActionResult.Success;
|
||||
}
|
||||
|
||||
public InventoryActionResult AddItemToSlot(ItemInstance itemInstance, int destinationSlot)
|
||||
{
|
||||
if (destinationSlot < 0 || destinationSlot >= _slots.Count)
|
||||
@@ -174,8 +207,8 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
{
|
||||
return items.All(HasItems);
|
||||
}
|
||||
|
||||
#region SAVE AND LOAD
|
||||
|
||||
#region SAVE AND LOAD
|
||||
|
||||
public void UpdateSaveData()
|
||||
{
|
||||
@@ -189,17 +222,17 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
string[] value = new string[2];
|
||||
value[0] = _slots[i].itemInstance.blueprint.ResourcePath;
|
||||
value[1] = _slots[i].itemInstance.amount.ToString();
|
||||
payloadData.Add(key,value);
|
||||
payloadData.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SavegameService.AppendDataToSave(ID, payloadData);
|
||||
}
|
||||
|
||||
public void LoadFromSaveData()
|
||||
{
|
||||
var id = ID;
|
||||
|
||||
|
||||
Godot.Collections.Dictionary<string, Variant> save = SavegameService.GetSaveData(id);
|
||||
|
||||
if (save.Count > 0)
|
||||
@@ -210,15 +243,15 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
{
|
||||
string[] savePayload = inventoryItemData.AsStringArray();
|
||||
ItemResource resource = ResourceLoader.Load<ItemResource>(savePayload[0]);
|
||||
int _amount = int.Parse(savePayload[1]);
|
||||
|
||||
int _amount = int.Parse(savePayload[1]);
|
||||
|
||||
ItemInstance instance = new ItemInstance { blueprint = resource, amount = _amount };
|
||||
AddItem(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called when a new save is created.
|
||||
/// Needs to do a runtime check because the InventoryInstance is already in existence at the beginning of the first scene.
|
||||
@@ -230,5 +263,6 @@ public partial class InventoryInstance : Node, ISaveable
|
||||
slot.itemInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Babushka.scripts.CSharp.Common.Inventory;
|
||||
[GlobalClass]
|
||||
public partial class ItemInstance: Resource
|
||||
{
|
||||
[Export] public ItemResource blueprint;
|
||||
[Export] public required ItemResource blueprint;
|
||||
[Export] public int amount = 1;
|
||||
|
||||
public ItemInstance Clone()
|
||||
|
||||
Reference in New Issue
Block a user