Configurable item list

This commit is contained in:
Liza 2023-09-21 23:17:46 +02:00
parent facac551ca
commit f56a66df29
Signed by: liza
GPG Key ID: 7199F8D727D55F67
14 changed files with 535 additions and 180 deletions

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Dalamud.Configuration;
namespace Deliveroo;
internal sealed class Configuration : IPluginConfiguration
{
public int Version { get; set; } = 1;
public List<uint> ItemsAvailableForPurchase { get; set; } = new();
public uint SelectedPurchaseItemId { get; set; } = 0;
}

View File

@ -56,6 +56,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin"> <Target Name="RenameLatestZip" AfterTargets="PackagePlugin">
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/> <Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/>
</Target> </Target>

View File

@ -1,7 +1,7 @@
{ {
"Name": "Deliveroo", "Name": "Deliveroo",
"Author": "Liza Carvelli", "Author": "Liza Carvelli",
"Punchline": "", "Punchline": "Better Grand Company Deliveries",
"Description": "", "Description": "",
"RepoUrl": "https://git.carvel.li/liza/Deliveroo" "RepoUrl": "https://git.carvel.li/liza/Deliveroo"
} }

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Dalamud.Data;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Game.ClientState; using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects;
@ -13,6 +14,8 @@ using Dalamud.Interface.Windowing;
using Dalamud.Logging; using Dalamud.Logging;
using Dalamud.Memory; using Dalamud.Memory;
using Dalamud.Plugin; using Dalamud.Plugin;
using Deliveroo.GameData;
using Deliveroo.Windows;
using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.Game.UI; using FFXIVClientStructs.FFXIV.Client.Game.UI;
@ -24,7 +27,7 @@ using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Deliveroo; namespace Deliveroo;
public class DeliverooPlugin : IDalamudPlugin public sealed class DeliverooPlugin : IDalamudPlugin
{ {
private readonly WindowSystem _windowSystem = new(typeof(DeliverooPlugin).AssemblyQualifiedName); private readonly WindowSystem _windowSystem = new(typeof(DeliverooPlugin).AssemblyQualifiedName);
@ -36,14 +39,20 @@ public class DeliverooPlugin : IDalamudPlugin
private readonly ObjectTable _objectTable; private readonly ObjectTable _objectTable;
private readonly TargetManager _targetManager; private readonly TargetManager _targetManager;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly Configuration _configuration;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly GcRewardsCache _gcRewardsCache;
private readonly ConfigWindow _configWindow;
private readonly TurnInWindow _turnInWindow; private readonly TurnInWindow _turnInWindow;
private Stage _currentStageInternal = Stage.Stop; private Stage _currentStageInternal = Stage.Stop;
private DateTime _continueAt = DateTime.MinValue; private DateTime _continueAt = DateTime.MinValue;
private SealPurchaseOption? _selectedPurchaseOption; private GcRewardItem _selectedRewardItem = GcRewardItem.None;
public DeliverooPlugin(DalamudPluginInterface pluginInterface, ChatGui chatGui, GameGui gameGui, public DeliverooPlugin(DalamudPluginInterface pluginInterface, ChatGui chatGui, GameGui gameGui,
Framework framework, ClientState clientState, ObjectTable objectTable, TargetManager targetManager) Framework framework, ClientState clientState, ObjectTable objectTable, TargetManager targetManager,
DataManager dataManager)
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
_chatGui = chatGui; _chatGui = chatGui;
@ -53,11 +62,16 @@ public class DeliverooPlugin : IDalamudPlugin
_objectTable = objectTable; _objectTable = objectTable;
_targetManager = targetManager; _targetManager = targetManager;
_turnInWindow = new TurnInWindow(); _configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration();
_gcRewardsCache = new GcRewardsCache(dataManager);
_configWindow = new ConfigWindow(_pluginInterface, this, _configuration, _gcRewardsCache);
_windowSystem.AddWindow(_configWindow);
_turnInWindow = new TurnInWindow(this, _pluginInterface, _configuration, _gcRewardsCache);
_windowSystem.AddWindow(_turnInWindow); _windowSystem.AddWindow(_turnInWindow);
_framework.Update += FrameworkUpdate; _framework.Update += FrameworkUpdate;
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw; _pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
} }
public string Name => "Deliveroo"; public string Name => "Deliveroo";
@ -80,9 +94,12 @@ public class DeliverooPlugin : IDalamudPlugin
{ {
if (!_clientState.IsLoggedIn || _clientState.TerritoryType is not 128 and not 130 and not 132 || if (!_clientState.IsLoggedIn || _clientState.TerritoryType is not 128 and not 130 and not 132 ||
GetDistanceToNpc(GetQuartermasterId(), out GameObject? quartermaster) >= 7f || GetDistanceToNpc(GetQuartermasterId(), out GameObject? quartermaster) >= 7f ||
GetDistanceToNpc(GetPersonnelOfficerId(), out GameObject? personnelOfficer) >= 7f) GetDistanceToNpc(GetPersonnelOfficerId(), out GameObject? personnelOfficer) >= 7f ||
_configWindow.IsOpen)
{ {
_turnInWindow.IsOpen = false; _turnInWindow.IsOpen = false;
_turnInWindow.State = false;
CurrentStage = Stage.Stop;
} }
else if (DateTime.Now > _continueAt) else if (DateTime.Now > _continueAt)
{ {
@ -98,10 +115,11 @@ public class DeliverooPlugin : IDalamudPlugin
if (_turnInWindow.State && CurrentStage == Stage.Stop) if (_turnInWindow.State && CurrentStage == Stage.Stop)
{ {
CurrentStage = Stage.TargetPersonnelOfficer; CurrentStage = Stage.TargetPersonnelOfficer;
_selectedPurchaseOption = _turnInWindow.SelectedOption; _selectedRewardItem = _turnInWindow.SelectedItem;
if (_selectedPurchaseOption.ItemId == 0) if (_selectedRewardItem.IsValid() && _selectedRewardItem.RequiredRank > GetGrandCompanyRank())
_selectedPurchaseOption = null; _selectedRewardItem = GcRewardItem.None;
else if (GetCurrentSealCount() > GetSealCap() / 2)
if (_selectedRewardItem.IsValid() && GetCurrentSealCount() > GetSealCap() / 2)
CurrentStage = Stage.TargetQuartermaster; CurrentStage = Stage.TargetQuartermaster;
if (TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList", out var gcSupplyList) && if (TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList", out var gcSupplyList) &&
@ -145,7 +163,7 @@ public class DeliverooPlugin : IDalamudPlugin
break; break;
var agent = (AgentGrandCompanySupply*)agentInterface; var agent = (AgentGrandCompanySupply*)agentInterface;
List<GcItem> items = BuildTurnInList(agent); List<TurnInItem> items = BuildTurnInList(agent);
if (items.Count == 0 || addon->UldManager.NodeList[20]->IsVisible) if (items.Count == 0 || addon->UldManager.NodeList[20]->IsVisible)
{ {
CurrentStage = Stage.CloseGcSupplyThenStop; CurrentStage = Stage.CloseGcSupplyThenStop;
@ -212,7 +230,7 @@ public class DeliverooPlugin : IDalamudPlugin
case Stage.CloseGcSupply: case Stage.CloseGcSupply:
if (SelectSelectString(3)) if (SelectSelectString(3))
{ {
if (_selectedPurchaseOption == null) if (!_selectedRewardItem.IsValid())
{ {
_turnInWindow.State = false; _turnInWindow.State = false;
CurrentStage = Stage.Stop; CurrentStage = Stage.Stop;
@ -230,12 +248,12 @@ public class DeliverooPlugin : IDalamudPlugin
case Stage.CloseGcSupplyThenStop: case Stage.CloseGcSupplyThenStop:
if (SelectSelectString(3)) if (SelectSelectString(3))
{ {
if (_selectedPurchaseOption == null) if (!_selectedRewardItem.IsValid())
{ {
_turnInWindow.State = false; _turnInWindow.State = false;
CurrentStage = Stage.Stop; CurrentStage = Stage.Stop;
} }
else if (GetCurrentSealCount() <= 2000 + _selectedPurchaseOption!.SealCost) else if (GetCurrentSealCount() <= 2000 + _selectedRewardItem.SealCost)
{ {
_turnInWindow.State = false; _turnInWindow.State = false;
CurrentStage = Stage.Stop; CurrentStage = Stage.Stop;
@ -260,18 +278,19 @@ public class DeliverooPlugin : IDalamudPlugin
break; break;
InteractWithTarget(quartermaster!); InteractWithTarget(quartermaster!);
CurrentStage = Stage.SelectRewardRank; CurrentStage = Stage.SelectRewardTier;
break; break;
case Stage.SelectRewardRank: case Stage.SelectRewardTier:
{ {
if (TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) && if (TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) &&
IsAddonReady(addonExchange)) IsAddonReady(addonExchange))
{ {
PluginLog.Information($"Selecting tier 1, {(int)_selectedRewardItem.Tier - 1}");
var selectRank = stackalloc AtkValue[] var selectRank = stackalloc AtkValue[]
{ {
new() { Type = ValueType.Int, Int = 1 }, new() { Type = ValueType.Int, Int = 1 },
new() { Type = ValueType.Int, Int = (int)_selectedPurchaseOption!.Rank }, new() { Type = ValueType.Int, Int = (int)_selectedRewardItem.Tier - 1 },
new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 },
new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 },
new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 },
@ -282,21 +301,22 @@ public class DeliverooPlugin : IDalamudPlugin
}; };
addonExchange->FireCallback(9, selectRank); addonExchange->FireCallback(9, selectRank);
_continueAt = DateTime.Now.AddSeconds(0.5); _continueAt = DateTime.Now.AddSeconds(0.5);
CurrentStage = Stage.SelectRewardType; CurrentStage = Stage.SelectRewardSubCategory;
} }
break; break;
} }
case Stage.SelectRewardType: case Stage.SelectRewardSubCategory:
{ {
if (TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) && if (TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) &&
IsAddonReady(addonExchange)) IsAddonReady(addonExchange))
{ {
PluginLog.Information($"Selecting subcategory 2, {(int)_selectedRewardItem.SubCategory}");
var selectType = stackalloc AtkValue[] var selectType = stackalloc AtkValue[]
{ {
new() { Type = ValueType.Int, Int = 2 }, new() { Type = ValueType.Int, Int = 2 },
new() { Type = ValueType.Int, Int = (int)_selectedPurchaseOption!.Type }, new() { Type = ValueType.Int, Int = (int)_selectedRewardItem.SubCategory },
new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 },
new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 },
new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 },
@ -315,38 +335,20 @@ public class DeliverooPlugin : IDalamudPlugin
case Stage.SelectReward: case Stage.SelectReward:
{ {
// coke: 0i, 31i, 5i[count], unknown, true, false, unknown, unknown, unknown
if (TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) && if (TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) &&
IsAddonReady(addonExchange)) IsAddonReady(addonExchange))
{ {
int toBuy = (GetCurrentSealCount() - 2000) / _selectedPurchaseOption!.SealCost; if (SelectRewardItem(addonExchange))
bool isVenture = _selectedPurchaseOption!.ItemId == 21072;
if (isVenture)
toBuy = Math.Min(toBuy, 65000 - GetCurrentVentureCount());
if (toBuy == 0)
{ {
_turnInWindow.State = false; _continueAt = DateTime.Now.AddSeconds(0.5);
CurrentStage = Stage.Stop; CurrentStage = Stage.ConfirmReward;
break;
} }
else
_chatGui.Print($"Buying {toBuy}x {_selectedPurchaseOption!.Name}...");
var selectReward = stackalloc AtkValue[]
{ {
new() { Type = ValueType.Int, Int = 0 }, PluginLog.Warning("Could not find selected reward item");
new() { Type = ValueType.Int, Int = _selectedPurchaseOption!.Position }, _continueAt = DateTime.Now.AddSeconds(0.5);
new() { Type = ValueType.Int, Int = toBuy }, CurrentStage = Stage.CloseGcExchange;
new() { Type = 0, Int = 0 }, }
new() { Type = ValueType.Bool, Byte = 1 },
new() { Type = ValueType.Bool, Byte = 0 },
new() { Type = 0, Int = 0 },
new() { Type = 0, Int = 0 },
new() { Type = 0, Int = 0 }
};
addonExchange->FireCallback(9, selectReward);
_continueAt = DateTime.Now.AddSeconds(0.5);
CurrentStage = Stage.ConfirmReward;
} }
break; break;
@ -407,19 +409,20 @@ public class DeliverooPlugin : IDalamudPlugin
public void Dispose() public void Dispose()
{ {
_pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle;
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw; _pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
_framework.Update -= FrameworkUpdate; _framework.Update -= FrameworkUpdate;
} }
private unsafe List<GcItem> BuildTurnInList(AgentGrandCompanySupply* agent) private unsafe List<TurnInItem> BuildTurnInList(AgentGrandCompanySupply* agent)
{ {
List<GcItem> list = new(); List<TurnInItem> list = new();
for (int i = 11 /* skip over provisioning items */; i < agent->NumItems; ++i) for (int i = 11 /* skip over provisioning items */; i < agent->NumItems; ++i)
{ {
GrandCompanyItem item = agent->ItemArray[i]; GrandCompanyItem item = agent->ItemArray[i];
// this includes all items, even if they don't match the filter // this includes all items, even if they don't match the filter
list.Add(new GcItem list.Add(new TurnInItem
{ {
ItemId = Marshal.ReadInt32(new nint(&item) + 132), ItemId = Marshal.ReadInt32(new nint(&item) + 132),
Name = MemoryHelper.ReadSeString(&item.ItemName).ToString(), Name = MemoryHelper.ReadSeString(&item.ItemName).ToString(),
@ -490,6 +493,48 @@ public class DeliverooPlugin : IDalamudPlugin
return addon->IsVisible && addon->UldManager.LoadedState == AtkLoadState.Loaded; return addon->IsVisible && addon->UldManager.LoadedState == AtkLoadState.Loaded;
} }
private unsafe bool SelectRewardItem(AtkUnitBase* addonExchange)
{
uint itemsOnCurrentPage = addonExchange->AtkValues[1].UInt;
for (uint i = 0; i < itemsOnCurrentPage; ++i)
{
uint itemId = addonExchange->AtkValues[317 + i].UInt;
if (itemId == _selectedRewardItem.ItemId)
{
long toBuy = (GetCurrentSealCount() - 2000) / _selectedRewardItem.SealCost;
bool isVenture = _selectedRewardItem.ItemId == ItemIds.Venture;
if (isVenture)
toBuy = Math.Min(toBuy, 65000 - GetCurrentVentureCount());
if (toBuy == 0)
{
_turnInWindow.State = false;
CurrentStage = Stage.Stop;
break;
}
PluginLog.Information($"Selecting item {itemId}, {i}");
_chatGui.Print($"Buying {toBuy}x {_selectedRewardItem.Name}...");
var selectReward = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 0 },
new() { Type = ValueType.Int, Int = (int)i },
new() { Type = ValueType.Int, Int = (int)toBuy },
new() { Type = 0, Int = 0 },
new() { Type = ValueType.Bool, Byte = 1 },
new() { Type = ValueType.Bool, Byte = 0 },
new() { Type = 0, Int = 0 },
new() { Type = 0, Int = 0 },
new() { Type = 0, Int = 0 }
};
addonExchange->FireCallback(9, selectReward);
return true;
}
}
return false;
}
private unsafe int GetCurrentSealCount() private unsafe int GetCurrentSealCount()
{ {
InventoryManager* inventoryManager = InventoryManager.Instance(); InventoryManager* inventoryManager = InventoryManager.Instance();
@ -506,9 +551,13 @@ public class DeliverooPlugin : IDalamudPlugin
} }
} }
private unsafe int GetPersonnelOfficerId() internal unsafe GrandCompany GetGrandCompany() => (GrandCompany)PlayerState.Instance()->GrandCompany;
internal unsafe byte GetGrandCompanyRank() => PlayerState.Instance()->GetGrandCompanyRank();
private int GetPersonnelOfficerId()
{ {
return ((GrandCompany)PlayerState.Instance()->GrandCompany) switch return GetGrandCompany() switch
{ {
GrandCompany.Maelstrom => 0xF4B94, GrandCompany.Maelstrom => 0xF4B94,
GrandCompany.ImmortalFlames => 0xF4B97, GrandCompany.ImmortalFlames => 0xF4B97,
@ -517,9 +566,9 @@ public class DeliverooPlugin : IDalamudPlugin
}; };
} }
private unsafe int GetQuartermasterId() private int GetQuartermasterId()
{ {
return ((GrandCompany)PlayerState.Instance()->GrandCompany) switch return GetGrandCompany() switch
{ {
GrandCompany.Maelstrom => 0xF4B93, GrandCompany.Maelstrom => 0xF4B93,
GrandCompany.ImmortalFlames => 0xF4B96, GrandCompany.ImmortalFlames => 0xF4B96,
@ -528,9 +577,9 @@ public class DeliverooPlugin : IDalamudPlugin
}; };
} }
private unsafe int GetSealCap() private int GetSealCap()
{ {
return PlayerState.Instance()->GetGrandCompanyRank() switch return GetGrandCompanyRank() switch
{ {
1 => 10_000, 1 => 10_000,
2 => 15_000, 2 => 15_000,
@ -550,7 +599,7 @@ public class DeliverooPlugin : IDalamudPlugin
private unsafe int GetCurrentVentureCount() private unsafe int GetCurrentVentureCount()
{ {
InventoryManager* inventoryManager = InventoryManager.Instance(); InventoryManager* inventoryManager = InventoryManager.Instance();
return inventoryManager->GetInventoryItemCount(21072, false, false, false); return inventoryManager->GetInventoryItemCount(ItemIds.Venture, false, false, false);
} }
private unsafe bool SelectSelectString(int choice) private unsafe bool SelectSelectString(int choice)
@ -570,6 +619,9 @@ public class DeliverooPlugin : IDalamudPlugin
if (TryGetAddonByName<AddonSelectYesno>("SelectYesno", out var addonSelectYesno) && if (TryGetAddonByName<AddonSelectYesno>("SelectYesno", out var addonSelectYesno) &&
IsAddonReady(&addonSelectYesno->AtkUnitBase)) IsAddonReady(&addonSelectYesno->AtkUnitBase))
{ {
PluginLog.Information(
$"Selecting choice={choice} for '{MemoryHelper.ReadSeString(&addonSelectYesno->PromptText->NodeText)}'");
addonSelectYesno->AtkUnitBase.FireCallbackInt(choice); addonSelectYesno->AtkUnitBase.FireCallbackInt(choice);
return true; return true;
} }
@ -616,8 +668,8 @@ public class DeliverooPlugin : IDalamudPlugin
CloseGcSupplyThenStop, CloseGcSupplyThenStop,
TargetQuartermaster, TargetQuartermaster,
SelectRewardRank, SelectRewardTier,
SelectRewardType, SelectRewardSubCategory,
SelectReward, SelectReward,
ConfirmReward, ConfirmReward,
CloseGcExchange, CloseGcExchange,

View File

@ -0,0 +1,57 @@
using System;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
namespace Deliveroo.GameData;
internal sealed class GcRewardItem : IEquatable<GcRewardItem>
{
public static GcRewardItem None { get; } = new GcRewardItem
{
ItemId = 0,
Name = "Buy Nothing",
GrandCompany = GrandCompany.None,
Tier = RewardTier.First,
SubCategory = RewardSubCategory.Unknown,
RequiredRank = 0,
StackSize = 0,
SealCost = 100_000,
};
public required uint ItemId { get; init; }
public required string Name { get; init; }
public required GrandCompany GrandCompany { get; init; }
public required RewardTier Tier { get; init; }
public required RewardSubCategory SubCategory { get; init; }
public required uint RequiredRank { get; init; }
public required uint StackSize { get; init; }
public required uint SealCost { get; init; }
public bool IsValid() => ItemId > 0 && GrandCompany != GrandCompany.None;
public bool Equals(GcRewardItem? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return ItemId == other.ItemId;
}
public override bool Equals(object? obj)
{
return ReferenceEquals(this, obj) || obj is GcRewardItem other && Equals(other);
}
public override int GetHashCode()
{
return (int)ItemId;
}
public static bool operator ==(GcRewardItem? left, GcRewardItem? right)
{
return Equals(left, right);
}
public static bool operator !=(GcRewardItem? left, GcRewardItem? right)
{
return !Equals(left, right);
}
}

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using Dalamud;
using Dalamud.Data;
using Dalamud.Logging;
using Lumina.Excel.GeneratedSheets;
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
namespace Deliveroo.GameData;
internal class GcRewardsCache
{
public GcRewardsCache(DataManager dataManager)
{
var categories = dataManager.GetExcelSheet<GCScripShopCategory>()!
.Where(x => x.RowId > 0)
.ToDictionary(x => x.RowId,
x =>
(Gc: (GrandCompany)x.GrandCompany.Row,
Tier: (RewardTier)x.Tier,
SubCategory: (RewardSubCategory)x.SubCategory));
var items = dataManager.GetExcelSheet<GCScripShopItem>()!
.Where(x => x.RowId > 0 && x.Item.Row > 0)
.ToList();
foreach (var item in items)
{
var category = categories[item.RowId];
Rewards[category.Gc].Add(new GcRewardItem
{
ItemId = item.Item.Row,
Name = item.Item.Value!.Name.ToString(),
GrandCompany = category.Gc,
Tier = category.Tier,
SubCategory = category.SubCategory,
RequiredRank = item.RequiredGrandCompanyRank.Row,
StackSize = item.Item!.Value.StackSize,
SealCost = item.CostGCSeals,
});
}
}
public Dictionary<GrandCompany, List<GcRewardItem>> Rewards { get; } = new()
{
{ GrandCompany.Maelstrom, new() },
{ GrandCompany.TwinAdder, new() },
{ GrandCompany.ImmortalFlames, new() }
};
}

View File

@ -0,0 +1,6 @@
namespace Deliveroo.GameData;
public static class ItemIds
{
public const uint Venture = 21072;
}

View File

@ -0,0 +1,10 @@
namespace Deliveroo.GameData;
public enum RewardSubCategory : int
{
Unknown = 0,
Materiel = 1,
Weapons = 2,
Armor = 3,
Materials = 4,
}

View File

@ -0,0 +1,9 @@
namespace Deliveroo.GameData;
public enum RewardTier : int
{
Unknown = 0,
First = 1,
Second = 2,
Third = 3,
}

View File

@ -1,9 +1,9 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.System.String;
namespace Deliveroo; namespace Deliveroo.GameData;
internal sealed class GcItem internal sealed class TurnInItem
{ {
public required int ItemId { get; init; } public required int ItemId { get; init; }
public required string Name { get; init; } public required string Name { get; init; }

View File

@ -1,39 +0,0 @@
namespace Deliveroo;
internal sealed class SealPurchaseOption
{
public SealPurchaseOption(uint itemId, string name, PurchaseRank rank, PurchaseType type, int position,
int sealCost)
{
ItemId = itemId;
Name = name;
Rank = rank;
Type = type;
Position = position;
SealCost = sealCost;
}
public uint ItemId { get; }
public string Name { get; }
public PurchaseRank Rank { get; }
public PurchaseType Type { get; }
public int Position { get; }
public int SealCost { get; }
public enum PurchaseRank : int
{
First = 0,
Second = 1,
Third = 2,
Unknown = int.MaxValue,
}
public enum PurchaseType : int
{
Materiel = 1,
Weapons = 2,
Armor = 3,
Materials = 4,
Unknown = int.MaxValue,
}
}

View File

@ -1,81 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing;
using FFXIVClientStructs.FFXIV.Client.Game;
using ImGuiNET;
namespace Deliveroo;
internal sealed class TurnInWindow : Window
{
private int _sealPurchaseSelected = 0;
private readonly List<SealPurchaseOption> _sealPurchaseOptions = new()
{
new SealPurchaseOption(0, "Buy Nothing", SealPurchaseOption.PurchaseRank.Unknown,
SealPurchaseOption.PurchaseType.Unknown, 0, 0),
new SealPurchaseOption(21072, "Venture", SealPurchaseOption.PurchaseRank.First,
SealPurchaseOption.PurchaseType.Materiel, 0, 200),
new SealPurchaseOption(5530, "Coke", SealPurchaseOption.PurchaseRank.Third,
SealPurchaseOption.PurchaseType.Materials, 31, 200),
};
public TurnInWindow()
: base("GC Delivery###DeliverooTurnIn")
{
Position = new Vector2(100, 100);
PositionCondition = ImGuiCond.FirstUseEver;
Flags = ImGuiWindowFlags.AlwaysAutoResize;
}
public bool State { get; set; }
public decimal Multiplier { private get; set; }
public SealPurchaseOption SelectedOption => _sealPurchaseOptions[_sealPurchaseSelected];
public string Debug { get; set; }
public override void Draw()
{
bool state = State;
if (ImGui.Checkbox("Handle GC turn ins/exchange automatically", ref state))
{
State = state;
}
ImGui.Indent(27);
if (Multiplier == 1m)
{
ImGui.TextColored(ImGuiColors.DalamudRed, "You do not have a buff active");
}
else
{
ImGui.TextColored(ImGuiColors.HealerGreen, $"Current Buff: {(Multiplier - 1m) * 100:N0}%%");
}
ImGui.Spacing();
ImGui.BeginDisabled(state);
string[] comboValues = _sealPurchaseOptions.Select(x =>
{
if (x.ItemId == 0)
return x.Name;
return $"{x.Name} ({GetItemCount(x.ItemId):N0}x)";
}).ToArray();
ImGui.Combo("", ref _sealPurchaseSelected, comboValues, comboValues.Length);
ImGui.EndDisabled();
ImGui.Unindent(27);
ImGui.Separator();
ImGui.Text($"Debug (State): {Debug}");
}
private unsafe int GetItemCount(uint itemId)
{
InventoryManager* inventoryManager = InventoryManager.Instance();
return inventoryManager->GetInventoryItemCount(itemId, false, false, false);
}
}

View File

@ -0,0 +1,139 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Dalamud.Plugin;
using Deliveroo.GameData;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using ImGuiNET;
namespace Deliveroo.Windows;
internal sealed class ConfigWindow : Window
{
private readonly DalamudPluginInterface _pluginInterface;
private readonly DeliverooPlugin _plugin;
private readonly Configuration _configuration;
private readonly GcRewardsCache _gcRewardsCache;
private readonly Dictionary<uint, GcRewardItem> _itemLookup;
private uint _dragDropSource = 0;
public ConfigWindow(DalamudPluginInterface pluginInterface, DeliverooPlugin plugin, Configuration configuration,
GcRewardsCache gcRewardsCache)
: base("Deliveroo - Configuration###DeliverooConfig")
{
_pluginInterface = pluginInterface;
_plugin = plugin;
_configuration = configuration;
_gcRewardsCache = gcRewardsCache;
_itemLookup = _gcRewardsCache.Rewards.Values
.SelectMany(x => x)
.Distinct()
.ToDictionary(x => x.ItemId, x => x);
Size = new Vector2(400, 300);
SizeCondition = ImGuiCond.Appearing;
}
public override unsafe void Draw()
{
ImGui.Text("Items available for Auto-Buy:");
uint? itemToRemove = null;
uint? itemToAdd = null;
int indexToAdd = 0;
if (_configuration.ItemsAvailableForPurchase.Count == 0)
{
_configuration.ItemsAvailableForPurchase.Add(ItemIds.Venture);
_pluginInterface.SavePluginConfig(_configuration);
}
if (ImGui.BeginChild("Items", new Vector2(-1, -30), true, ImGuiWindowFlags.NoSavedSettings))
{
for (int i = 0; i < _configuration.ItemsAvailableForPurchase.Count; ++i)
{
uint itemId = _configuration.ItemsAvailableForPurchase[i];
ImGui.PushID($"###Item{i}");
ImGui.BeginDisabled(_configuration.ItemsAvailableForPurchase.Count == 1 && itemId == ItemIds.Venture);
ImGui.Selectable(_itemLookup[itemId].Name);
if (ImGui.BeginDragDropSource())
{
ImGui.SetDragDropPayload("DeliverooDragDrop", nint.Zero, 0);
_dragDropSource = itemId;
ImGui.EndDragDropSource();
}
if (ImGui.BeginDragDropTarget() &&
_dragDropSource > 0 &&
ImGui.AcceptDragDropPayload("DeliverooDragDrop").NativePtr != null)
{
itemToAdd = _dragDropSource;
indexToAdd = i;
ImGui.EndDragDropTarget();
_dragDropSource = 0;
}
ImGui.OpenPopupOnItemClick($"###ctx{i}", ImGuiPopupFlags.MouseButtonRight);
if (ImGui.BeginPopup($"###ctx{i}"))
{
if (ImGui.Selectable($"Remove {_itemLookup[itemId].Name}"))
itemToRemove = itemId;
ImGui.EndPopup();
}
ImGui.EndDisabled();
ImGui.PopID();
}
}
ImGui.EndChild();
if (itemToRemove != null)
{
_configuration.ItemsAvailableForPurchase.Remove(itemToRemove.Value);
_pluginInterface.SavePluginConfig(_configuration);
}
if (itemToAdd != null)
{
_configuration.ItemsAvailableForPurchase.Remove(itemToAdd.Value);
_configuration.ItemsAvailableForPurchase.Insert(indexToAdd, itemToAdd.Value);
_pluginInterface.SavePluginConfig(_configuration);
}
if (_plugin.GetGrandCompany() != GrandCompany.None)
{
List<(uint ItemId, string Name)> comboValues = _gcRewardsCache.Rewards[_plugin.GetGrandCompany()]
.Where(x => x.SubCategory == RewardSubCategory.Materials || x.SubCategory == RewardSubCategory.Materiel)
.Where(x => x.StackSize > 1)
.Where(x => !_configuration.ItemsAvailableForPurchase.Contains(x.ItemId))
.Select(x => (x.ItemId, x.Name))
.OrderBy(x => x.Name)
.ThenBy(x => x.GetHashCode())
.ToList();
comboValues.Insert(0, (0, ""));
int currentItem = 0;
if (ImGui.Combo("Add Item", ref currentItem, comboValues.Select(x => x.Name).ToArray(), comboValues.Count))
{
_configuration.ItemsAvailableForPurchase.Add(comboValues[currentItem].ItemId);
_pluginInterface.SavePluginConfig(_configuration);
}
}
else
{
int currentItem = 0;
ImGui.Combo("Add Item", ref currentItem, new string[] { "(Not part of a GC)" }, 1);
}
ImGui.Unindent(30);
}
}

View File

@ -0,0 +1,138 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Dalamud.Plugin;
using Deliveroo.GameData;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using ImGuiNET;
namespace Deliveroo.Windows;
internal sealed class TurnInWindow : Window
{
private readonly DeliverooPlugin _plugin;
private readonly DalamudPluginInterface _pluginInterface;
private readonly Configuration _configuration;
private readonly GcRewardsCache _gcRewardsCache;
private int _selectedAutoBuyItem = 0;
public TurnInWindow(DeliverooPlugin plugin, DalamudPluginInterface pluginInterface, Configuration configuration,
GcRewardsCache gcRewardsCache)
: base("GC Delivery###DeliverooTurnIn")
{
_plugin = plugin;
_pluginInterface = pluginInterface;
_configuration = configuration;
_gcRewardsCache = gcRewardsCache;
Position = new Vector2(100, 100);
PositionCondition = ImGuiCond.FirstUseEver;
Flags = ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoCollapse;
ShowCloseButton = false;
}
public bool State { get; set; }
public decimal Multiplier { private get; set; }
public uint SelectedItemId
{
get
{
if (_selectedAutoBuyItem == 0 || _selectedAutoBuyItem > _configuration.ItemsAvailableForPurchase.Count)
return 0;
return _configuration.ItemsAvailableForPurchase[_selectedAutoBuyItem - 1];
}
set
{
int index = _configuration.ItemsAvailableForPurchase.IndexOf(value);
if (index >= 0)
_selectedAutoBuyItem = index + 1;
else
_selectedAutoBuyItem = 0;
}
}
public GcRewardItem SelectedItem
{
get
{
uint selectedItemId = SelectedItemId;
if (selectedItemId == 0)
return GcRewardItem.None;
return _gcRewardsCache.Rewards[_plugin.GetGrandCompany()].Single(x => x.ItemId == selectedItemId);
}
}
public string Debug { get; set; }
public override void OnOpen()
{
SelectedItemId = _configuration.SelectedPurchaseItemId;
}
public override void Draw()
{
bool state = State;
if (ImGui.Checkbox("Handle GC turn ins/exchange automatically", ref state))
{
State = state;
}
ImGui.Indent(27);
if (Multiplier == 1m)
{
ImGui.TextColored(ImGuiColors.DalamudRed, "You do not have a buff active");
}
else
{
ImGui.TextColored(ImGuiColors.HealerGreen, $"Current Buff: {(Multiplier - 1m) * 100:N0}%%");
}
GrandCompany grandCompany = _plugin.GetGrandCompany();
if (grandCompany != GrandCompany.None) // not sure we should ever get here
{
ImGui.Spacing();
ImGui.BeginDisabled(state);
List<string> comboValues = new() { "Buy Nothing" };
foreach (var itemId in _configuration.ItemsAvailableForPurchase)
{
var name = _gcRewardsCache.Rewards[grandCompany].First(x => x.ItemId == itemId).Name;
int itemCount = GetItemCount(itemId);
if (itemCount > 0)
comboValues.Add($"{name} ({itemCount:N0})");
else
comboValues.Add(name);
}
if (ImGui.Combo("", ref _selectedAutoBuyItem, comboValues.ToArray(), comboValues.Count))
{
_configuration.SelectedPurchaseItemId = SelectedItemId;
_pluginInterface.SavePluginConfig(_configuration);
}
if (SelectedItem.IsValid() && SelectedItem.RequiredRank > _plugin.GetGrandCompanyRank())
ImGui.TextColored(ImGuiColors.DalamudRed, "Your rank isn't high enough to buy this item.");
ImGui.EndDisabled();
}
ImGui.Unindent(27);
ImGui.Separator();
ImGui.Text($"Debug (State): {Debug}");
}
private unsafe int GetItemCount(uint itemId)
{
InventoryManager* inventoryManager = InventoryManager.Instance();
return inventoryManager->GetInventoryItemCount(itemId, false, false, false);
}
}