Files
GameJamDungeon/src/inventory_menu/InventoryMenu.cs

354 lines
11 KiB
C#

using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
using System.Linq;
using System.Threading.Tasks;
public interface IInventoryMenu : IControl
{
public void PopulateItems();
public void ClearItems();
}
[Meta(typeof(IAutoNode))]
public partial class InventoryMenu : Control, IInventoryMenu
{
public override void _Notification(int what) => this.Notify(what);
[Dependency]
public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency]
public IGame Game => this.DependOn<IGame>();
// Player Info
[Node] public Label FloorLabel { get; set; } = default!;
[Node] public Label CurrentLevelLabel { get; set; } = default!;
[Node] public Label EXPValue { get; set; } = default!;
[Node] public Label HPValue { get; set; } = default!;
[Node] public Label HPBonusLabel { get; set; } = default!;
[Node] public Label VTValue { get; set; } = default!;
[Node] public Label VTBonusLabel { get; set; } = default!;
[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!;
public void OnReady()
{
UseButton.Pressed += UseButtonPressed;
ThrowButton.Pressed += ThrowButtonPressed;
DropButton.Pressed += DropButtonPressed;
}
private InventoryPageNumber _currentPageNumber = InventoryPageNumber.FirstPage;
private string ITEM_SLOT_SCENE = "res://src/inventory_menu/ItemSlot.tscn";
private const int _itemsPerPage = 10;
private int _currentIndex = 0;
private IItemSlot[] ItemSlots => ItemsPage.GetChildren().OfType<IItemSlot>().ToArray();
public void PopulateItems()
{
PopulateInventory();
PopulatePlayerInfo();
}
public void ClearItems()
{
foreach (var item in ItemSlots)
ItemsPage.RemoveChildEx(item);
HideUserActionPrompt();
}
public void PopulatePlayerInfo()
{
FloorLabel.Text = $"Level {GameRepo.CurrentFloor:D2}";
var currentLevel = GameRepo.PlayerStatInfo.Value.CurrentLevel;
CurrentLevelLabel.Text = $"Level {currentLevel:D2}";
var currentHP = GameRepo.PlayerStatInfo.Value.CurrentHP;
var maxHP = GameRepo.PlayerStatInfo.Value.MaximumHP;
HPValue.Text = $"{currentHP}/{maxHP}";
var currentVT = GameRepo.PlayerStatInfo.Value.CurrentVT;
var maxVT = GameRepo.PlayerStatInfo.Value.MaximumVT;
VTValue.Text = $"{currentVT}/{maxVT}";
var currentAttack = GameRepo.PlayerStatInfo.Value.CurrentAttack;
var maxAttack = GameRepo.PlayerStatInfo.Value.MaxAttack;
ATKValue.Text = $"{currentAttack}/{maxAttack}";
var currentDefense = GameRepo.PlayerStatInfo.Value.CurrentDefense;
var maxDefense = GameRepo.PlayerStatInfo.Value.MaxDefense;
DEFValue.Text = $"{currentDefense}/{maxDefense}";
var atkBonus = GameRepo.PlayerStatInfo.Value.BonusAttack;
var defBonus = GameRepo.PlayerStatInfo.Value.BonusDefense;
ATKBonusLabel.Text = atkBonus != 0 ? $"{atkBonus:+0;-#}" : "...";
DEFBonusLabel.Text = defBonus != 0 ? $"{defBonus:+0;-#}" : "...";
// TODO: Change font style when EXP Bonus effect is active
var currentExp = GameRepo.PlayerStatInfo.Value.CurrentEXP;
var expToNextLevel = GameRepo.PlayerStatInfo.Value.EXPToNextLevel;
EXPValue.Text = $"{currentExp}/{expToNextLevel}";
if (ItemSlots.Any())
{
var item = ItemSlots.ElementAt(_currentIndex).Item;
ItemDescriptionTitle.Text = $"{item.Info.Name}";
ItemEffectLabel.Text = $"{item.Info.Description}";
}
}
public override void _UnhandledInput(InputEvent @event)
{
if (ItemSlots.Length == 0)
return;
var inventory = GameRepo.InventoryItems.Value;
if (@event.IsActionPressed(GameInputs.UiRight) && _currentPageNumber == InventoryPageNumber.FirstPage && inventory.Count > _itemsPerPage)
ChangeInventoryPage(InventoryPageNumber.SecondPage);
if (@event.IsActionPressed(GameInputs.UiLeft) && _currentPageNumber == InventoryPageNumber.SecondPage)
ChangeInventoryPage(InventoryPageNumber.FirstPage);
if (@event.IsActionPressed(GameInputs.UiDown))
{
SetToUnselectedStyle(ItemSlots.ElementAt(_currentIndex));
_currentIndex = new[] { _currentIndex + 1, _itemsPerPage - 1, ItemSlots.Length - 1 }.Min();
SetToSelectedStyle(ItemSlots.ElementAt(_currentIndex));
}
if (@event.IsActionPressed(GameInputs.UiUp))
{
SetToUnselectedStyle(ItemSlots.ElementAt(_currentIndex));
_currentIndex = new[] { _currentIndex - 1, 0 }.Max();
SetToSelectedStyle(ItemSlots.ElementAt(_currentIndex));
}
if (@event.IsActionPressed(GameInputs.UiAccept))
{
if (ItemSlots.Length == 0 || GetViewport().GuiGetFocusOwner() is Button)
return;
DisplayUserActionPrompt();
}
}
private void DisplayUserActionPrompt()
{
ItemDescriptionTitle.Hide();
ItemEffectLabel.Hide();
UseItemPrompt.Show();
UseButton.Show();
ThrowButton.Show();
DropButton.Show();
var currentItem = ItemSlots.ElementAt(_currentIndex).Item;
if (currentItem is IEquipable equipable)
{
var isEquipped = GameRepo.IsItemEquipped(equipable);
UseButton.Text = isEquipped ? "Unequip" : "Equip";
ThrowButton.Disabled = isEquipped ? true : false;
ThrowButton.FocusMode = isEquipped ? FocusModeEnum.None : FocusModeEnum.All;
DropButton.Disabled = isEquipped ? true : false;
DropButton.FocusMode = isEquipped ? FocusModeEnum.None : FocusModeEnum.All;
}
else
{
UseButton.Text = "Use";
}
UseButton.GrabFocus();
}
private async Task HideUserActionPrompt()
{
ItemDescriptionTitle.Show();
ItemEffectLabel.Show();
UseItemPrompt.Hide();
UseButton.Hide();
ThrowButton.Hide();
DropButton.Hide();
}
private async void ChangeInventoryPage(InventoryPageNumber pageToChangeTo)
{
ClearItems();
await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
_currentIndex = 0;
_currentPageNumber = pageToChangeTo;
PopulateItems();
}
private void PopulateInventory()
{
var inventory = GameRepo.InventoryItems.Value;
var numberOfItemsToDisplay = _currentPageNumber == InventoryPageNumber.FirstPage ? Mathf.Min(inventory.Count, _itemsPerPage) : Mathf.Min(inventory.Count - _itemsPerPage, _itemsPerPage);
var indexToStart = _currentPageNumber == InventoryPageNumber.FirstPage ? 0 : _itemsPerPage - 1;
ForwardArrow.Text = "";
BackArrow.Text = "";
if (_currentPageNumber == InventoryPageNumber.FirstPage && inventory.Count > _itemsPerPage)
{
ForwardArrow.Text = "►";
BackArrow.Text = "";
}
if (_currentPageNumber == InventoryPageNumber.SecondPage)
{
ForwardArrow.Text = "";
BackArrow.Text = "◄";
}
for (var i = 0; i < numberOfItemsToDisplay; i++)
{
var item = inventory.ElementAt(i + indexToStart);
var itemScene = GD.Load<PackedScene>(ITEM_SLOT_SCENE);
var itemSlot = itemScene.Instantiate<IItemSlot>();
itemSlot.Item = item;
ItemsPage.AddChildEx(itemSlot);
if (itemSlot.Item is IEquipable equipable && GameRepo.IsItemEquipped(equipable))
itemSlot.SetEquippedItemStyle();
}
if (ItemSlots.Any())
{
ItemSlots.ElementAt(_currentIndex).SetSelectedItemStyle();
if (ItemSlots.ElementAt(_currentIndex).Item is IEquipable equipable && GameRepo.IsItemEquipped(equipable))
ItemSlots.ElementAt(_currentIndex).SetEquippedSelectedItemStyle();
}
}
private async void SetToUnselectedStyle(IItemSlot itemSlot)
{
await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
itemSlot.SetItemStyle();
if (itemSlot.Item is IEquipable equipable && GameRepo.IsItemEquipped(equipable))
itemSlot.SetEquippedItemStyle();
}
private async void SetToSelectedStyle(IItemSlot itemSlot)
{
await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
itemSlot.SetSelectedItemStyle();
if (itemSlot.Item is IEquipable newEquipable && GameRepo.IsItemEquipped(newEquipable))
itemSlot.SetEquippedSelectedItemStyle();
ItemDescriptionTitle.Text = $"{itemSlot.Item.Info.Name}";
ItemEffectLabel.Text = $"{itemSlot.Item.Info.Description}";
}
private async void EquipOrUnequipItem()
{
var itemSlot = ItemSlots[_currentIndex];
await ToSignal(GetTree().CreateTimer(0.2f), "timeout");
if (itemSlot.Item is IEquipable equipableItem)
{
if (GameRepo.IsItemEquipped(equipableItem))
{
GameRepo.UnequipItem(equipableItem);
itemSlot.SetSelectedItemStyle();
}
else
{
GameRepo.EquipItem(equipableItem);
itemSlot.SetEquippedSelectedItemStyle();
}
UpdateStatBonusInfo();
}
}
private void UpdateStatBonusInfo()
{
var atkBonus = GameRepo.PlayerStatInfo.Value.BonusAttack;
ATKBonusLabel.Text = atkBonus != 0 ? $"{atkBonus:+0;-#}" : "...";
var defBonus = GameRepo.PlayerStatInfo.Value.BonusDefense;
DEFBonusLabel.Text = defBonus != 0 ? $"{defBonus:+0;-#}" : "...";
var currentHP = GameRepo.PlayerStatInfo.Value.CurrentHP;
var maxHP = GameRepo.PlayerStatInfo.Value.MaximumHP;
HPValue.Text = $"{currentHP}/{maxHP}";
var currentVT = GameRepo.PlayerStatInfo.Value.CurrentVT;
var maxVT = GameRepo.PlayerStatInfo.Value.MaximumVT;
VTValue.Text = $"{currentVT}/{maxVT}";
}
private async void UseButtonPressed()
{
var currentItem = ItemSlots[_currentIndex].Item;
if (currentItem is IEquipable)
EquipOrUnequipItem();
if (currentItem is ConsumableItem consumable)
consumable.Use();
// TODO: Replace with animation player (for visual effects/sound effects?)
await ToSignal(GetTree().CreateTimer(0.25f), "timeout");
await HideUserActionPrompt();
PopulatePlayerInfo();
}
private async void ThrowButtonPressed()
{
var currentItem = ItemSlots[_currentIndex].Item;
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
_currentIndex = 0;
Game.ToggleInventory();
currentItem.Throw();
}
private async void DropButtonPressed()
{
var currentItem = ItemSlots[_currentIndex].Item;
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
_currentIndex = 0;
Game.ToggleInventory();
currentItem.Drop();
}
private enum InventoryPageNumber
{
FirstPage,
SecondPage
}
}