Fix up item usage

This commit is contained in:
2025-03-07 18:40:14 -08:00
parent 93c04440d4
commit fe3c539a62
15 changed files with 125 additions and 65 deletions

View File

@@ -0,0 +1,8 @@
namespace Zennysoft.Game.Abstractions;
public interface IInventoryItem
{
string ItemName { get; }
string Description { get; }
}

View File

@@ -1,5 +1,6 @@
using Chickensoft.Collections;
using Godot;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma.Implementation;
@@ -11,12 +12,16 @@ public interface IGameRepo : IDisposable
event Action? CloseInventory;
event Action<string>? AnnounceMessage;
event Action<string>? AnnounceMessageOnMainScreenEvent;
event Action<string>? AnnounceMessageInInventoryEvent;
event Action<int>? DoubleExpTimeStart;
event Action? DoubleExpTimeEnd;
event Action<IInventoryItem>? RemoveItemFromInventoryEvent;
void Pause();
void Resume();
@@ -29,6 +34,10 @@ public interface IGameRepo : IDisposable
public void AnnounceMessageOnMainScreen(string message);
public void AnnounceMessageInInventory(string message);
public void RemoveItemFromInventory(IInventoryItem item);
public double ExpRate { get; }
}
@@ -37,9 +46,11 @@ public class GameRepo : IGameRepo
public event Action? Ended;
public event Action? OpenInventory;
public event Action? CloseInventory;
public event Action<string>? AnnounceMessage;
public event Action<string>? AnnounceMessageOnMainScreenEvent;
public event Action<string>? AnnounceMessageInInventoryEvent;
public event Action<int>? DoubleExpTimeStart;
public event Action? DoubleExpTimeEnd;
public event Action<IInventoryItem>? RemoveItemFromInventoryEvent;
public IAutoProp<bool> IsPaused => _isPaused;
private readonly AutoProp<bool> _isPaused;
@@ -68,8 +79,7 @@ public class GameRepo : IGameRepo
public void StartDoubleEXP(TimeSpan lengthOfEffect)
{
CloseInventory?.Invoke();
AnnounceMessageOnMainScreen("Experience points temporarily doubled.");
AnnounceMessageInInventory("Experience points temporarily doubled.");
DoubleExpTimeStart?.Invoke(lengthOfEffect.Seconds);
ExpRate = 2;
}
@@ -82,7 +92,17 @@ public class GameRepo : IGameRepo
public void AnnounceMessageOnMainScreen(string message)
{
AnnounceMessage?.Invoke(message);
AnnounceMessageOnMainScreenEvent?.Invoke(message);
}
public void AnnounceMessageInInventory(string message)
{
AnnounceMessageInInventoryEvent?.Invoke(message);
}
public void RemoveItemFromInventory(IInventoryItem item)
{
RemoveItemFromInventoryEvent?.Invoke(item);
}
public void OnGameEnded()

View File

@@ -1,5 +1,7 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma.Implementation;
@@ -13,18 +15,29 @@ public partial class InGameUILogic
OnAttach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.AnnounceMessage += OnAnnounceMessage;
gameRepo.AnnounceMessageOnMainScreenEvent += OnAnnounceMessageOnMainScreen;
gameRepo.AnnounceMessageInInventoryEvent += OnAnnounceMessageInInventory;
gameRepo.RemoveItemFromInventoryEvent += OnRemoveItemFromInventory;
});
OnDetach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.AnnounceMessage -= OnAnnounceMessage;
gameRepo.AnnounceMessageOnMainScreenEvent -= OnAnnounceMessageOnMainScreen;
gameRepo.AnnounceMessageInInventoryEvent -= OnAnnounceMessageInInventory;
});
}
private void OnAnnounceMessage(string message)
private void OnAnnounceMessageOnMainScreen(string message)
{
Output(new Output.AnnounceMessage(message));
}
Output(new Output.AnnounceMessageOnMainScreen(message));
}
private void OnAnnounceMessageInInventory(string message)
{
Output(new Output.AnnounceMessageInInventory(message));
}
private void OnRemoveItemFromInventory(IInventoryItem item) => Output(new Output.RemoveItemFromInventory(item));
}
}

View File

@@ -1,8 +1,12 @@
namespace Zennysoft.Game.Ma.Implementation;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma.Implementation;
public partial class InGameUILogic
{
public static class Output
{
public readonly record struct AnnounceMessage(string Message);
public readonly record struct AnnounceMessageOnMainScreen(string Message);
public readonly record struct AnnounceMessageInInventory(string Message);
public readonly record struct RemoveItemFromInventory(IInventoryItem Item);
}
}

View File

@@ -14,6 +14,7 @@ using Zennysoft.Game.Ma.Implementation;
using System.IO;
using SimpleInjector;
using static Zennysoft.Game.Ma.Implementation.GameLogic.State;
using System.Threading.Tasks;
[Meta(typeof(IAutoNode))]
public partial class Game : Node3D, IGame
@@ -266,7 +267,7 @@ public partial class Game : Node3D, IGame
GameEventDepot.OnTeleportEntered();
}
public void UseItem(InventoryItem item)
public async Task UseItem(IInventoryItem item)
{
if (item is ConsumableItem consumableItem)
{
@@ -338,9 +339,12 @@ public partial class Game : Node3D, IGame
if (throwableItem.ThrowableItemTag == ThrowableItemTag.WarpToExitIfFound)
_effectService.WarpToExit(Player);
}
await ToSignal(GetTree().CreateTimer(1f), "timeout");
GameRepo.RemoveItemFromInventory(item);
}
public void DropItem(InventoryItem item)
public void DropItem(IInventoryItem item)
{
var droppedScene = GD.Load<PackedScene>("res://src/items/dropped/DroppedItem.tscn");
var dropped = droppedScene.Instantiate<DroppedItem>();
@@ -349,7 +353,7 @@ public partial class Game : Node3D, IGame
dropped.Drop();
}
public void ThrowItem(InventoryItem item)
public void ThrowItem(IInventoryItem item)
{
var thrownScene = GD.Load<PackedScene>("res://src/items/thrown/ThrownItem.tscn");
var thrown = thrownScene.Instantiate<ThrownItem>();
@@ -359,11 +363,6 @@ public partial class Game : Node3D, IGame
thrown.Throw(_effectService);
}
public void AnnounceMessageOnInventoryScreen(string message)
{
InGameUI.InventoryMenu.ShowMessage(message);
}
public IDungeonFloor CurrentFloor => Map.CurrentFloor;
public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource resource)

View File

@@ -5,6 +5,8 @@ using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.SaveFileBuilder;
using Godot;
using System.Threading.Tasks;
using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Ma.Implementation;
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, IProvide<IPlayer>, IProvide<ISaveChunk<GameData>>, INode3D
@@ -19,18 +21,16 @@ public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvid
public IDungeonFloor CurrentFloor { get; }
public void UseItem(InventoryItem item);
public Task UseItem(IInventoryItem item);
public void DropItem(InventoryItem item);
public void DropItem(IInventoryItem item);
public void ThrowItem(InventoryItem item);
public void ThrowItem(IInventoryItem item);
public void ToggleInventory();
public void ToggleMinimap();
public void AnnounceMessageOnInventoryScreen(string message);
public void FloorExitReached();
public void NextFloorLoaded();

View File

@@ -4,6 +4,7 @@ using Chickensoft.Introspection;
using Godot;
using System.Linq;
using System.Threading.Tasks;
using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Ma.Implementation;
namespace Zennysoft.Game.Ma;
@@ -12,9 +13,11 @@ public interface IInventoryMenu : IControl
{
public Task RefreshInventoryScreen();
public Task ShowMessage(string message);
public Task DisplayMessage(string message);
event InventoryMenu.ClosedMenuEventHandler ClosedMenu;
public void RemoveItem(IInventoryItem item);
}
[Meta(typeof(IAutoNode))]
@@ -99,7 +102,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
SetProcessInput(false);
}
public async Task ShowMessage(string message)
public async Task DisplayMessage(string message)
{
SetProcessInput(false);
await HideUserActionPrompt();
@@ -229,6 +232,15 @@ public partial class InventoryMenu : Control, IInventoryMenu
}
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public async void RemoveItem(IInventoryItem item)
{
Player.Inventory.Remove(item);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
_currentIndex = 0;
}
private void PopulateItems()
{
PopulateInventory();
@@ -398,23 +410,13 @@ public partial class InventoryMenu : Control, IInventoryMenu
await EquipOrUnequipItem();
else
{
Game.UseItem(currentItem);
DestroyItem(currentItem);
EmitSignal(SignalName.ClosedMenu);
await Game.UseItem(currentItem);
//DestroyItem(currentItem);
}
RefreshUIAfterUserSelection();
}
private async void DestroyItem(InventoryItem item)
{
Player.Inventory.Remove(item);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
_currentIndex = 0;
}
private async void ThrowButtonPressed()
{
var currentItem = ItemSlots[_currentIndex].Item;

View File

@@ -2,12 +2,13 @@ using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma;
public interface IItemSlot : IHBoxContainer
{
public InventoryItem Item { get; set; }
public IInventoryItem Item { get; set; }
public void SetItemStyle();
@@ -41,7 +42,7 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
{
ItemName.Text = Item.ItemName;
//EquipBonus.Text = "...";
ItemTexture.Texture = Item.ItemStats.Texture;
ItemTexture.Texture = ((InventoryItem)Item).ItemStats.Texture;
Player.EquippedWeapon.Sync += EquippedWeapon_Sync;
Player.EquippedArmor.Sync += EquippedArmor_Sync;
Player.EquippedAccessory.Sync += EquippedAccessory_Sync;
@@ -113,5 +114,5 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
//EquipBonus.LabelSettings = EquippedItemFont;
}
public InventoryItem Item { get; set; } = default!;
public IInventoryItem Item { get; set; } = default!;
}

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using System.Collections.Generic;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma;
@@ -8,10 +9,10 @@ namespace Zennysoft.Game.Ma;
public partial class RescuedItemDatabase
{
[Save("rescued_item_list")]
public List<InventoryItem> Items { get; init; }
public List<IInventoryItem> Items { get; init; }
public RescuedItemDatabase()
{
Items = new List<InventoryItem>();
Items = new List<IInventoryItem>();
}
}

View File

@@ -5,16 +5,17 @@ using Chickensoft.Serialization;
using Godot;
using System.Collections.Generic;
using System.Linq;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma;
public interface IInventory : INode
{
public List<InventoryItem> Items { get; }
public List<IInventoryItem> Items { get; }
public bool TryAdd(InventoryItem inventoryItem);
public bool TryAdd(IInventoryItem inventoryItem);
public void Remove(InventoryItem inventoryItem);
public void Remove(IInventoryItem inventoryItem);
public void Sort();
}
@@ -33,9 +34,9 @@ public partial class Inventory : Node, IInventory
}
[Save("inventory_items")]
public List<InventoryItem> Items { get; private set; }
public List<IInventoryItem> Items { get; private set; }
public bool TryAdd(InventoryItem inventoryItem)
public bool TryAdd(IInventoryItem inventoryItem)
{
if (Items.Count >= _maxInventorySize)
return false;
@@ -44,15 +45,15 @@ public partial class Inventory : Node, IInventory
return true;
}
public void Remove(InventoryItem inventoryItem) => Items.Remove(inventoryItem);
public void Remove(IInventoryItem inventoryItem) => Items.Remove(inventoryItem);
public void Sort()
{
var equippedWeapon = Items.OfType<Weapon>().Where(x => x.IsEquipped);
var equippedArmor = Items.OfType<Armor>().Where(x => x.IsEquipped);
var equippedAccessory = Items.OfType<Accessory>().Where(x => x.IsEquipped);
var equippedItems = new List<InventoryItem>();
var equippedWeapon = Items.OfType<Weapon>().Where(x => x.IsEquipped).Cast<IInventoryItem>();
var equippedArmor = Items.OfType<Armor>().Where(x => x.IsEquipped).Cast<IInventoryItem>();
var equippedAccessory = Items.OfType<Accessory>().Where(x => x.IsEquipped).Cast<IInventoryItem>();
var equippedItems = new List<IInventoryItem>();
equippedItems.AddRange(equippedWeapon);
equippedItems.AddRange(equippedArmor);
equippedItems.AddRange(equippedAccessory);

View File

@@ -2,11 +2,12 @@
using Chickensoft.Serialization;
using Godot;
using System;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma;
[Meta]
public abstract partial class InventoryItem : Node3D
public abstract partial class InventoryItem : Node3D, IInventoryItem
{
[Save("inventory_item_id")]
public Guid ID => Guid.NewGuid();

View File

@@ -2,6 +2,7 @@
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma;
@@ -23,12 +24,12 @@ public partial class DroppedItem : RigidBody3D, IDroppedItem
[Node] private Area3D Pickup { get; set; } = default!;
public InventoryItem Item { get; set; }
public IInventoryItem Item { get; set; }
public void OnResolved()
{
ContactMonitor = true;
Sprite.Texture = Item.ItemStats.Texture;
Sprite.Texture = ((InventoryItem)Item).ItemStats.Texture;
}
public async void Drop()

View File

@@ -3,6 +3,7 @@ using Chickensoft.Introspection;
using Zennysoft.Game.Ma.src.items;
using Godot;
using Zennysoft.Game.Ma.Implementation;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Ma;
@@ -15,7 +16,7 @@ public partial class ThrownItem : RigidBody3D
[Dependency] public IGame Game => this.DependOn<IGame>();
public InventoryItem ItemThatIsThrown;
public IInventoryItem ItemThatIsThrown;
private EffectService _effectService;
@@ -25,7 +26,7 @@ public partial class ThrownItem : RigidBody3D
{
BodyEntered += ThrownItem_BodyEntered;
GlobalPosition = Player.CurrentPosition;
Sprite.Texture = ItemThatIsThrown.ItemStats.Texture;
Sprite.Texture = ((InventoryItem)ItemThatIsThrown).ItemStats.Texture;
AddCollisionExceptionWith((Node)Player);
}
@@ -39,7 +40,7 @@ public partial class ThrownItem : RigidBody3D
public void Throw(EffectService effectService)
{
_effectService = effectService;
ApplyCentralImpulse(-Player.CurrentBasis.Z.Normalized() * ItemThatIsThrown.ThrowSpeed);
ApplyCentralImpulse(-Player.CurrentBasis.Z.Normalized() * ((InventoryItem)ItemThatIsThrown).ThrowSpeed);
}
public void RescueItem()
@@ -100,6 +101,6 @@ public partial class ThrownItem : RigidBody3D
}
}
else
enemy.TakeDamage(ItemThatIsThrown.ThrowDamage);
enemy.TakeDamage(((InventoryItem)ItemThatIsThrown).ThrowDamage);
}
}

View File

@@ -292,14 +292,14 @@ public partial class Player : CharacterBody3D, IPlayer
{
Stats.SetMaximumHP(Stats.MaximumHP.Value + amountToRaise);
Stats.SetCurrentHP(Stats.MaximumHP.Value);
Game.AnnounceMessageOnInventoryScreen($"{amountToRaise}MAXHP Up.");
_gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXHP Up.");
}
public void HealHP(int amountToRestore)
{
Stats.SetCurrentHP(Stats.CurrentHP.Value + amountToRestore);
var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}";
Game.AnnounceMessageOnInventoryScreen($"{raiseString}HP Restored.");
_gameRepo.AnnounceMessageInInventory($"{raiseString}HP Restored.");
}
public void RaiseVT(int amountToRaise)
@@ -308,7 +308,7 @@ public partial class Player : CharacterBody3D, IPlayer
{
Stats.SetMaximumVT(Stats.MaximumVT.Value + amountToRaise);
Stats.SetCurrentVT(Stats.MaximumVT.Value);
Game.AnnounceMessageOnInventoryScreen($"{amountToRaise}MAXVT Up.");
_gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXVT Up.");
}
}
@@ -316,7 +316,7 @@ public partial class Player : CharacterBody3D, IPlayer
{
Stats.SetCurrentVT(Stats.CurrentVT.Value + amountToRestore);
var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}";
Game.AnnounceMessageOnInventoryScreen($"{raiseString}VT Restored.");
_gameRepo.AnnounceMessageInInventory($"{raiseString}VT Restored.");
}
public void ModifyBonusAttack(int amount)

View File

@@ -53,9 +53,17 @@ public partial class InGameUI : Control, IInGameUI
InGameUILogicBinding = InGameUILogic.Bind();
InGameUILogicBinding
.Handle((in InGameUILogic.Output.AnnounceMessage output) =>
.Handle((in InGameUILogic.Output.AnnounceMessageOnMainScreen output) =>
{
PlayerInfoUI.DisplayMessage(output.Message);
})
.Handle((in InGameUILogic.Output.AnnounceMessageInInventory output) =>
{
InventoryMenu.DisplayMessage(output.Message);
})
.Handle((in InGameUILogic.Output.RemoveItemFromInventory output) =>
{
InventoryMenu.RemoveItem(output.Item);
});
InGameUILogic.Start();