Rewrite and simplify Inventory Menu, various fixes for item effects

This commit is contained in:
2025-11-04 01:12:16 -08:00
parent 7b7fc910bd
commit a5846e08dc
49 changed files with 1070 additions and 826 deletions

View File

@@ -44,9 +44,9 @@ public partial class InGameUI : Control, IInGameUI
InGameUILogicBinding
.Handle((in InGameUILogic.Output.AnnounceMessageOnMainScreen output) => { InventoryMessageUI.DisplayMessage(output.Message); })
.Handle((in InGameUILogic.Output.AnnounceMessageInInventory output) => { InventoryMenu.DisplayMessage(output.Message); })
.Handle((in InGameUILogic.Output.RemoveItemFromInventory output) => { InventoryMenu.RemoveItem(output.Item); })
.Handle((in InGameUILogic.Output.ShowInventory _) => { InventoryMenu.RefreshInventoryScreen(); InventoryMenu.Show(); InventoryMenu.SetProcessInput(true); })
.Handle((in InGameUILogic.Output.AnnounceMessageInInventory output) => { })
.Handle((in InGameUILogic.Output.RemoveItemFromInventory output) => { })
.Handle((in InGameUILogic.Output.ShowInventory _) => { InventoryMenu.Show(); InventoryMenu.SetProcessInput(true); })
.Handle((in InGameUILogic.Output.HideInventory _) => { CloseInventory(); });
InGameUILogic.Start();

View File

@@ -0,0 +1,7 @@
using Chickensoft.GodotNodeInterfaces;
namespace Zennysoft.Game.Ma;
public interface IInventoryMenu : IControl
{
}

View File

@@ -0,0 +1 @@
uid://nhhwcce8ma3a

View File

@@ -1,18 +1,19 @@
using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces;
using Zennysoft.Game.Implementation;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public interface IItemSlot : IHBoxContainer
public interface IItemSlot : IButton
{
public InventoryItem Item { get; set; }
public AutoProp<InventoryItem> Item { get; }
public bool IsSelected { get; set; }
public void SetItemStyle();
public void SetSelectedItemStyle();
public void SetEquippedItemStyle();
public void SetEquippedSelectedItemStyle();
public event Action<InventoryItem> ItemPressed;
public event Action<IItemSlot> ItemEnterFocus;
public event Action<IItemSlot> ItemExitFocus;
}

View File

@@ -1,32 +1,52 @@
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public interface IInventoryMenu : IControl
{
public Task RefreshInventoryScreen();
public Task DisplayMessage(string message);
public void RemoveItem(InventoryItem item);
}
[Meta(typeof(IAutoNode))]
public partial class InventoryMenu : Control, IInventoryMenu
{
public override void _Notification(int what) => this.Notify(what);
[Node] public VBoxContainer ItemsPage { get; set; }
[Node] public Label ATKValue { get; set; }
[Node] public Label ATKBonusLabel { get; set; }
[Node] public Label DEFValue { get; set; }
[Node] public Label DEFBonusLabel { get; set; }
[Node] public Button UseButton { get; set; }
[Node] public Button ThrowButton { get; set; }
[Node] public Button DropButton { get; set; }
[Node] public Label ItemDescriptionTitle { get; set; }
[Node] public Label UseItemPrompt { get; set; }
[Node] public Label ItemEffectLabel { get; set; }
[Node] public Label BackArrow { get; set; } = default!;
[Node] public Label ForwardArrow { get; set; } = default!;
[Node] public ItemSlot ItemSlot1 { get; set; }
[Node] public ItemSlot ItemSlot2 { get; set; }
[Node] public ItemSlot ItemSlot3 { get; set; }
[Node] public ItemSlot ItemSlot4 { get; set; }
[Node] public ItemSlot ItemSlot5 { get; set; }
[Node] public ItemSlot ItemSlot6 { get; set; }
[Node] public ItemSlot ItemSlot7 { get; set; }
[Node] public ItemSlot ItemSlot8 { get; set; }
[Node] public ItemSlot ItemSlot9 { get; set; }
[Node] public ItemSlot ItemSlot10 { get; set; }
[Dependency] private IPlayer _player => this.DependOn<IPlayer>();
[Dependency] private IGame _game => this.DependOn<IGame>();
[Dependency] private IGameRepo _gameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
private List<IItemSlot> ItemSlots;
private InventoryPageNumber _currentPageNumber = InventoryPageNumber.FirstPage;
@@ -34,204 +54,208 @@ public partial class InventoryMenu : Control, IInventoryMenu
private const int _itemsPerPage = 10;
private int _currentIndex = 0;
private IItemSlot _currentlySelectedItem = null;
private IItemSlot[] ItemSlots => [.. ItemsPage.GetChildren().OfType<IItemSlot>()];
#region Control Nodes
[Node] public Label ATKValue { get; set; } = default!;
[Node] public Label ATKBonusLabel { get; set; } = default!;
[Node] public Label DEFValue { get; set; } = default!;
[Node] public Label DEFBonusLabel { get; set; } = default!;
[Node] public Label ItemDescriptionTitle { get; set; } = default!;
[Node] public Label ItemEffectLabel { get; set; } = default!;
// Item Menu
[Node] public Label BackArrow { get; set; } = default!;
[Node] public Label ForwardArrow { get; set; } = default!;
[Node] public Control ItemsPage { get; set; } = default!;
// User Prompt Menu
[Node] public Label UseItemPrompt { get; set; } = default!;
[Node] public Button UseButton { get; set; } = default!;
[Node] public Button ThrowButton { get; set; } = default!;
[Node] public Button DropButton { get; set; } = default!;
[Node] public AnimationPlayer AnimationPlayer { get; set; } = default!;
#endregion
public InventoryMenu()
public override void _EnterTree()
{
SetProcessInput(false);
SetProcess(false);
}
public void OnResolved()
{
ItemSlots = [ItemSlot1, ItemSlot2, ItemSlot3, ItemSlot4, ItemSlot5, ItemSlot6, ItemSlot7, ItemSlot8, ItemSlot9, ItemSlot10];
_currentlySelectedItem = ItemSlot1;
foreach (var item in ItemSlots)
{
item.ItemPressed += Item_Pressed;
item.ItemEnterFocus += Item_FocusEntered;
item.ItemExitFocus += Item_ItemExitFocus;
}
_player.AttackComponent.CurrentAttack.Sync += Attack_Sync;
_player.AttackComponent.MaximumAttack.Sync += Attack_Sync;
_player.DefenseComponent.CurrentDefense.Sync += Defense_Sync;
_player.DefenseComponent.MaximumDefense.Sync += Defense_Sync;
_player.EquipmentComponent.EquipmentChanged += EquipmentComponent_EquipmentChanged;
_player.Inventory.InventoryChanged += Inventory_InventoryChanged;
UseButton.Pressed += UseButtonPressed;
ThrowButton.Pressed += ThrowButtonPressed;
DropButton.Pressed += DropButtonPressed;
Player.AttackComponent.CurrentAttack.Sync += AttackSync;
Player.AttackComponent.MaximumAttack.Sync += AttackSync;
Player.EquipmentComponent.EquippedWeapon.Sync += BonusSync;
Player.EquipmentComponent.EquippedArmor.Sync += BonusSync;
Player.EquipmentComponent.EquippedAccessory.Sync += BonusSync;
Player.DefenseComponent.CurrentDefense.Sync += DefenseSync;
Player.DefenseComponent.MaximumDefense.Sync += DefenseSync;
VisibilityChanged += InventoryMenu2_VisibilityChanged;
}
public void OnExitTree()
{
Player.AttackComponent.CurrentAttack.Sync -= AttackSync;
Player.AttackComponent.MaximumAttack.Sync -= AttackSync;
Player.EquipmentComponent.EquippedWeapon.Sync -= BonusSync;
Player.EquipmentComponent.EquippedArmor.Sync -= BonusSync;
Player.EquipmentComponent.EquippedAccessory.Sync -= BonusSync;
Player.DefenseComponent.CurrentDefense.Sync -= DefenseSync;
Player.DefenseComponent.MaximumDefense.Sync -= DefenseSync;
}
public async Task DisplayMessage(string message)
{
SetProcessInput(false);
await HideUserActionPrompt();
await ShowInventoryInfo();
ItemEffectLabel.Text = message;
await ToSignal(GetTree().CreateTimer(1f), "timeout");
await RefreshInventoryScreen();
SetProcessInput(true);
}
private void AttackSync(int obj) => ATKValue.Text = $"{Player.AttackComponent.CurrentAttack.Value}/{Player.AttackComponent.MaximumAttack.Value}";
private void BonusSync(EquipableItem equip)
{
ATKBonusLabel.Text = $"{Player.EquipmentComponent.BonusAttack:+0;-#;\\.\\.\\.}";
DEFBonusLabel.Text = $"{Player.EquipmentComponent.BonusDefense:+0;-#;\\.\\.\\.}";
}
private void DefenseSync(int obj) => DEFValue.Text = $"{Player.DefenseComponent.CurrentDefense.Value}/{Player.DefenseComponent.MaximumDefense.Value}";
public async Task RefreshInventoryScreen()
{
await ClearItems();
PopulateInventory();
PopulatePlayerInfo();
await HideUserActionPrompt();
await ShowInventoryInfo();
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public override void _Input(InputEvent @event)
{
if (Visible && @event.IsActionPressed(GameInputs.Inventory))
{
if (UseButton.HasFocus() || DropButton.HasFocus() || ThrowButton.HasFocus())
{
HideUserActionPrompt();
ShowInventoryInfo();
Autoload.AudioManager.Play(SoundEffect.Cancel);
}
else
{
AcceptEvent();
Autoload.AudioManager.Play(SoundEffect.Cancel);
_gameRepo.CloseInventory();
}
}
if (ItemSlots.Length == 0 || UseButton.HasFocus() || DropButton.HasFocus() || ThrowButton.HasFocus())
if (!Visible)
return;
if (@event.IsActionPressed(GameInputs.MoveRight) && _currentPageNumber == InventoryPageNumber.FirstPage)
if (Input.IsActionJustPressed(GameInputs.Inventory) && !(UseButton.HasFocus() || DropButton.HasFocus() || ThrowButton.HasFocus()))
{
var inventory = Player.Inventory;
if (inventory.Items.Count > _itemsPerPage)
ChangeInventoryPage(InventoryPageNumber.SecondPage);
AcceptEvent();
_gameRepo.CloseInventory();
}
if (Input.IsActionJustPressed(GameInputs.UiCancel) && (UseButton.HasFocus() || DropButton.HasFocus() || ThrowButton.HasFocus()))
{
AcceptEvent();
HideUserActionPrompt();
}
else if (Input.IsActionJustPressed(GameInputs.UiCancel))
{
AcceptEvent();
_gameRepo.CloseInventory();
}
if (_currentPageNumber == InventoryPageNumber.FirstPage && _player.Inventory.Items.Count > 10 && Input.IsActionJustPressed(GameInputs.MoveRight))
{
_currentPageNumber = InventoryPageNumber.SecondPage;
Inventory_InventoryChanged();
_currentlySelectedItem = ItemSlot1;
ItemSlot1.GrabFocus();
}
else if (_currentPageNumber == InventoryPageNumber.SecondPage && Input.IsActionJustPressed(GameInputs.MoveLeft))
{
_currentPageNumber = InventoryPageNumber.FirstPage;
Inventory_InventoryChanged();
_currentlySelectedItem = ItemSlot1;
ItemSlot1.GrabFocus();
}
if (@event.IsActionPressed(GameInputs.MoveLeft) && _currentPageNumber == InventoryPageNumber.SecondPage)
ChangeInventoryPage(InventoryPageNumber.FirstPage);
if (@event.IsActionPressed(GameInputs.MoveDown))
if (Input.IsActionJustPressed(GameInputs.InventorySort))
{
var oldIndex = _currentIndex;
var newIndex = new[] { _currentIndex + 1, _itemsPerPage - 1, ItemSlots.Length - 1 }.Min();
if (oldIndex == newIndex)
return;
SetToUnselectedStyle(ItemSlots.ElementAt(oldIndex));
SetToSelectedStyle(ItemSlots.ElementAt(newIndex));
Autoload.AudioManager.Play(SoundEffect.MoveThroughOptions);
_currentIndex = newIndex;
}
if (@event.IsActionPressed(GameInputs.MoveUp))
{
var oldIndex = _currentIndex;
var newIndex = new[] { _currentIndex - 1, 0 }.Max();
if (oldIndex == newIndex)
return;
SetToUnselectedStyle(ItemSlots.ElementAt(oldIndex));
SetToSelectedStyle(ItemSlots.ElementAt(newIndex));
Autoload.AudioManager.Play(SoundEffect.MoveThroughOptions);
_currentIndex = newIndex;
}
if (@event.IsActionPressed(GameInputs.Attack))
{
DisplayUserActionPrompt();
Autoload.AudioManager.Play(SoundEffect.Select);
}
if (@event.IsActionPressed(GameInputs.InventorySort))
{
var inventory = Player.Inventory;
inventory.Sort(Player.EquipmentComponent.EquippedWeapon.Value, Player.EquipmentComponent.EquippedArmor.Value, Player.EquipmentComponent.EquippedAccessory.Value);
if (_currentIndex > inventory.Items.Count - 1)
_currentIndex = inventory.Items.Count - 1;
RefreshInventoryScreen();
_player.Inventory.Sort(_player.EquipmentComponent.EquippedWeapon.Value, _player.EquipmentComponent.EquippedArmor.Value, _player.EquipmentComponent.EquippedAccessory.Value);
Inventory_InventoryChanged();
foreach (var slot in ItemSlots)
slot.SetItemStyle();
Item_ItemExitFocus(_currentlySelectedItem);
_currentlySelectedItem = ItemSlot1;
_currentlySelectedItem.GrabFocus();
}
}
#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(InventoryItem item)
private void InventoryMenu2_VisibilityChanged()
{
Player.Inventory.Remove(item);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
_currentIndex = 0;
_currentlySelectedItem.GrabFocus();
}
private void PopulateItems()
private void Item_ItemExitFocus(IItemSlot itemSlot)
{
PopulateInventory();
PopulatePlayerInfo();
}
private async Task ClearItems()
{
foreach (var item in ItemSlots)
ItemsPage.RemoveChildEx(item);
ItemDescriptionTitle.Text = string.Empty;
ItemEffectLabel.Text = string.Empty;
itemSlot.IsSelected = false;
itemSlot.SetItemStyle();
}
private void PopulatePlayerInfo()
private void Item_FocusEntered(IItemSlot itemSlot)
{
if (ItemSlots.Length != 0)
if (itemSlot.Item.Value == null)
return;
ItemDescriptionTitle.Text = $"{itemSlot.Item.Value.ItemName}";
ItemEffectLabel.Text = $"{itemSlot.Item.Value.Description}";
_currentlySelectedItem = itemSlot;
itemSlot.IsSelected = true;
itemSlot.SetItemStyle();
}
private void Item_Pressed(InventoryItem item) => DisplayUserActionPrompt(item);
private async void Inventory_InventoryChanged()
{
foreach (var slot in ItemSlots)
{
var item = ItemSlots.ElementAt(_currentIndex).Item;
ItemDescriptionTitle.Text = $"{item.ItemName}";
ItemEffectLabel.Text = $"{item.Description}";
slot.Visible = false;
slot.SetItemStyle();
}
if (_currentPageNumber == InventoryPageNumber.SecondPage && _player.Inventory.Items.Count <= 10)
{
_currentPageNumber = InventoryPageNumber.FirstPage;
var elementToSelect = _player.Inventory.Items.IndexOf(_player.Inventory.Items.Last());
_currentlySelectedItem = ItemSlots.ElementAt(elementToSelect);
_currentlySelectedItem.GrabFocus();
}
var itemsToDisplay = new List<InventoryItem>();
if (_currentPageNumber == InventoryPageNumber.FirstPage)
itemsToDisplay = [.. _player.Inventory.Items.Take(_itemsPerPage)];
else
itemsToDisplay = [.. _player.Inventory.Items.TakeLast(_player.Inventory.Items.Count - _itemsPerPage)];
for (var i = 0; i < itemsToDisplay.Count; i++)
{
ItemSlots[i].Item.OnNext(itemsToDisplay[i]);
ItemSlots[i].Visible = true;
}
SetPageIndicators();
if (!_player.Inventory.Items.Contains(_currentlySelectedItem.Item.Value))
{
_currentlySelectedItem.Item.OnNext(null);
var elementToSelect = Mathf.Max(0, ItemSlots.IndexOf(_currentlySelectedItem) - 1);
_currentlySelectedItem = ItemSlots.ElementAt(elementToSelect);
_currentlySelectedItem.GrabFocus();
}
}
private void DisplayUserActionPrompt()
private void SetPageIndicators()
{
if (_player.Inventory.Items.Count > 10 && _currentPageNumber == InventoryPageNumber.FirstPage)
{
ForwardArrow.Text = "►";
BackArrow.Text = "";
}
else if (_player.Inventory.Items.Count > 10 && _currentPageNumber == InventoryPageNumber.SecondPage)
{
ForwardArrow.Text = "";
BackArrow.Text = "◄";
}
}
private void Attack_Sync(int obj) => ATKValue.Text = $"{_player.AttackComponent.CurrentAttack.Value}/{_player.AttackComponent.MaximumAttack.Value}";
private void Defense_Sync(int obj) => DEFValue.Text = $"{_player.DefenseComponent.CurrentDefense.Value}/{_player.DefenseComponent.MaximumDefense.Value}";
private void EquipmentComponent_EquipmentChanged(EquipableItem equipableItem)
{
ATKBonusLabel.Text = $"{_player.EquipmentComponent.BonusAttack:+0;-#;\\.\\.\\.}";
DEFBonusLabel.Text = $"{_player.EquipmentComponent.BonusDefense:+0;-#;\\.\\.\\.}";
}
private async void UseButtonPressed()
{
UseButton.Disabled = true;
if (_currentlySelectedItem.Item.Value is EquipableItem equipable)
await EquipOrUnequipItem(equipable);
else
await _game.UseItem(_currentlySelectedItem.Item.Value);
UseButton.Disabled = false;
HideUserActionPrompt();
await ShowInventoryInfo();
await ToSignal(GetTree().CreateTimer(1f), "timeout");
}
private async void ThrowButtonPressed()
{
_game.ThrowItem(_currentlySelectedItem.Item.Value);
_player.Inventory.Remove(_currentlySelectedItem.Item.Value);
HideUserActionPrompt();
await ShowInventoryInfo();
_gameRepo.CloseInventory();
}
private async void DropButtonPressed()
{
_game.DropItem(_currentlySelectedItem.Item.Value);
_player.Inventory.Remove(_currentlySelectedItem.Item.Value);
HideUserActionPrompt();
await ShowInventoryInfo();
_gameRepo.CloseInventory();
}
private void DisplayUserActionPrompt(InventoryItem item)
{
ItemDescriptionTitle.Hide();
ItemEffectLabel.Hide();
@@ -240,11 +264,9 @@ public partial class InventoryMenu : Control, IInventoryMenu
ThrowButton.Show();
DropButton.Show();
var currentItem = ItemSlots.ElementAt(_currentIndex).Item;
if (currentItem is EquipableItem equipable)
if (item is EquipableItem equipable)
{
var isItemEquipped = Player.EquipmentComponent.IsItemEquipped(equipable);
var isItemEquipped = _player.EquipmentComponent.IsItemEquipped(equipable);
UseButton.Text = isItemEquipped ? "Unequip" : "Equip";
ThrowButton.Disabled = isItemEquipped;
ThrowButton.FocusMode = isItemEquipped ? FocusModeEnum.None : FocusModeEnum.All;
@@ -259,7 +281,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
UseButton.CallDeferred(MethodName.GrabFocus);
}
private async Task HideUserActionPrompt()
private void HideUserActionPrompt()
{
UseItemPrompt.Hide();
UseButton.Hide();
@@ -268,6 +290,23 @@ public partial class InventoryMenu : Control, IInventoryMenu
UseButton.ReleaseFocus();
ThrowButton.ReleaseFocus();
DropButton.ReleaseFocus();
_currentlySelectedItem.GrabFocus();
}
private async Task EquipOrUnequipItem(EquipableItem equipable)
{
if (_player.EquipmentComponent.IsItemEquipped(equipable))
{
ItemEffectLabel.Text = $"{equipable.GetType().Name} unequipped.";
_player.Unequip(equipable);
}
else
{
var itemSlot = _currentlySelectedItem;
ItemEffectLabel.Text = $"{equipable.GetType().Name} equipped.";
_player.Equip(equipable);
_currentlySelectedItem = itemSlot;
}
}
private async Task ShowInventoryInfo()
@@ -276,149 +315,6 @@ public partial class InventoryMenu : Control, IInventoryMenu
ItemEffectLabel.Show();
}
private async Task ChangeInventoryPage(InventoryPageNumber pageToChangeTo)
{
await ClearItems();
await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
_currentIndex = 0;
_currentPageNumber = pageToChangeTo;
await RefreshInventoryScreen();
Autoload.AudioManager.Play(SoundEffect.MoveThroughOptions);
}
private async void PopulateInventory()
{
var inventory = Player.Inventory;
var numberOfItemsToDisplay = _currentPageNumber == InventoryPageNumber.FirstPage ? Mathf.Min(inventory.Items.Count, _itemsPerPage) : Mathf.Min(inventory.Items.Count - _itemsPerPage, _itemsPerPage);
var indexToStart = _currentPageNumber == InventoryPageNumber.FirstPage ? 0 : _itemsPerPage;
ForwardArrow.Text = "";
BackArrow.Text = "";
if (_currentPageNumber == InventoryPageNumber.FirstPage && inventory.Items.Count > _itemsPerPage)
{
ForwardArrow.Text = "►";
BackArrow.Text = "";
}
if (_currentPageNumber == InventoryPageNumber.SecondPage)
{
ForwardArrow.Text = "";
BackArrow.Text = "◄";
}
for (var i = 0; i < numberOfItemsToDisplay; i++)
{
var item = inventory.Items.ElementAt(i + indexToStart);
var itemScene = GD.Load<PackedScene>(ITEM_SLOT_SCENE);
var itemSlot = itemScene.Instantiate<IItemSlot>();
itemSlot.Item = item;
ItemsPage.AddChildEx(itemSlot);
if (Player.EquipmentComponent.IsItemEquipped(itemSlot.Item))
itemSlot.SetEquippedItemStyle();
}
if (ItemSlots.Length != 0)
{
ItemSlots.ElementAt(_currentIndex).SetSelectedItemStyle();
if (Player.EquipmentComponent.IsItemEquipped(ItemSlots.ElementAt(_currentIndex).Item))
ItemSlots.ElementAt(_currentIndex).SetEquippedSelectedItemStyle();
}
}
private async Task SetToUnselectedStyle(IItemSlot itemSlot)
{
await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
itemSlot.SetItemStyle();
if (Player.EquipmentComponent.IsItemEquipped(itemSlot.Item))
itemSlot.SetEquippedItemStyle();
}
private async Task SetToSelectedStyle(IItemSlot itemSlot)
{
await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
itemSlot.SetSelectedItemStyle();
ItemDescriptionTitle.Text = $"{itemSlot.Item.ItemName}";
ItemEffectLabel.Text = $"{itemSlot.Item.Description}";
}
private async Task EquipOrUnequipItem()
{
var itemSlot = ItemSlots[_currentIndex];
if (itemSlot.Item is not EquipableItem)
return;
var equippableItem = (EquipableItem)itemSlot.Item;
if (Player.EquipmentComponent.IsItemEquipped(equippableItem))
{
ItemEffectLabel.Text = $"{equippableItem.GetType().Name} unequipped.";
Player.EquipmentComponent.Unequip(equippableItem);
itemSlot.SetSelectedItemStyle();
if (itemSlot.Item.ItemTag == ItemTag.BreaksOnChange)
Player.Inventory.Remove(equippableItem);
}
else
{
ItemEffectLabel.Text = $"{equippableItem.GetType().Name} equipped.";
Player.Equip(equippableItem);
itemSlot.SetEquippedSelectedItemStyle();
}
RefreshUIAfterUserSelection();
}
private async void UseButtonPressed()
{
UseButton.Disabled = true;
var currentItem = ItemSlots[_currentIndex].Item;
if (currentItem is EquipableItem)
await EquipOrUnequipItem();
else
await Game.UseItem(currentItem);
RefreshUIAfterUserSelection();
UseButton.Disabled = false;
}
private async void ThrowButtonPressed()
{
var currentItem = ItemSlots[_currentIndex].Item;
Game.ThrowItem(currentItem);
Player.Inventory.Remove(currentItem);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
_currentIndex = 0;
_gameRepo.CloseInventory();
}
private async void DropButtonPressed()
{
var currentItem = ItemSlots[_currentIndex].Item;
Game.DropItem(currentItem);
Player.Inventory.Remove(currentItem);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
_currentIndex = 0;
_gameRepo.CloseInventory();
}
private async void RefreshUIAfterUserSelection()
{
SetProcessInput(false);
await HideUserActionPrompt();
await ShowInventoryInfo();
await RefreshInventoryScreen();
await ToSignal(GetTree().CreateTimer(1f), "timeout");
SetProcessInput(true);
}
private enum InventoryPageNumber
{
FirstPage,

View File

@@ -1 +1 @@
uid://cmtet15hi5oiy
uid://bi1xopts68paw

View File

@@ -1,14 +1,14 @@
[gd_scene load_steps=30 format=3 uid="uid://dlj8qdg1c5048"]
[gd_scene load_steps=27 format=3 uid="uid://dlj8qdg1c5048"]
[ext_resource type="Script" uid="uid://cmtet15hi5oiy" path="res://src/ui/inventory_menu/InventoryMenu.cs" id="1_l64wl"]
[ext_resource type="Script" uid="uid://bi1xopts68paw" path="res://src/ui/inventory_menu/InventoryMenu.cs" id="1_b6rkr"]
[ext_resource type="Shader" uid="uid://cnphwvmr05hp1" path="res://src/ui/inventory_menu/InventoryMenu.gdshader" id="2_0fvsh"]
[ext_resource type="FontFile" uid="uid://cm8j5vcdop5x0" path="res://src/ui/fonts/Mrs-Eaves-OT-Roman_31443.ttf" id="3_lm4o1"]
[ext_resource type="PackedScene" uid="uid://c005nd0m2eim" path="res://src/ui/inventory_menu/ItemSlot.tscn" id="4_aiji3"]
[ext_resource type="FontFile" uid="uid://cb41qqmxqurj8" path="res://src/ui/fonts/FT88-Bold.ttf" id="4_rg5yb"]
[ext_resource type="FontFile" uid="uid://dit3vylt7hmmx" path="res://src/ui/fonts/FT88-Regular.ttf" id="5_2qnnx"]
[ext_resource type="LabelSettings" uid="uid://ca1q6yu8blwxf" path="res://src/ui/label_settings/InventoryMainTextBold.tres" id="6_tmdno"]
[ext_resource type="LabelSettings" uid="uid://cuuo43x72xcsc" path="res://src/ui/label_settings/MainTextBold.tres" id="7_vyrxm"]
[ext_resource type="Theme" uid="uid://daxuhpmyxwxck" path="res://src/ui/inventory_menu/InventoryDialogueSelectionStyle.tres" id="8_khyvo"]
[ext_resource type="Shortcut" uid="uid://dumkrjur22k2a" path="res://src/ui/ButtonShortcut.tres" id="9_b6rkr"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_i55tv"]
shader = ExtResource("2_0fvsh")
@@ -72,67 +72,6 @@ font_color = Color(0.737255, 0.705882, 0.690196, 1)
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ct6ql"]
[sub_resource type="Animation" id="Animation_dg155"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("InventoryInfo/HBoxContainer/PlayerInfo/HBoxContainer/VBoxContainer/ItemEffectLabel:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("InventoryInfo/HBoxContainer/PlayerInfo/HBoxContainer/VBoxContainer/ItemEffectLabel:text")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [""]
}
[sub_resource type="Animation" id="Animation_7by7u"]
resource_name = "status_up"
length = 2.5
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("InventoryInfo/HBoxContainer/PlayerInfo/HBoxContainer/VBoxContainer/ItemEffectLabel:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 2.5),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [true, false]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("InventoryInfo/HBoxContainer/PlayerInfo/HBoxContainer/VBoxContainer/ItemEffectLabel:text")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(2.5),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [""]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_eivo2"]
_data = {
&"RESET": SubResource("Animation_dg155"),
&"status_up": SubResource("Animation_7by7u")
}
[node name="InventoryMenu" type="Control"]
custom_minimum_size = Vector2(1440, 1080)
layout_mode = 3
@@ -141,7 +80,8 @@ offset_right = 1440.0
offset_bottom = 1080.0
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1_l64wl")
mouse_filter = 2
script = ExtResource("1_b6rkr")
[node name="BG" type="TextureRect" parent="."]
material = SubResource("ShaderMaterial_i55tv")
@@ -151,6 +91,7 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
texture = SubResource("PlaceholderTexture2D_3ynpe")
expand_mode = 2
@@ -206,7 +147,6 @@ horizontal_alignment = 2
[node name="ItemsPage" type="VBoxContainer" parent="InventoryInfo/HBoxContainer/ItemInfo"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 0
theme_override_constants/separation = 15
alignment = 1
@@ -214,8 +154,59 @@ alignment = 1
custom_minimum_size = Vector2(0, 14)
layout_mode = 2
[node name="ItemSlot1" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot2" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot3" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot4" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot5" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot6" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot7" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot8" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot9" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="ItemSlot10" parent="InventoryInfo/HBoxContainer/ItemInfo/ItemsPage" instance=ExtResource("4_aiji3")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="PlayerInfo" type="VBoxContainer" parent="InventoryInfo/HBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/separation = 20
[node name="VTBox" type="HBoxContainer" parent="InventoryInfo/HBoxContainer/PlayerInfo"]
@@ -382,7 +373,6 @@ theme_override_styles/disabled = SubResource("StyleBoxEmpty_fu7o2")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_nkvce")
theme_override_styles/normal = SubResource("StyleBoxEmpty_545ij")
button_mask = 0
shortcut = ExtResource("9_b6rkr")
text = "Use"
alignment = 0
@@ -407,7 +397,6 @@ theme_override_styles/disabled = SubResource("StyleBoxEmpty_ascpt")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_abpb1")
theme_override_styles/normal = SubResource("StyleBoxEmpty_545ij")
button_mask = 0
shortcut = ExtResource("9_b6rkr")
text = "Throw"
alignment = 0
@@ -432,12 +421,5 @@ theme_override_styles/disabled = SubResource("StyleBoxEmpty_uerb4")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_lvcf8")
theme_override_styles/normal = SubResource("StyleBoxEmpty_ct6ql")
button_mask = 0
shortcut = ExtResource("9_b6rkr")
text = "Drop"
alignment = 0
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
unique_name_in_owner = true
libraries = {
&"": SubResource("AnimationLibrary_eivo2")
}

View File

@@ -1,18 +1,19 @@
using Chickensoft.AutoInject;
using Chickensoft.Collections;
using Chickensoft.Introspection;
using Godot;
using System;
using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
[Meta(typeof(IAutoNode))]
public partial class ItemSlot : HBoxContainer, IItemSlot
public partial class ItemSlot : Button, IItemSlot
{
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Dependency] private IPlayer _player => this.DependOn<IPlayer>();
[Node] public TextureRect ItemTexture { get; set; } = default!;
@@ -20,6 +21,8 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
[Node] public Label ItemCount { get; set; } = default!;
public AutoProp<InventoryItem> Item { get; } = new AutoProp<InventoryItem>(default);
private static LabelSettings ItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextBold.tres");
private static LabelSettings SelectedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontItalicized.tres");
@@ -28,53 +31,89 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
private static LabelSettings SelectedEquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontSelectedEquipped.tres");
public void OnReady()
{
ItemName.Text = Item.ItemName;
ItemTexture.Texture = Item.GetTexture();
Player.EquipmentComponent.EquippedWeapon.Sync += EquipableItem_Sync;
Player.EquipmentComponent.EquippedArmor.Sync += EquipableItem_Sync;
Player.EquipmentComponent.EquippedAccessory.Sync += EquipableItem_Sync;
public event Action<InventoryItem> ItemPressed;
public event Action<IItemSlot> ItemEnterFocus;
public event Action<IItemSlot> ItemExitFocus;
if (Item is IStackable stackableItem)
{
ItemCount.Text = $"{stackableItem.Count:D2}";
ItemCount.Visible = true;
}
}
public bool IsSelected { get; set; } = false;
private void EquipableItem_Sync(EquipableItem obj)
public void OnResolved()
{
if (Item is EquipableItem equipableItem && equipableItem == obj)
{
SetEquippedSelectedItemStyle();
}
if (Item is EquipableItem unequippedItem && unequippedItem != obj)
{
SetItemStyle();
}
Item.Changed += Item_Changed;
_player.EquipmentComponent.EquippedWeapon.Sync += EquipableItem_Sync;
_player.EquipmentComponent.EquippedArmor.Sync += EquipableItem_Sync;
_player.EquipmentComponent.EquippedAccessory.Sync += EquipableItem_Sync;
FocusEntered += ItemSlot_FocusEntered;
FocusExited += ItemSlot_FocusExited;
Pressed += ItemSlot_Pressed;
}
public void SetItemStyle()
{
ItemName.LabelSettings = ItemFont;
if (_player.EquipmentComponent.IsItemEquipped(Item.Value) && IsSelected)
SetEquippedSelectedItemStyle();
else if (_player.EquipmentComponent.IsItemEquipped(Item.Value))
SetEquippedItemStyle();
else if (IsSelected)
SetSelectedItemStyle();
else
SetToUnselectedStyle();
}
public void SetSelectedItemStyle()
public void SetToUnselectedStyle()
{
if (Player.EquipmentComponent.IsItemEquipped(Item))
SetItemFont();
if (_player.EquipmentComponent.IsItemEquipped(Item.Value))
SetEquippedItemStyle();
}
private void EquipableItem_Sync(EquipableItem item) => SetItemStyle();
private void ItemSlot_Pressed()
{
if (Item.Value == null)
return;
ItemPressed?.Invoke(Item.Value);
}
private void ItemSlot_FocusExited() => ItemExitFocus?.Invoke(this);
private void ItemSlot_FocusEntered() => ItemEnterFocus?.Invoke(this);
private void Item_Changed(InventoryItem obj)
{
if (obj == null)
return;
ItemName.Text = obj.ItemName;
ItemTexture.Texture = obj.GetTexture();
if (obj is IStackable stackableItem)
{
ItemCount.Text = $"{stackableItem.Count:D2}";
ItemCount.Visible = true;
}
else
{
ItemCount.Text = string.Empty;
ItemCount.Visible = false;
}
}
private void SetToSelectedStyle() => SetSelectedItemStyle();
private void SetSelectedItemStyle()
{
if (_player.EquipmentComponent.IsItemEquipped(Item.Value))
ItemName.LabelSettings = SelectedEquippedItemFont;
else
ItemName.LabelSettings = SelectedItemFont;
}
public void SetEquippedItemStyle()
{
ItemName.LabelSettings = EquippedItemFont;
}
private void SetItemFont() => ItemName.LabelSettings = ItemFont;
public void SetEquippedSelectedItemStyle()
{
ItemName.LabelSettings = SelectedEquippedItemFont;
}
private void SetEquippedItemStyle() => ItemName.LabelSettings = EquippedItemFont;
public InventoryItem Item { get; set; } = default!;
private void SetEquippedSelectedItemStyle() => ItemName.LabelSettings = SelectedEquippedItemFont;
}

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3 uid="uid://c005nd0m2eim"]
[gd_scene load_steps=8 format=3 uid="uid://c005nd0m2eim"]
[ext_resource type="Script" uid="uid://cglxk7v8hpesn" path="res://src/ui/inventory_menu/ItemSlot.cs" id="1_yttxt"]
[ext_resource type="Texture2D" uid="uid://0r1dws4ajhdx" path="res://src/items/accessory/textures/MASK 01.PNG" id="2_7kdbd"]
@@ -6,42 +6,65 @@
[ext_resource type="FontFile" uid="uid://bohbd123672ea" path="res://src/ui/fonts/FT88-Italic.ttf" id="4_vcxwm"]
[ext_resource type="LabelSettings" uid="uid://bl5xpqyq8vjtv" path="res://src/ui/inventory_menu/InventoryLabelSettings.tres" id="5_a7hko"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_lt1pw"]
[sub_resource type="LabelSettings" id="LabelSettings_lgjx0"]
font = ExtResource("4_vcxwm")
font_size = 30
font_color = Color(0, 0.682353, 0.937255, 1)
[node name="ItemSlot" type="HBoxContainer"]
[node name="ItemSlot" type="Button"]
custom_minimum_size = Vector2(100, 60)
offset_right = 1748.0
offset_bottom = 85.0
anchors_preset = -1
anchor_right = 0.885
anchor_bottom = 0.093
offset_right = 0.799927
offset_bottom = -0.440002
theme_override_styles/focus = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/disabled_mirrored = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/disabled = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/hover_pressed_mirrored = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/hover_mirrored = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/hover = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/pressed_mirrored = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/normal_mirrored = SubResource("StyleBoxEmpty_lt1pw")
theme_override_styles/normal = SubResource("StyleBoxEmpty_lt1pw")
flat = true
alignment = 0
script = ExtResource("1_yttxt")
[node name="ReferenceRect" type="ReferenceRect" parent="."]
[node name="HBox" type="HBoxContainer" parent="."]
custom_minimum_size = Vector2(100, 60)
layout_mode = 0
offset_right = 1700.0
offset_bottom = 100.0
[node name="ReferenceRect" type="ReferenceRect" parent="HBox"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
[node name="ItemTexture" type="TextureRect" parent="."]
[node name="ItemTexture" type="TextureRect" parent="HBox"]
unique_name_in_owner = true
layout_mode = 2
texture = ExtResource("2_7kdbd")
expand_mode = 2
[node name="ReferenceRect2" type="ReferenceRect" parent="."]
[node name="ReferenceRect2" type="ReferenceRect" parent="HBox"]
custom_minimum_size = Vector2(40, 0)
layout_mode = 2
[node name="ItemName" type="Label" parent="."]
[node name="ItemName" type="Label" parent="HBox"]
unique_name_in_owner = true
custom_minimum_size = Vector2(550, 50)
layout_mode = 2
text = "Mask of the Goddess of Destruction"
label_settings = SubResource("LabelSettings_lgjx0")
vertical_alignment = 1
autowrap_mode = 2
script = ExtResource("3_xlgl0")
[node name="ItemCount" type="Label" parent="."]
[node name="ItemCount" type="Label" parent="HBox"]
unique_name_in_owner = true
visible = false
layout_mode = 2

View File

@@ -1,10 +1,8 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
using System;
using System.Collections.Immutable;
using System.Linq;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;