Skip to content

Commit

Permalink
Add inventory source option to the Inventory Space Indicator widget (F…
Browse files Browse the repository at this point in the history
…ixes #147, #185)
  • Loading branch information
haroldiedema committed Jul 20, 2024
1 parent e1da916 commit bdea1e1
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 24 deletions.
10 changes: 6 additions & 4 deletions Umbra.Game/src/Player/IPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

using System.Collections.Generic;
using System.Numerics;
using Umbra.Game.Inventory;
using Umbra.Game.Societies;

namespace Umbra.Game;
Expand Down Expand Up @@ -172,14 +173,15 @@ public interface IPlayer
public IEnumerable<Society> Societies { get; }

/// <summary>
/// Get the job information by the specified job ID.
/// Returns a service instance that allows retrieving information about the
/// player's different inventory containers.
/// </summary>
public JobInfo GetJobInfo(byte jobId);
public IPlayerInventory Inventory { get; }

/// <summary>
/// Returns the amount of free space the player has in their main inventory.
/// Get the job information by the specified job ID.
/// </summary>
public uint GetFreeInventorySpace();
public JobInfo GetJobInfo(byte jobId);

/// <summary>
/// Returns true if the player has the specified item in their inventory.
Expand Down
14 changes: 14 additions & 0 deletions Umbra.Game/src/Player/Inventory/IPlayerInventory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Umbra.Game.Inventory;

public interface IPlayerInventory
{
/// <summary>
/// Returns the amount of occupied inventory space for the given inventory type.
/// </summary>
public uint GetOccupiedInventorySpace(PlayerInventoryType type);

/// <summary>
/// Returns the total amount of inventory space for the given inventory type.
/// </summary>
public uint GetTotalInventorySpace(PlayerInventoryType type);
}
74 changes: 74 additions & 0 deletions Umbra.Game/src/Player/Inventory/PlayerInventory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using FFXIVClientStructs.FFXIV.Client.Game;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbra.Common;

namespace Umbra.Game.Inventory;

[Service]
internal class PlayerInventory : IPlayerInventory
{
public uint GetOccupiedInventorySpace(PlayerInventoryType type)
{
return GetInventoryTypes(type).Aggregate<InventoryType, uint>(0, (current, iType) => current + GetUsedSizeOf(iType));
}

public uint GetTotalInventorySpace(PlayerInventoryType type)
{
return GetInventoryTypes(type).Aggregate<InventoryType, uint>(0, (current, iType) => current + GetTotalSizeOf(iType));
}

private static unsafe uint GetUsedSizeOf(InventoryType type)
{
InventoryManager* im = InventoryManager.Instance();
InventoryContainer* inv = im->GetInventoryContainer(type);

if (inv->Loaded == 0 || inv->Size == 0) return 0;

uint usedSpace = 0;

for (var i = 0; i <= inv->Size; i++) {
var slot = inv->GetInventorySlot(i);
if (slot == null) continue;
if (slot->ItemId > 0) usedSpace++;
}

return usedSpace;
}

private static unsafe uint GetTotalSizeOf(InventoryType type)
{
InventoryManager* im = InventoryManager.Instance();
InventoryContainer* inv = im->GetInventoryContainer(type);

return inv->Loaded == 0 ? 0 : inv->Size;
}

private static IEnumerable<InventoryType> GetInventoryTypes(PlayerInventoryType type)
{
return type switch
{
PlayerInventoryType.Inventory =>
[
InventoryType.Inventory1,
InventoryType.Inventory2,
InventoryType.Inventory3,
InventoryType.Inventory4
],
PlayerInventoryType.Saddlebag =>
[
InventoryType.SaddleBag1,
InventoryType.SaddleBag2,
],
PlayerInventoryType.SaddlebagPremium =>
[
InventoryType.SaddleBag1,
InventoryType.SaddleBag2,
InventoryType.PremiumSaddleBag1,
InventoryType.PremiumSaddleBag2
],
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
}
8 changes: 8 additions & 0 deletions Umbra.Game/src/Player/Inventory/PlayerInventoryType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Umbra.Game.Inventory;

public enum PlayerInventoryType
{
Inventory,
Saddlebag,
SaddlebagPremium,
}
14 changes: 6 additions & 8 deletions Umbra.Game/src/Player/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Excel.GeneratedSheets;
using Umbra.Common;
using Umbra.Game.Inventory;
using Umbra.Game.Societies;

namespace Umbra.Game;
Expand Down Expand Up @@ -174,6 +175,8 @@ internal sealed class Player : IPlayer

public IEquipmentRepository Equipment { get; }

public IPlayerInventory Inventory { get; }

private readonly IClientState _clientState;
private readonly ICondition _condition;
private readonly JobInfoRepository _jobInfoRepository;
Expand All @@ -186,7 +189,8 @@ public Player(
ICondition condition,
IEquipmentRepository equipmentRepository,
JobInfoRepository jobInfoRepository,
SocietiesRepository societiesRepository
SocietiesRepository societiesRepository,
IPlayerInventory playerInventory
)
{
_clientState = clientState;
Expand All @@ -195,6 +199,7 @@ SocietiesRepository societiesRepository
_societiesRepository = societiesRepository;

Equipment = equipmentRepository;
Inventory = playerInventory;

OnTick();
}
Expand Down Expand Up @@ -268,13 +273,6 @@ public JobInfo GetJobInfo(byte jobId)
return _jobInfoRepository.GetJobInfo(jobId);
}

public unsafe uint GetFreeInventorySpace()
{
InventoryManager* im = InventoryManager.Instance();

return im == null ? 0 : im->GetEmptySlotsInBag();
}

/// <summary>
/// Returns true if the player has the specified item in their inventory.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions Umbra/i18n/de.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Umbra/i18n/en.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Umbra/i18n/fr.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Umbra/i18n/ja.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Umbra/i18n/zh.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ internal partial class InventorySpaceWidget
protected override IEnumerable<IWidgetConfigVariable> GetConfigVariables()
{
return [
new SelectWidgetConfigVariable(
"Source",
I18N.Translate("Widget.InventorySpace.Config.Source.Name"),
I18N.Translate("Widget.InventorySpace.Config.Source.Description"),
"Inventory",
new() {
{ "Inventory", I18N.Translate("Widget.InventorySpace.Config.Source.Option.Inventory") },
{ "SaddleBag", I18N.Translate("Widget.InventorySpace.Config.Source.Option.SaddleBag") },
{ "SaddleBagPremium", I18N.Translate("Widget.InventorySpace.Config.Source.Option.SaddleBagPremium") }
}
),
new IntegerWidgetConfigVariable(
"WarningThreshold",
I18N.Translate("Widget.InventorySpace.Config.WarningThreshold.Name"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Collections.Generic;
using Umbra.Common;
using Umbra.Game;
using Umbra.Game.Inventory;

namespace Umbra.Widgets.Library.InventorySpace;

Expand All @@ -30,60 +31,84 @@ internal partial class InventorySpaceWidget(
/// <inheritdoc/>
public override WidgetPopup? Popup => null;

private IPlayer Player { get; set; } = null!;
private uint FreeSpace { get; set; }
private IPlayer Player { get; set; } = null!;

/// <inheritdoc/>
protected override void Initialize()
{
Player = Framework.Service<IPlayer>();

Node.OnClick += _ => Framework.Service<IChatSender>().Send("/inventory");
Node.OnClick += _ => OpenInventoryWindow();
Node.OnRightClick += _ => Framework.Service<IChatSender>().Send("/keyitem");
}

/// <inheritdoc/>
protected override void OnUpdate()
{
FreeSpace = Player.GetFreeInventorySpace();
var iconLocation = GetConfigValue<string>("IconLocation");
PlayerInventoryType source = GetConfigValue<string>("Source") switch {
"Inventory" => PlayerInventoryType.Inventory,
"SaddleBag" => PlayerInventoryType.Saddlebag,
"SaddleBagPremium" => PlayerInventoryType.SaddlebagPremium,
_ => PlayerInventoryType.Inventory
};

uint usedSpace = Player.Inventory.GetOccupiedInventorySpace(source);
uint totalSpace = Player.Inventory.GetTotalInventorySpace(source);
var iconLocation = GetConfigValue<string>("IconLocation");

SetGhost(!GetConfigValue<bool>("Decorate"));

switch (iconLocation) {
case "Left":
SetLeftIcon(GetIconId());
SetLeftIcon(GetIconId(source, totalSpace - usedSpace));
SetRightIcon(null);
break;
case "Right":
SetLeftIcon(null);
SetRightIcon(GetIconId());
SetRightIcon(GetIconId(source, totalSpace - usedSpace));
break;
}

var l = iconLocation == "Left" ? " " : "";
var r = iconLocation == "Right" ? " " : "";
SetLabel(GetConfigValue<bool>("ShowTotal") ? $"{l}{FreeSpace} / 140{r}" : $"{l}{FreeSpace}{r}");
SetLabel(GetConfigValue<bool>("ShowTotal") ? $"{l}{usedSpace} / {totalSpace}{r}" : $"{l}{usedSpace}{r}");

LabelNode.Style.TextOffset = new(0, GetConfigValue<int>("TextYOffset"));
}

private uint GetIconId()
private uint GetIconId(PlayerInventoryType type, uint freeSpace)
{
LeftIconNode.Style.ImageGrayscale = false;
RightIconNode.Style.ImageGrayscale = false;

if (FreeSpace <= GetConfigValue<int>("CriticalThreshold")) {
if (freeSpace <= GetConfigValue<int>("CriticalThreshold")) {
return 60074; // Critical
}

if (FreeSpace <= GetConfigValue<int>("WarningThreshold")) {
if (freeSpace <= GetConfigValue<int>("WarningThreshold")) {
return 60073; // Warning
}

LeftIconNode.Style.ImageGrayscale = GetConfigValue<bool>("DesaturateIcon");
RightIconNode.Style.ImageGrayscale = GetConfigValue<bool>("DesaturateIcon");

return 2;
return GetSourceIconId(type);
}

private static uint GetSourceIconId(PlayerInventoryType source)
{
return source switch {
PlayerInventoryType.Inventory => 2,
PlayerInventoryType.Saddlebag => 74,
PlayerInventoryType.SaddlebagPremium => 74,
_ => 2
};
}

private void OpenInventoryWindow()
{
Framework
.Service<IChatSender>()
.Send(GetConfigValue<string>("Source") == "Inventory" ? "/inventory" : "/saddlebag");
}
}

0 comments on commit bdea1e1

Please sign in to comment.