Compare commits
No commits in common. "master" and "v2.9" have entirely different histories.
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
|||||||
[submodule "LLib"]
|
[submodule "LLib"]
|
||||||
path = LLib
|
path = LLib
|
||||||
url = https://git.carvel.li/liza/LLib.git
|
url = git@git.carvel.li:liza/LLib.git
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -11,16 +11,15 @@ internal sealed class CharacterConfiguration
|
|||||||
public string? CachedPlayerName { get; set; }
|
public string? CachedPlayerName { get; set; }
|
||||||
public string? CachedWorldName { get; set; }
|
public string? CachedWorldName { get; set; }
|
||||||
|
|
||||||
public bool DisableForCharacter { get; set; }
|
public bool DisableForCharacter { get; set; } = false;
|
||||||
public bool UseHideArmouryChestItemsFilter { get; set; }
|
public bool UseHideArmouryChestItemsFilter { get; set; } = false;
|
||||||
public bool IgnoreMinimumSealsToKeep { get; set; }
|
|
||||||
public bool OverrideItemsToPurchase { get; set; }
|
public bool OverrideItemsToPurchase { get; set; }
|
||||||
public List<Configuration.PurchasePriority> ItemsToPurchase { get; set; } = new();
|
public List<Configuration.PurchasePriority> ItemsToPurchase { get; set; } = new();
|
||||||
|
|
||||||
public static string ResolveFilePath(IDalamudPluginInterface pluginInterface, ulong localContentId)
|
public static string ResolveFilePath(DalamudPluginInterface pluginInterface, ulong localContentId)
|
||||||
=> Path.Join(pluginInterface.GetPluginConfigDirectory(), $"char.{localContentId:X}.json");
|
=> Path.Join(pluginInterface.GetPluginConfigDirectory(), $"char.{localContentId:X}.json");
|
||||||
|
|
||||||
public static CharacterConfiguration? Load(IDalamudPluginInterface pluginInterface, ulong localContentId)
|
public static CharacterConfiguration? Load(DalamudPluginInterface pluginInterface, ulong localContentId)
|
||||||
{
|
{
|
||||||
string path = ResolveFilePath(pluginInterface, localContentId);
|
string path = ResolveFilePath(pluginInterface, localContentId);
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
@ -29,11 +28,11 @@ internal sealed class CharacterConfiguration
|
|||||||
return JsonConvert.DeserializeObject<CharacterConfiguration>(File.ReadAllText(path));
|
return JsonConvert.DeserializeObject<CharacterConfiguration>(File.ReadAllText(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save(IDalamudPluginInterface pluginInterface)
|
public void Save(DalamudPluginInterface pluginInterface)
|
||||||
{
|
{
|
||||||
File.WriteAllText(ResolveFilePath(pluginInterface, LocalContentId), JsonConvert.SerializeObject(this, Formatting.Indented));
|
File.WriteAllText(ResolveFilePath(pluginInterface, LocalContentId), JsonConvert.SerializeObject(this, Formatting.Indented));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(IDalamudPluginInterface pluginInterface) =>
|
public void Delete(DalamudPluginInterface pluginInterface) =>
|
||||||
File.Delete(ResolveFilePath(pluginInterface, LocalContentId));
|
File.Delete(ResolveFilePath(pluginInterface, LocalContentId));
|
||||||
}
|
}
|
||||||
|
@ -1,92 +1,39 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using Dalamud.Configuration;
|
using Dalamud.Configuration;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Deliveroo.GameData;
|
using Deliveroo.GameData;
|
||||||
using LLib.ImGui;
|
|
||||||
|
|
||||||
namespace Deliveroo;
|
namespace Deliveroo;
|
||||||
|
|
||||||
internal sealed class Configuration : IPluginConfiguration
|
internal sealed class Configuration : IPluginConfiguration
|
||||||
{
|
{
|
||||||
public int Version { get; set; } = 2;
|
public int Version { get; set; } = 1;
|
||||||
|
|
||||||
[Obsolete]
|
public List<uint> ItemsAvailableForPurchase { get; set; } = new();
|
||||||
public List<uint> ItemsAvailableForPurchase { get; set; } = [];
|
public List<PurchasePriority> ItemsToPurchase { get; set; } = new();
|
||||||
|
|
||||||
public List<PurchaseOption> ItemsAvailableToPurchase { get; set; } = [];
|
public int ReservedSealCount { get; set; } = 0;
|
||||||
public List<PurchasePriority> ItemsToPurchase { get; set; } = [];
|
|
||||||
|
|
||||||
public int ReservedSealCount { get; set; }
|
/// <summary>
|
||||||
public bool ReserveDifferentSealCountAtMaxRank { get; set; }
|
/// A config-only setting, not exposed in the UI.
|
||||||
public int ReservedSealCountAtMaxRank { get; set; }
|
///
|
||||||
public XivChatType ChatType { get; set; } = XivChatType.Debug;
|
/// If set, buys all GC items in their max quantity (otherwise, everything except ventures is capped to 99).
|
||||||
public int PauseAtRank { get; set; }
|
/// </summary>
|
||||||
public EBehaviorOnOtherWorld BehaviorOnOtherWorld { get; set; } = EBehaviorOnOtherWorld.Warning;
|
public bool IgnoreCertainLimitations { get; set; } = false;
|
||||||
public bool DisableFrameLimiter { get; set; } = true;
|
|
||||||
public bool UncapFrameRate { get; set; }
|
|
||||||
public VirtualKey QuickTurnInKey { get; set; } = VirtualKey.SHIFT;
|
|
||||||
|
|
||||||
public MinimizableWindowConfig TurnInWindowConfig { get; } = new();
|
|
||||||
public WindowConfig ConfigWindowConfig { get; } = new();
|
|
||||||
|
|
||||||
internal sealed class PurchaseOption
|
|
||||||
{
|
|
||||||
public uint ItemId { get; set; }
|
|
||||||
public bool SameQuantityForAllLists { get; set; }
|
|
||||||
public int GlobalLimit { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class PurchasePriority
|
internal sealed class PurchasePriority
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
|
||||||
public Guid InternalId { get; } = Guid.NewGuid();
|
|
||||||
|
|
||||||
public uint ItemId { get; set; }
|
public uint ItemId { get; set; }
|
||||||
public int Limit { get; set; }
|
public int Limit { get; set; }
|
||||||
public bool Enabled { get; set; } = true;
|
|
||||||
public PurchaseType Type { get; set; } = PurchaseType.KeepStocked;
|
|
||||||
public bool CheckRetainerInventory { get; set; }
|
|
||||||
|
|
||||||
public string GetIcon()
|
|
||||||
{
|
|
||||||
return Type switch
|
|
||||||
{
|
|
||||||
PurchaseType.PurchaseOneTime => SeIconChar.BoxedNumber1.ToIconString(),
|
|
||||||
PurchaseType.KeepStocked => SeIconChar.Circle.ToIconString(),
|
|
||||||
_ => SeIconChar.BoxedQuestionMark.ToIconString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PurchaseType
|
|
||||||
{
|
|
||||||
PurchaseOneTime,
|
|
||||||
KeepStocked,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AddVentureIfNoItemToPurchaseSelected()
|
public bool AddVentureIfNoItemToPurchaseSelected()
|
||||||
{
|
{
|
||||||
if (ItemsAvailableToPurchase.Count == 0)
|
if (ItemsAvailableForPurchase.Count == 0)
|
||||||
{
|
{
|
||||||
ItemsAvailableToPurchase.Add(new PurchaseOption { ItemId = ItemIds.Venture });
|
ItemsAvailableForPurchase.Add(ItemIds.Venture);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum EBehaviorOnOtherWorld
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Warning,
|
|
||||||
DisableTurnIn,
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class MinimizableWindowConfig : WindowConfig
|
|
||||||
{
|
|
||||||
public bool IsMinimized { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project>
|
<Project>
|
||||||
<Target Name="PackagePluginDebug" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
|
<Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
|
||||||
<DalamudPackager
|
<DalamudPackager
|
||||||
ProjectDir="$(ProjectDir)"
|
ProjectDir="$(ProjectDir)"
|
||||||
OutputPath="$(OutputPath)"
|
OutputPath="$(OutputPath)"
|
||||||
|
@ -1,13 +1,63 @@
|
|||||||
<Project Sdk="Dalamud.NET.Sdk/11.0.0">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>6.3</Version>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
|
<Version>2.9</Version>
|
||||||
|
<LangVersion>11.0</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
<OutputPath>dist</OutputPath>
|
<OutputPath>dist</OutputPath>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="..\LLib\LLib.targets"/>
|
<PropertyGroup>
|
||||||
<Import Project="..\LLib\RenameZip.targets"/>
|
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
|
||||||
|
<DalamudLibPath>$(DALAMUD_HOME)/</DalamudLibPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\LLib\LLib.csproj" />
|
<ProjectReference Include="..\LLib\LLib.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Dalamud">
|
||||||
|
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ImGui.NET">
|
||||||
|
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Lumina">
|
||||||
|
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Lumina.Excel">
|
||||||
|
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json">
|
||||||
|
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FFXIVClientStructs">
|
||||||
|
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin">
|
||||||
|
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/>
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -4,5 +4,5 @@
|
|||||||
"Punchline": "Better Grand Company Deliveries",
|
"Punchline": "Better Grand Company Deliveries",
|
||||||
"Description": "",
|
"Description": "",
|
||||||
"RepoUrl": "https://git.carvel.li/liza/Deliveroo",
|
"RepoUrl": "https://git.carvel.li/liza/Deliveroo",
|
||||||
"IconUrl": "https://plugins.carvel.li/icons/Deliveroo.png"
|
"IconUrl": "https://git.carvel.li/liza/plugin-repo/raw/branch/master/dist/Deliveroo.png"
|
||||||
}
|
}
|
||||||
|
@ -1,85 +1,50 @@
|
|||||||
using System;
|
using System;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Deliveroo.GameData;
|
using Deliveroo.GameData;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using LLib.GameUI;
|
using LLib.GameUI;
|
||||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
|
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
|
||||||
|
|
||||||
namespace Deliveroo.Handlers;
|
namespace Deliveroo;
|
||||||
|
|
||||||
internal sealed class ExchangeHandler
|
partial class DeliverooPlugin
|
||||||
{
|
{
|
||||||
private readonly DeliverooPlugin _plugin;
|
private void InteractWithQuartermaster(GameObject personnelOfficer, GameObject quartermaster)
|
||||||
private readonly GameFunctions _gameFunctions;
|
|
||||||
private readonly ITargetManager _targetManager;
|
|
||||||
private readonly IGameGui _gameGui;
|
|
||||||
private readonly IChatGui _chatGui;
|
|
||||||
private readonly IPluginLog _pluginLog;
|
|
||||||
|
|
||||||
public ExchangeHandler(DeliverooPlugin plugin, GameFunctions gameFunctions, ITargetManager targetManager,
|
|
||||||
IGameGui gameGui, IChatGui chatGui, IPluginLog pluginLog)
|
|
||||||
{
|
{
|
||||||
_plugin = plugin;
|
if (GetCurrentSealCount() < _configuration.ReservedSealCount)
|
||||||
_gameFunctions = gameFunctions;
|
|
||||||
_targetManager = targetManager;
|
|
||||||
_gameGui = gameGui;
|
|
||||||
_chatGui = chatGui;
|
|
||||||
_pluginLog = pluginLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InteractWithQuartermaster(IGameObject personnelOfficer, IGameObject quartermaster)
|
|
||||||
{
|
|
||||||
if (_gameFunctions.GetCurrentSealCount() < _plugin.EffectiveReservedSealCount)
|
|
||||||
{
|
{
|
||||||
_plugin.CurrentStage = Stage.RequestStop;
|
CurrentStage = Stage.RequestStop;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_targetManager.Target == personnelOfficer)
|
if (_targetManager.Target == personnelOfficer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_gameFunctions.InteractWithTarget(quartermaster);
|
InteractWithTarget(quartermaster);
|
||||||
_plugin.CurrentStage = Stage.SelectRewardTier;
|
CurrentStage = Stage.SelectRewardTier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PurchaseItemRequest? GetNextItemToPurchase(PurchaseItemRequest? previousRequest = null)
|
private PurchaseItemRequest? GetNextItemToPurchase(PurchaseItemRequest? previousRequest = null)
|
||||||
{
|
{
|
||||||
foreach (PurchaseItemRequest request in _plugin.ItemsToPurchaseNow)
|
foreach (PurchaseItemRequest request in _itemsToPurchaseNow)
|
||||||
{
|
{
|
||||||
int toBuy = 0;
|
int offset = 0;
|
||||||
if (request == previousRequest)
|
if (request == previousRequest)
|
||||||
{
|
offset = (int)request.StackSize;
|
||||||
toBuy = (int)request.StackSize;
|
|
||||||
if (request.ItemId != ItemIds.Venture)
|
|
||||||
toBuy = Math.Min(toBuy, 99);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.Type == Configuration.PurchaseType.KeepStocked)
|
if (GetItemCount(request.ItemId) + offset < request.EffectiveLimit)
|
||||||
{
|
return request;
|
||||||
if (_gameFunctions.GetItemCount(request.ItemId, request.CheckRetainerInventory) + toBuy <
|
|
||||||
request.EffectiveLimit)
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (toBuy < request.EffectiveLimit)
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SelectRewardTier()
|
private unsafe void SelectRewardTier()
|
||||||
{
|
{
|
||||||
PurchaseItemRequest? item = GetNextItemToPurchase();
|
PurchaseItemRequest? item = GetNextItemToPurchase();
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
_plugin.CurrentStage = Stage.CloseGcExchange;
|
CurrentStage = Stage.CloseGcExchange;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,17 +65,17 @@ internal sealed class ExchangeHandler
|
|||||||
new() { Type = 0, Int = 0 }
|
new() { Type = 0, Int = 0 }
|
||||||
};
|
};
|
||||||
addonExchange->FireCallback(9, selectRank);
|
addonExchange->FireCallback(9, selectRank);
|
||||||
_plugin.ContinueAt = DateTime.Now.AddSeconds(0.5);
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
||||||
_plugin.CurrentStage = Stage.SelectRewardSubCategory;
|
CurrentStage = Stage.SelectRewardSubCategory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SelectRewardSubCategory()
|
private unsafe void SelectRewardSubCategory()
|
||||||
{
|
{
|
||||||
PurchaseItemRequest? item = GetNextItemToPurchase();
|
PurchaseItemRequest? item = GetNextItemToPurchase();
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
_plugin.CurrentStage = Stage.CloseGcExchange;
|
CurrentStage = Stage.CloseGcExchange;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,25 +96,25 @@ internal sealed class ExchangeHandler
|
|||||||
new() { Type = 0, Int = 0 }
|
new() { Type = 0, Int = 0 }
|
||||||
};
|
};
|
||||||
addonExchange->FireCallback(9, selectType);
|
addonExchange->FireCallback(9, selectType);
|
||||||
_plugin.ContinueAt = DateTime.Now.AddSeconds(0.5);
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
||||||
_plugin.CurrentStage = Stage.SelectReward;
|
CurrentStage = Stage.SelectReward;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SelectReward()
|
private unsafe void SelectReward()
|
||||||
{
|
{
|
||||||
if (_gameGui.TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) &&
|
if (_gameGui.TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) &&
|
||||||
LAddon.IsAddonReady(addonExchange))
|
LAddon.IsAddonReady(addonExchange))
|
||||||
{
|
{
|
||||||
if (SelectRewardItem(addonExchange))
|
if (SelectRewardItem(addonExchange))
|
||||||
{
|
{
|
||||||
_plugin.ContinueAt = DateTime.Now.AddSeconds(0.2);
|
_continueAt = DateTime.Now.AddSeconds(0.2);
|
||||||
_plugin.CurrentStage = Stage.ConfirmReward;
|
CurrentStage = Stage.ConfirmReward;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_plugin.ContinueAt = DateTime.Now.AddSeconds(0.2);
|
_continueAt = DateTime.Now.AddSeconds(0.2);
|
||||||
_plugin.CurrentStage = Stage.CloseGcExchange;
|
CurrentStage = Stage.CloseGcExchange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,15 +132,10 @@ internal sealed class ExchangeHandler
|
|||||||
if (itemId == item.ItemId)
|
if (itemId == item.ItemId)
|
||||||
{
|
{
|
||||||
_pluginLog.Information($"Selecting item {itemId}, {i}");
|
_pluginLog.Information($"Selecting item {itemId}, {i}");
|
||||||
long toBuy = (_gameFunctions.GetCurrentSealCount() - _plugin.EffectiveReservedSealCount) /
|
long toBuy = (GetCurrentSealCount() - _configuration.ReservedSealCount) / item.SealCost;
|
||||||
item.SealCost;
|
toBuy = Math.Min(toBuy, item.EffectiveLimit - GetItemCount(item.ItemId));
|
||||||
if (item.Type == Configuration.PurchaseType.KeepStocked)
|
|
||||||
toBuy = Math.Min(toBuy,
|
|
||||||
item.EffectiveLimit - _gameFunctions.GetItemCount(item.ItemId, item.CheckRetainerInventory));
|
|
||||||
else
|
|
||||||
toBuy = Math.Min(toBuy, item.EffectiveLimit);
|
|
||||||
|
|
||||||
if (item.ItemId != ItemIds.Venture)
|
if (item.ItemId != ItemIds.Venture && !_configuration.IgnoreCertainLimitations)
|
||||||
toBuy = Math.Min(toBuy, 99);
|
toBuy = Math.Min(toBuy, 99);
|
||||||
|
|
||||||
if (toBuy <= 0)
|
if (toBuy <= 0)
|
||||||
@ -184,10 +144,7 @@ internal sealed class ExchangeHandler
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
item.TemporaryPurchaseQuantity = toBuy;
|
_chatGui.Print($"Buying {toBuy}x {item.Name}...");
|
||||||
_chatGui.Print(new SeString(new TextPayload($"Buying {toBuy}x "))
|
|
||||||
.Append(SeString.CreateItemLink(item.ItemId))
|
|
||||||
.Append(new TextPayload("...")));
|
|
||||||
var selectReward = stackalloc AtkValue[]
|
var selectReward = stackalloc AtkValue[]
|
||||||
{
|
{
|
||||||
new() { Type = ValueType.Int, Int = 0 },
|
new() { Type = ValueType.Int, Int = 0 },
|
||||||
@ -209,24 +166,13 @@ internal sealed class ExchangeHandler
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void CloseGcExchange()
|
private unsafe void CloseGcExchange()
|
||||||
{
|
{
|
||||||
if (_gameGui.TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) &&
|
if (_gameGui.TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var addonExchange) &&
|
||||||
LAddon.IsAddonReady(addonExchange))
|
LAddon.IsAddonReady(addonExchange))
|
||||||
{
|
{
|
||||||
addonExchange->FireCallbackInt(-1);
|
addonExchange->FireCallbackInt(-1);
|
||||||
|
CurrentStage = Stage.TargetPersonnelOfficer;
|
||||||
// If we just turned in the final item, there's no need to talk to the personnel officer again
|
|
||||||
if (_plugin.LastTurnInListSize == 1)
|
|
||||||
{
|
|
||||||
_plugin.TurnInState = false;
|
|
||||||
_plugin.CurrentStage = Stage.RequestStop;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_plugin.ContinueAt = DateTime.Now.AddSeconds(1);
|
|
||||||
_plugin.CurrentStage = Stage.TargetPersonnelOfficer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
160
Deliveroo/DeliverooPlugin.GameFunctions.cs
Normal file
160
Deliveroo/DeliverooPlugin.GameFunctions.cs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using Dalamud.Memory;
|
||||||
|
using Deliveroo.GameData;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Control;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
using FFXIVClientStructs.FFXIV.Common.Math;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
|
namespace Deliveroo;
|
||||||
|
|
||||||
|
partial class DeliverooPlugin
|
||||||
|
{
|
||||||
|
private unsafe void InteractWithTarget(GameObject obj)
|
||||||
|
{
|
||||||
|
_pluginLog.Information($"Setting target to {obj}");
|
||||||
|
if (_targetManager.Target == null || _targetManager.Target != obj)
|
||||||
|
{
|
||||||
|
_targetManager.Target = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetSystem.Instance()->InteractWithObject(
|
||||||
|
(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)obj.Address, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe int GetCurrentSealCount()
|
||||||
|
{
|
||||||
|
InventoryManager* inventoryManager = InventoryManager.Instance();
|
||||||
|
switch ((GrandCompany)PlayerState.Instance()->GrandCompany)
|
||||||
|
{
|
||||||
|
case GrandCompany.Maelstrom:
|
||||||
|
return inventoryManager->GetInventoryItemCount(20, false, false, false);
|
||||||
|
case GrandCompany.TwinAdder:
|
||||||
|
return inventoryManager->GetInventoryItemCount(21, false, false, false);
|
||||||
|
case GrandCompany.ImmortalFlames:
|
||||||
|
return inventoryManager->GetInventoryItemCount(22, false, false, false);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal unsafe GrandCompany GetGrandCompany() => (GrandCompany)PlayerState.Instance()->GrandCompany;
|
||||||
|
|
||||||
|
internal unsafe byte GetGrandCompanyRank() => PlayerState.Instance()->GetGrandCompanyRank();
|
||||||
|
|
||||||
|
private float GetDistanceToNpc(int npcId, out GameObject? o)
|
||||||
|
{
|
||||||
|
foreach (var obj in _objectTable)
|
||||||
|
{
|
||||||
|
if (obj.ObjectKind == ObjectKind.EventNpc && obj is Character c)
|
||||||
|
{
|
||||||
|
if (GetNpcId(obj) == npcId)
|
||||||
|
{
|
||||||
|
o = obj;
|
||||||
|
return Vector3.Distance(_clientState.LocalPlayer?.Position ?? Vector3.Zero, c.Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
o = null;
|
||||||
|
return float.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetNpcId(GameObject obj)
|
||||||
|
{
|
||||||
|
return Marshal.ReadInt32(obj.Address + 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetPersonnelOfficerId()
|
||||||
|
{
|
||||||
|
return GetGrandCompany() switch
|
||||||
|
{
|
||||||
|
GrandCompany.Maelstrom => 0xF4B94,
|
||||||
|
GrandCompany.ImmortalFlames => 0xF4B97,
|
||||||
|
GrandCompany.TwinAdder => 0xF4B9A,
|
||||||
|
_ => int.MaxValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetQuartermasterId()
|
||||||
|
{
|
||||||
|
return GetGrandCompany() switch
|
||||||
|
{
|
||||||
|
GrandCompany.Maelstrom => 0xF4B93,
|
||||||
|
GrandCompany.ImmortalFlames => 0xF4B96,
|
||||||
|
GrandCompany.TwinAdder => 0xF4B99,
|
||||||
|
_ => int.MaxValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint GetSealCap() => _sealCaps.TryGetValue(GetGrandCompanyRank(), out var cap) ? cap : 0;
|
||||||
|
|
||||||
|
public unsafe int GetItemCount(uint itemId)
|
||||||
|
{
|
||||||
|
InventoryManager* inventoryManager = InventoryManager.Instance();
|
||||||
|
return inventoryManager->GetInventoryItemCount(itemId, false, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private decimal GetSealMultiplier()
|
||||||
|
{
|
||||||
|
// priority seal allowance
|
||||||
|
if (_clientState.LocalPlayer!.StatusList.Any(x => x.StatusId == 1078))
|
||||||
|
return 1.15m;
|
||||||
|
|
||||||
|
// seal sweetener 1/2
|
||||||
|
var fcStatus = _clientState.LocalPlayer!.StatusList.FirstOrDefault(x => x.StatusId == 414);
|
||||||
|
if (fcStatus != null)
|
||||||
|
{
|
||||||
|
return 1m + fcStatus.StackCount / 100m;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This returns ALL items that can be turned in, regardless of filter settings.
|
||||||
|
/// </summary>
|
||||||
|
private unsafe List<TurnInItem> BuildTurnInList(AgentGrandCompanySupply* agent)
|
||||||
|
{
|
||||||
|
List<TurnInItem> list = new();
|
||||||
|
for (int i = 11 /* skip over provisioning items */; i < agent->NumItems; ++i)
|
||||||
|
{
|
||||||
|
GrandCompanyItem item = agent->ItemArray[i];
|
||||||
|
|
||||||
|
// this includes all items, even if they don't match the filter
|
||||||
|
list.Add(new TurnInItem
|
||||||
|
{
|
||||||
|
ItemId = Marshal.ReadInt32(new nint(&item) + 132),
|
||||||
|
Name = MemoryHelper.ReadSeString(&item.ItemName).ToString(),
|
||||||
|
SealsWithBonus = (int)Math.Round(item.SealReward * GetSealMultiplier(), MidpointRounding.AwayFromZero),
|
||||||
|
SealsWithoutBonus = item.SealReward,
|
||||||
|
ItemUiCategory = Marshal.ReadByte(new nint(&item) + 150),
|
||||||
|
});
|
||||||
|
|
||||||
|
// GrandCompanyItem + 104 = [int] InventoryType
|
||||||
|
// GrandCompanyItem + 108 = [int] ??
|
||||||
|
// GrandCompanyItem + 124 = [int] <Item's Column 19 in the sheet, but that has no name>
|
||||||
|
// GrandCompanyItem + 132 = [int] itemId
|
||||||
|
// GrandCompanyItem + 136 = [int] 0 (always)?
|
||||||
|
// GrandCompanyItem + 140 = [int] i (item's own position within the unsorted list)
|
||||||
|
// GrandCompanyItem + 148 = [short] ilvl
|
||||||
|
// GrandCompanyItem + 150 = [byte] ItemUICategory
|
||||||
|
// GrandCompanyItem + 151 = [byte] (unchecked) inventory slot in container
|
||||||
|
// GrandCompanyItem + 152 = [short] 512 (always)?
|
||||||
|
// int itemId = Marshal.ReadInt32(new nint(&item) + 132);
|
||||||
|
// PluginLog.Verbose($" {Marshal.ReadInt32(new nint(&item) + 132)};;;; {MemoryHelper.ReadSeString(&item.ItemName)}, {new nint(&agent->ItemArray[i]):X8}, {item.SealReward}, {item.IsTurnInAvailable}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.OrderByDescending(x => x.SealsWithBonus)
|
||||||
|
.ThenBy(x => x.ItemUiCategory)
|
||||||
|
.ThenBy(x => x.ItemId)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Game.Addon.Lifecycle;
|
|
||||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
|
||||||
using Dalamud.Game.ClientState.Keys;
|
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
|
||||||
using Deliveroo.GameData;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
|
||||||
using LLib.GameUI;
|
|
||||||
|
|
||||||
namespace Deliveroo;
|
|
||||||
|
|
||||||
partial class DeliverooPlugin
|
|
||||||
{
|
|
||||||
private unsafe void GrandCompanySupplyRewardPostSetup(AddonEvent type, AddonArgs args)
|
|
||||||
{
|
|
||||||
bool quickTurnIn = CurrentStage == Stage.Stopped && _configuration.QuickTurnInKey != VirtualKey.NO_KEY && _keyState[_configuration.QuickTurnInKey];
|
|
||||||
if (CurrentStage == Stage.TurnInSelected || quickTurnIn)
|
|
||||||
{
|
|
||||||
AddonGrandCompanySupplyReward* addonSupplyReward = (AddonGrandCompanySupplyReward*)args.Addon;
|
|
||||||
|
|
||||||
string? itemName = addonSupplyReward->AtkUnitBase.AtkValues[4].ReadAtkString();
|
|
||||||
if (itemName != null && _itemCache.GetItemIdFromItemName(itemName)
|
|
||||||
.Any(itemId => InternalConfiguration.QuickVentureExclusiveItems.Contains(itemId)))
|
|
||||||
{
|
|
||||||
DeliveryResult = new MessageDeliveryResult
|
|
||||||
{
|
|
||||||
Message = new SeStringBuilder()
|
|
||||||
.Append("Won't turn in ")
|
|
||||||
.AddItemLink(_itemCache.GetItemIdFromItemName(itemName).First())
|
|
||||||
.Append(", as it can only be obtained through Quick Ventures.")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
|
|
||||||
addonSupplyReward->AtkUnitBase.FireCallbackInt(1);
|
|
||||||
if (quickTurnIn)
|
|
||||||
CurrentStage = Stage.RequestStop;
|
|
||||||
else
|
|
||||||
CurrentStage = Stage.CloseGcSupplyWindowThenStop;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_pluginLog.Information($"Turning in '{itemName}'");
|
|
||||||
|
|
||||||
addonSupplyReward->AtkUnitBase.FireCallbackInt(0);
|
|
||||||
ContinueAt = DateTime.Now.AddSeconds(0.58);
|
|
||||||
if (quickTurnIn)
|
|
||||||
{
|
|
||||||
DeliveryResult = new NoDeliveryResult();
|
|
||||||
CurrentStage = Stage.SingleFinalizeTurnIn;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CurrentStage = Stage.FinalizeTurnIn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,36 +9,31 @@ namespace Deliveroo;
|
|||||||
partial class DeliverooPlugin
|
partial class DeliverooPlugin
|
||||||
{
|
{
|
||||||
private unsafe void SelectStringPostSetup(AddonEvent type, AddonArgs args)
|
private unsafe void SelectStringPostSetup(AddonEvent type, AddonArgs args)
|
||||||
{
|
|
||||||
AddonSelectString* addonSelectString = (AddonSelectString*)args.Addon;
|
|
||||||
SelectStringPostSetup(addonSelectString, CurrentStage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe bool SelectStringPostSetup(AddonSelectString* addonSelectString, Stage stage)
|
|
||||||
{
|
{
|
||||||
_pluginLog.Verbose("SelectString post-setup");
|
_pluginLog.Verbose("SelectString post-setup");
|
||||||
|
|
||||||
string desiredText;
|
string desiredText;
|
||||||
Action followUp;
|
Action followUp;
|
||||||
if (stage == Stage.OpenGcSupply)
|
if (CurrentStage == Stage.OpenGcSupply)
|
||||||
{
|
{
|
||||||
desiredText = _gameStrings.UndertakeSupplyAndProvisioningMission;
|
desiredText = _gameStrings.UndertakeSupplyAndProvisioningMission;
|
||||||
followUp = OpenGcSupplySelectStringFollowUp;
|
followUp = OpenGcSupplyFollowUp;
|
||||||
}
|
}
|
||||||
else if (stage == Stage.CloseGcSupplySelectString)
|
else if (CurrentStage == Stage.CloseGcSupply)
|
||||||
{
|
{
|
||||||
desiredText = _gameStrings.ClosePersonnelOfficerTalk;
|
desiredText = _gameStrings.ClosePersonnelOfficerTalk;
|
||||||
followUp = CloseGcSupplySelectStringFollowUp;
|
followUp = CloseGcSupplyFollowUp;
|
||||||
}
|
}
|
||||||
else if (stage == Stage.CloseGcSupplySelectStringThenStop)
|
else if (CurrentStage == Stage.CloseGcSupplyThenStop)
|
||||||
{
|
{
|
||||||
desiredText = _gameStrings.ClosePersonnelOfficerTalk;
|
desiredText = _gameStrings.ClosePersonnelOfficerTalk;
|
||||||
followUp = CloseGcSupplySelectStringThenStopFollowUp;
|
followUp = CloseGcSupplyThenCloseFollowUp;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
_pluginLog.Verbose($"Looking for '{desiredText}' in prompt");
|
_pluginLog.Verbose($"Looking for '{desiredText}' in prompt");
|
||||||
|
AddonSelectString* addonSelectString = (AddonSelectString*)args.Addon;
|
||||||
int entries = addonSelectString->PopupMenu.PopupMenu.EntryCount;
|
int entries = addonSelectString->PopupMenu.PopupMenu.EntryCount;
|
||||||
|
|
||||||
for (int i = 0; i < entries; ++i)
|
for (int i = 0; i < entries; ++i)
|
||||||
@ -51,27 +46,27 @@ partial class DeliverooPlugin
|
|||||||
_pluginLog.Verbose($" Choice {i} → {text}");
|
_pluginLog.Verbose($" Choice {i} → {text}");
|
||||||
if (text == desiredText)
|
if (text == desiredText)
|
||||||
{
|
{
|
||||||
|
|
||||||
_pluginLog.Information($"Selecting choice {i} ({text})");
|
_pluginLog.Information($"Selecting choice {i} ({text})");
|
||||||
addonSelectString->AtkUnitBase.FireCallbackInt(i);
|
addonSelectString->AtkUnitBase.FireCallbackInt(i);
|
||||||
|
|
||||||
followUp();
|
followUp();
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_pluginLog.Verbose($"Text '{desiredText}' was not found in prompt.");
|
_pluginLog.Verbose($"Text '{desiredText}' was not found in prompt.");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenGcSupplySelectStringFollowUp()
|
private void OpenGcSupplyFollowUp()
|
||||||
{
|
{
|
||||||
_supplyHandler.ResetTurnInErrorHandling();
|
ResetTurnInErrorHandling();
|
||||||
CurrentStage = Stage.SelectExpertDeliveryTab;
|
CurrentStage = Stage.SelectExpertDeliveryTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CloseGcSupplySelectStringFollowUp()
|
private void CloseGcSupplyFollowUp()
|
||||||
{
|
{
|
||||||
if (_exchangeHandler.GetNextItemToPurchase() == null)
|
if (GetNextItemToPurchase() == null)
|
||||||
{
|
{
|
||||||
_turnInWindow.State = false;
|
_turnInWindow.State = false;
|
||||||
CurrentStage = Stage.RequestStop;
|
CurrentStage = Stage.RequestStop;
|
||||||
@ -79,27 +74,27 @@ partial class DeliverooPlugin
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// you can occasionally get a 'not enough seals' warning lol
|
// you can occasionally get a 'not enough seals' warning lol
|
||||||
ContinueAt = DateTime.Now.AddSeconds(1);
|
_continueAt = DateTime.Now.AddSeconds(1);
|
||||||
CurrentStage = Stage.TargetQuartermaster;
|
CurrentStage = Stage.TargetQuartermaster;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CloseGcSupplySelectStringThenStopFollowUp()
|
private void CloseGcSupplyThenCloseFollowUp()
|
||||||
{
|
{
|
||||||
if (_exchangeHandler.GetNextItemToPurchase() == null)
|
if (GetNextItemToPurchase() == null)
|
||||||
{
|
{
|
||||||
_turnInWindow.State = false;
|
_turnInWindow.State = false;
|
||||||
CurrentStage = Stage.RequestStop;
|
CurrentStage = Stage.RequestStop;
|
||||||
}
|
}
|
||||||
else if (_gameFunctions.GetCurrentSealCount() <=
|
else if (GetCurrentSealCount() <=
|
||||||
EffectiveReservedSealCount + _exchangeHandler.GetNextItemToPurchase()!.SealCost)
|
_configuration.ReservedSealCount + GetNextItemToPurchase()!.SealCost)
|
||||||
{
|
{
|
||||||
_turnInWindow.State = false;
|
_turnInWindow.State = false;
|
||||||
CurrentStage = Stage.RequestStop;
|
CurrentStage = Stage.RequestStop;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ContinueAt = DateTime.Now.AddSeconds(1);
|
_continueAt = DateTime.Now.AddSeconds(1);
|
||||||
CurrentStage = Stage.TargetQuartermaster;
|
CurrentStage = Stage.TargetQuartermaster;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using Dalamud.Game.Addon.Lifecycle;
|
using Dalamud.Game.Addon.Lifecycle;
|
||||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
|
||||||
@ -14,13 +13,13 @@ partial class DeliverooPlugin
|
|||||||
_pluginLog.Verbose("SelectYesNo post-setup");
|
_pluginLog.Verbose("SelectYesNo post-setup");
|
||||||
|
|
||||||
AddonSelectYesno* addonSelectYesNo = (AddonSelectYesno*)args.Addon;
|
AddonSelectYesno* addonSelectYesNo = (AddonSelectYesno*)args.Addon;
|
||||||
string text = MemoryHelper.ReadSeString(&addonSelectYesNo->PromptText->NodeText).ToString().ReplaceLineEndings("");
|
string text = MemoryHelper.ReadSeString(&addonSelectYesNo->PromptText->NodeText).ToString().Replace("\n", "").Replace("\r", "");
|
||||||
_pluginLog.Verbose($"YesNo prompt: '{text}'");
|
_pluginLog.Verbose($"YesNo prompt: '{text}'");
|
||||||
|
|
||||||
if (CurrentStage == Stage.ConfirmReward &&
|
if (CurrentStage == Stage.ConfirmReward &&
|
||||||
_gameStrings.ExchangeItems.IsMatch(text))
|
_gameStrings.ExchangeItems.IsMatch(text))
|
||||||
{
|
{
|
||||||
PurchaseItemRequest? item = _exchangeHandler.GetNextItemToPurchase();
|
PurchaseItemRequest? item = GetNextItemToPurchase();
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
addonSelectYesNo->AtkUnitBase.FireCallbackInt(1);
|
addonSelectYesNo->AtkUnitBase.FireCallbackInt(1);
|
||||||
@ -28,20 +27,17 @@ partial class DeliverooPlugin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_pluginLog.Information($"Selecting 'yes' ({text}) (callback = {item.OnPurchase}, qty = {item.TemporaryPurchaseQuantity})");
|
_pluginLog.Information($"Selecting 'yes' ({text})");
|
||||||
addonSelectYesNo->AtkUnitBase.FireCallbackInt(0);
|
addonSelectYesNo->AtkUnitBase.FireCallbackInt(0);
|
||||||
|
|
||||||
item.OnPurchase?.Invoke((int)item.TemporaryPurchaseQuantity);
|
var nextItem = GetNextItemToPurchase(item);
|
||||||
item.TemporaryPurchaseQuantity = 0;
|
if (nextItem != null && GetCurrentSealCount() >= _configuration.ReservedSealCount + nextItem.SealCost)
|
||||||
|
|
||||||
var nextItem = _exchangeHandler.GetNextItemToPurchase(item);
|
|
||||||
if (nextItem != null && _gameFunctions.GetCurrentSealCount() >= EffectiveReservedSealCount + nextItem.SealCost)
|
|
||||||
CurrentStage = Stage.SelectRewardTier;
|
CurrentStage = Stage.SelectRewardTier;
|
||||||
else
|
else
|
||||||
CurrentStage = Stage.CloseGcExchange;
|
CurrentStage = Stage.CloseGcExchange;
|
||||||
ContinueAt = DateTime.Now.AddSeconds(0.5);
|
_continueAt = DateTime.Now.AddSeconds(0.5);
|
||||||
}
|
}
|
||||||
else if ((CurrentStage == Stage.TurnInSelected || (_configuration.QuickTurnInKey != VirtualKey.NO_KEY && _keyState[_configuration.QuickTurnInKey])) &&
|
else if (CurrentStage == Stage.TurnInSelected &&
|
||||||
_gameStrings.TradeHighQualityItem == text)
|
_gameStrings.TradeHighQualityItem == text)
|
||||||
{
|
{
|
||||||
_pluginLog.Information($"Selecting 'yes' ({text})");
|
_pluginLog.Information($"Selecting 'yes' ({text})");
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Deliveroo.GameData;
|
using Deliveroo.GameData;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
@ -10,43 +8,25 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
|
|||||||
using LLib.GameUI;
|
using LLib.GameUI;
|
||||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
|
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
|
||||||
|
|
||||||
namespace Deliveroo.Handlers;
|
namespace Deliveroo;
|
||||||
|
|
||||||
internal sealed class SupplyHandler
|
partial class DeliverooPlugin
|
||||||
{
|
{
|
||||||
private readonly DeliverooPlugin _plugin;
|
private void InteractWithPersonnelOfficer(GameObject personnelOfficer, GameObject quartermaster)
|
||||||
private readonly GameFunctions _gameFunctions;
|
|
||||||
private readonly ITargetManager _targetManager;
|
|
||||||
private readonly IGameGui _gameGui;
|
|
||||||
private readonly IPluginLog _pluginLog;
|
|
||||||
|
|
||||||
private uint _turnInErrors;
|
|
||||||
|
|
||||||
public SupplyHandler(DeliverooPlugin plugin, GameFunctions gameFunctions, ITargetManager targetManager,
|
|
||||||
IGameGui gameGui, IPluginLog pluginLog)
|
|
||||||
{
|
{
|
||||||
_plugin = plugin;
|
if (_targetManager.Target == quartermaster)
|
||||||
_gameFunctions = gameFunctions;
|
|
||||||
_targetManager = targetManager;
|
|
||||||
_gameGui = gameGui;
|
|
||||||
_pluginLog = pluginLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InteractWithPersonnelOfficer(IGameObject personnelOfficer, IGameObject quartermaster)
|
|
||||||
{
|
|
||||||
if (_targetManager.Target?.EntityId == quartermaster.EntityId)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_gameFunctions.InteractWithTarget(personnelOfficer);
|
InteractWithTarget(personnelOfficer);
|
||||||
_plugin.CurrentStage = Stage.OpenGcSupply;
|
CurrentStage = Stage.OpenGcSupply;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SelectExpertDeliveryTab()
|
private unsafe void SelectExpertDeliveryTab()
|
||||||
{
|
{
|
||||||
var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.GrandCompanySupply);
|
var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.GrandCompanySupply);
|
||||||
if (agentInterface != null && agentInterface->IsAgentActive())
|
if (agentInterface != null && agentInterface->IsAgentActive())
|
||||||
{
|
{
|
||||||
var addonId = agentInterface->GetAddonId();
|
var addonId = agentInterface->GetAddonID();
|
||||||
if (addonId == 0)
|
if (addonId == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -60,7 +40,7 @@ internal sealed class SupplyHandler
|
|||||||
{
|
{
|
||||||
_pluginLog.Information("Tab already selected, probably due to haseltweaks");
|
_pluginLog.Information("Tab already selected, probably due to haseltweaks");
|
||||||
ResetTurnInErrorHandling();
|
ResetTurnInErrorHandling();
|
||||||
_plugin.CurrentStage = Stage.SelectItemToTurnIn;
|
CurrentStage = Stage.SelectItemToTurnIn;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,23 +53,23 @@ internal sealed class SupplyHandler
|
|||||||
};
|
};
|
||||||
addon->FireCallback(3, selectExpertDeliveryTab);
|
addon->FireCallback(3, selectExpertDeliveryTab);
|
||||||
ResetTurnInErrorHandling();
|
ResetTurnInErrorHandling();
|
||||||
_plugin.CurrentStage = Stage.SelectItemToTurnIn;
|
CurrentStage = Stage.SelectItemToTurnIn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetTurnInErrorHandling(int listSize = int.MaxValue)
|
private void ResetTurnInErrorHandling(int listSize = int.MaxValue)
|
||||||
{
|
{
|
||||||
_pluginLog.Verbose("Resetting error handling state");
|
_pluginLog.Verbose("Resetting error handling state");
|
||||||
_plugin.LastTurnInListSize = listSize;
|
_lastTurnInListSize = listSize;
|
||||||
_turnInErrors = 0;
|
_turnInErrors = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SelectItemToTurnIn()
|
private unsafe void SelectItemToTurnIn()
|
||||||
{
|
{
|
||||||
var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.GrandCompanySupply);
|
var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.GrandCompanySupply);
|
||||||
if (agentInterface != null && agentInterface->IsAgentActive())
|
if (agentInterface != null && agentInterface->IsAgentActive())
|
||||||
{
|
{
|
||||||
var addonId = agentInterface->GetAddonId();
|
var addonId = agentInterface->GetAddonID();
|
||||||
if (addonId == 0)
|
if (addonId == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -98,30 +78,28 @@ internal sealed class SupplyHandler
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var addonGc = (AddonGrandCompanySupplyList*)addon;
|
var addonGc = (AddonGrandCompanySupplyList*)addon;
|
||||||
if (addonGc->ExpertDeliveryList == null ||
|
if (addonGc->ExpertDeliveryList == null || !addonGc->ExpertDeliveryList->AtkComponentBase.OwnerNode->AtkResNode.IsVisible)
|
||||||
!addonGc->ExpertDeliveryList->AtkComponentBase.OwnerNode->AtkResNode.IsVisible())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (addonGc->SelectedTab != 2)
|
if (addonGc->SelectedTab != 2)
|
||||||
{
|
{
|
||||||
_plugin.TurnInError = "Wrong tab selected";
|
_turnInWindow.Error = "Wrong tab selected";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemFilterType configuredFilter = ResolveSelectedSupplyFilter();
|
ItemFilterType configuredFilter = ResolveSelectedSupplyFilter();
|
||||||
if (addonGc->SelectedFilter == 0 || addonGc->SelectedFilter != (int)configuredFilter)
|
if (addonGc->SelectedFilter == 0 || addonGc->SelectedFilter != (int)configuredFilter)
|
||||||
{
|
{
|
||||||
_plugin.TurnInError =
|
_turnInWindow.Error =
|
||||||
$"Wrong filter selected (expected {configuredFilter}, but is {(ItemFilterType)addonGc->SelectedFilter})";
|
$"Wrong filter selected (expected {configuredFilter}, but is {(ItemFilterType)addonGc->SelectedFilter})";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int currentListSize = addonGc->ExpertDeliveryList->ListLength;
|
int currentListSize = addonGc->ExpertDeliveryList->ListLength;
|
||||||
if (addonGc->ListEmptyTextNode->AtkResNode.IsVisible() || currentListSize == 0)
|
if (addonGc->ListEmptyTextNode->AtkResNode.IsVisible || currentListSize == 0)
|
||||||
{
|
{
|
||||||
_pluginLog.Information(
|
_pluginLog.Information($"No items to turn in {addonGc->ListEmptyTextNode->AtkResNode.IsVisible}, {currentListSize})");
|
||||||
$"No items to turn in ({addonGc->ListEmptyTextNode->AtkResNode.IsVisible}, {currentListSize})");
|
CurrentStage = Stage.CloseGcSupplyThenStop;
|
||||||
_plugin.CurrentStage = Stage.CloseGcSupplySelectStringThenStop;
|
|
||||||
addon->FireCallbackInt(-1);
|
addon->FireCallbackInt(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -130,29 +108,28 @@ internal sealed class SupplyHandler
|
|||||||
// something is wrong.
|
// something is wrong.
|
||||||
if (_turnInErrors > 10)
|
if (_turnInErrors > 10)
|
||||||
{
|
{
|
||||||
_plugin.TurnInError = "Unable to refresh item list";
|
_turnInWindow.Error = "Unable to refresh item list";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentListSize >= _plugin.LastTurnInListSize)
|
if (currentListSize >= _lastTurnInListSize)
|
||||||
{
|
{
|
||||||
_turnInErrors++;
|
_turnInErrors++;
|
||||||
_pluginLog.Information(
|
_pluginLog.Information($"Trying to refresh expert delivery list manually ({_turnInErrors}, old list size = {_lastTurnInListSize}, new list size = {currentListSize})...");
|
||||||
$"Trying to refresh expert delivery list manually ({_turnInErrors}, old list size = {_plugin.LastTurnInListSize}, new list size = {currentListSize})...");
|
|
||||||
addon->FireCallbackInt(2);
|
addon->FireCallbackInt(2);
|
||||||
|
|
||||||
_plugin.ContinueAt = DateTime.Now.AddSeconds(0.1);
|
_continueAt = DateTime.Now.AddSeconds(0.1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetTurnInErrorHandling(currentListSize);
|
ResetTurnInErrorHandling(currentListSize);
|
||||||
|
|
||||||
var agent = (AgentGrandCompanySupply*)agentInterface;
|
var agent = (AgentGrandCompanySupply*)agentInterface;
|
||||||
List<TurnInItem> items = _gameFunctions.BuildTurnInList(agent);
|
List<TurnInItem> items = BuildTurnInList(agent);
|
||||||
if (items.Count == 0)
|
if (items.Count == 0)
|
||||||
{
|
{
|
||||||
// probably shouldn't happen with the previous node visibility check
|
// probably shouldn't happen with the previous node visibility check
|
||||||
_plugin.CurrentStage = Stage.CloseGcSupplySelectStringThenStop;
|
CurrentStage = Stage.CloseGcSupplyThenStop;
|
||||||
addon->FireCallbackInt(-1);
|
addon->FireCallbackInt(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -176,9 +153,9 @@ internal sealed class SupplyHandler
|
|||||||
// ---------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------
|
||||||
// TODO If we ever manage to obtain a mapping name to itemId here, we can try and exclude e.g. Red Onion
|
// TODO If we ever manage to obtain a mapping name to itemId here, we can try and exclude e.g. Red Onion
|
||||||
// Helms from being turned in.
|
// Helms from being turned in.
|
||||||
if (_gameFunctions.GetCurrentSealCount() + items[0].SealsWithBonus > _gameFunctions.GetSealCap())
|
if (GetCurrentSealCount() + items[0].SealsWithBonus > GetSealCap())
|
||||||
{
|
{
|
||||||
_plugin.CurrentStage = Stage.CloseGcSupplySelectString;
|
CurrentStage = Stage.CloseGcSupply;
|
||||||
addon->FireCallbackInt(-1);
|
addon->FireCallbackInt(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -190,11 +167,24 @@ internal sealed class SupplyHandler
|
|||||||
new() { Type = 0, Int = 0 }
|
new() { Type = 0, Int = 0 }
|
||||||
};
|
};
|
||||||
addon->FireCallback(3, selectFirstItem);
|
addon->FireCallback(3, selectFirstItem);
|
||||||
_plugin.CurrentStage = Stage.TurnInSelected;
|
CurrentStage = Stage.TurnInSelected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void FinalizeTurnInItem()
|
private unsafe void TurnInSelectedItem()
|
||||||
|
{
|
||||||
|
if (_gameGui.TryGetAddonByName<AddonGrandCompanySupplyReward>("GrandCompanySupplyReward",
|
||||||
|
out var addonSupplyReward) && LAddon.IsAddonReady(&addonSupplyReward->AtkUnitBase))
|
||||||
|
{
|
||||||
|
_pluginLog.Information($"Turning in '{addonSupplyReward->AtkUnitBase.AtkValues[4].ReadAtkString()}'");
|
||||||
|
|
||||||
|
addonSupplyReward->AtkUnitBase.FireCallbackInt(0);
|
||||||
|
_continueAt = DateTime.Now.AddSeconds(0.58);
|
||||||
|
CurrentStage = Stage.FinalizeTurnIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void FinalizeTurnInItem()
|
||||||
{
|
{
|
||||||
if (_gameGui.TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList",
|
if (_gameGui.TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList",
|
||||||
out var addonSupplyList) && LAddon.IsAddonReady(&addonSupplyList->AtkUnitBase))
|
out var addonSupplyList) && LAddon.IsAddonReady(&addonSupplyList->AtkUnitBase))
|
||||||
@ -206,36 +196,15 @@ internal sealed class SupplyHandler
|
|||||||
new() { Type = 0, Int = 0 }
|
new() { Type = 0, Int = 0 }
|
||||||
};
|
};
|
||||||
addonSupplyList->AtkUnitBase.FireCallback(3, updateFilter);
|
addonSupplyList->AtkUnitBase.FireCallback(3, updateFilter);
|
||||||
if (_plugin.CurrentStage == Stage.FinalizeTurnIn)
|
CurrentStage = Stage.SelectItemToTurnIn;
|
||||||
_plugin.CurrentStage = Stage.SelectItemToTurnIn;
|
|
||||||
else
|
|
||||||
_plugin.CurrentStage = Stage.RequestStop;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemFilterType ResolveSelectedSupplyFilter()
|
private ItemFilterType ResolveSelectedSupplyFilter()
|
||||||
{
|
{
|
||||||
if (_plugin.CharacterConfiguration is { UseHideArmouryChestItemsFilter: true })
|
if (CharacterConfiguration is { UseHideArmouryChestItemsFilter: true })
|
||||||
return ItemFilterType.HideArmouryChestItems;
|
return ItemFilterType.HideArmouryChestItems;
|
||||||
|
|
||||||
return ItemFilterType.HideGearSetItems;
|
return ItemFilterType.HideGearSetItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void CloseGcSupplyWindow()
|
|
||||||
{
|
|
||||||
var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.GrandCompanySupply);
|
|
||||||
if (agentInterface != null && agentInterface->IsAgentActive())
|
|
||||||
{
|
|
||||||
var addonId = agentInterface->GetAddonId();
|
|
||||||
if (addonId == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AtkUnitBase* addon = LAddon.GetAddonById(addonId);
|
|
||||||
if (addon == null || !LAddon.IsAddonReady(addon))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_plugin.CurrentStage = Stage.CloseGcSupplySelectStringThenStop;
|
|
||||||
addon->FireCallbackInt(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -6,19 +6,16 @@ using Dalamud.Game.ClientState.Conditions;
|
|||||||
using Dalamud.Game.ClientState.Objects;
|
using Dalamud.Game.ClientState.Objects;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Deliveroo.External;
|
using Deliveroo.External;
|
||||||
using Deliveroo.GameData;
|
using Deliveroo.GameData;
|
||||||
using Deliveroo.Handlers;
|
|
||||||
using Deliveroo.Windows;
|
using Deliveroo.Windows;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using LLib;
|
|
||||||
using LLib.GameUI;
|
using LLib.GameUI;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
namespace Deliveroo;
|
namespace Deliveroo;
|
||||||
|
|
||||||
@ -26,81 +23,70 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
{
|
{
|
||||||
private readonly WindowSystem _windowSystem = new(typeof(DeliverooPlugin).AssemblyQualifiedName);
|
private readonly WindowSystem _windowSystem = new(typeof(DeliverooPlugin).AssemblyQualifiedName);
|
||||||
|
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
private readonly IChatGui _chatGui;
|
private readonly IChatGui _chatGui;
|
||||||
private readonly IGameGui _gameGui;
|
private readonly IGameGui _gameGui;
|
||||||
private readonly IFramework _framework;
|
private readonly IFramework _framework;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
|
private readonly IObjectTable _objectTable;
|
||||||
|
private readonly ITargetManager _targetManager;
|
||||||
private readonly ICondition _condition;
|
private readonly ICondition _condition;
|
||||||
private readonly ICommandManager _commandManager;
|
private readonly ICommandManager _commandManager;
|
||||||
private readonly IPluginLog _pluginLog;
|
private readonly IPluginLog _pluginLog;
|
||||||
private readonly IAddonLifecycle _addonLifecycle;
|
private readonly IAddonLifecycle _addonLifecycle;
|
||||||
private readonly IKeyState _keyState;
|
|
||||||
|
|
||||||
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
|
|
||||||
private readonly GameStrings _gameStrings;
|
private readonly GameStrings _gameStrings;
|
||||||
private readonly GameFunctions _gameFunctions;
|
|
||||||
private readonly ExternalPluginHandler _externalPluginHandler;
|
private readonly ExternalPluginHandler _externalPluginHandler;
|
||||||
|
|
||||||
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
||||||
private readonly GcRewardsCache _gcRewardsCache;
|
private readonly GcRewardsCache _gcRewardsCache;
|
||||||
|
|
||||||
private readonly IconCache _iconCache;
|
|
||||||
private readonly ItemCache _itemCache;
|
|
||||||
private readonly ExchangeHandler _exchangeHandler;
|
|
||||||
private readonly SupplyHandler _supplyHandler;
|
|
||||||
private readonly ConfigWindow _configWindow;
|
private readonly ConfigWindow _configWindow;
|
||||||
private readonly TurnInWindow _turnInWindow;
|
private readonly TurnInWindow _turnInWindow;
|
||||||
|
private readonly IReadOnlyDictionary<uint, uint> _sealCaps;
|
||||||
|
|
||||||
private Stage _currentStageInternal = Stage.Stopped;
|
private Stage _currentStageInternal = Stage.Stopped;
|
||||||
|
private DateTime _continueAt = DateTime.MinValue;
|
||||||
|
private int _lastTurnInListSize = int.MaxValue;
|
||||||
|
private uint _turnInErrors = 0;
|
||||||
|
private List<PurchaseItemRequest> _itemsToPurchaseNow = new();
|
||||||
|
|
||||||
public DeliverooPlugin(IDalamudPluginInterface pluginInterface, IChatGui chatGui, IGameGui gameGui,
|
public DeliverooPlugin(DalamudPluginInterface pluginInterface, IChatGui chatGui, IGameGui gameGui,
|
||||||
IFramework framework, IClientState clientState, IObjectTable objectTable, ITargetManager targetManager,
|
IFramework framework, IClientState clientState, IObjectTable objectTable, ITargetManager targetManager,
|
||||||
IDataManager dataManager, ICondition condition, ICommandManager commandManager, IPluginLog pluginLog,
|
IDataManager dataManager, ICondition condition, ICommandManager commandManager, IPluginLog pluginLog,
|
||||||
IAddonLifecycle addonLifecycle, ITextureProvider textureProvider, IGameConfig gameConfig, IKeyState keyState)
|
IAddonLifecycle addonLifecycle)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(dataManager);
|
|
||||||
|
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_chatGui = chatGui;
|
_chatGui = chatGui;
|
||||||
_gameGui = gameGui;
|
_gameGui = gameGui;
|
||||||
_framework = framework;
|
_framework = framework;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
|
_objectTable = objectTable;
|
||||||
|
_targetManager = targetManager;
|
||||||
_condition = condition;
|
_condition = condition;
|
||||||
_commandManager = commandManager;
|
_commandManager = commandManager;
|
||||||
_pluginLog = pluginLog;
|
_pluginLog = pluginLog;
|
||||||
_addonLifecycle = addonLifecycle;
|
_addonLifecycle = addonLifecycle;
|
||||||
_keyState = keyState;
|
|
||||||
|
|
||||||
_configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration();
|
|
||||||
MigrateConfiguration();
|
|
||||||
|
|
||||||
_gameStrings = new GameStrings(dataManager, _pluginLog);
|
_gameStrings = new GameStrings(dataManager, _pluginLog);
|
||||||
_externalPluginHandler = new ExternalPluginHandler(_pluginInterface, gameConfig, _configuration, _pluginLog);
|
_externalPluginHandler = new ExternalPluginHandler(_pluginInterface, _framework, _pluginLog);
|
||||||
_gameFunctions = new GameFunctions(objectTable, _clientState, targetManager, dataManager,
|
_configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration();
|
||||||
_externalPluginHandler, _pluginLog);
|
|
||||||
_gcRewardsCache = new GcRewardsCache(dataManager);
|
_gcRewardsCache = new GcRewardsCache(dataManager);
|
||||||
_iconCache = new IconCache(textureProvider);
|
_configWindow = new ConfigWindow(_pluginInterface, this, _configuration, _gcRewardsCache, _clientState, _pluginLog);
|
||||||
_itemCache = new ItemCache(dataManager);
|
|
||||||
|
|
||||||
_exchangeHandler = new ExchangeHandler(this, _gameFunctions, targetManager, _gameGui, _chatGui, _pluginLog);
|
|
||||||
_supplyHandler = new SupplyHandler(this, _gameFunctions, targetManager, _gameGui, _pluginLog);
|
|
||||||
|
|
||||||
_configWindow = new ConfigWindow(_pluginInterface, this, _configuration, _gcRewardsCache, _clientState,
|
|
||||||
_pluginLog, _iconCache, _gameFunctions);
|
|
||||||
_windowSystem.AddWindow(_configWindow);
|
_windowSystem.AddWindow(_configWindow);
|
||||||
_turnInWindow = new TurnInWindow(this, _pluginInterface, _configuration, _condition, _clientState,
|
_turnInWindow = new TurnInWindow(this, _pluginInterface, _configuration, _condition, _gcRewardsCache, _configWindow);
|
||||||
_gcRewardsCache, _configWindow, _iconCache, _keyState, _gameFunctions);
|
|
||||||
_windowSystem.AddWindow(_turnInWindow);
|
_windowSystem.AddWindow(_turnInWindow);
|
||||||
|
_sealCaps = dataManager.GetExcelSheet<GrandCompanyRank>()!.Where(x => x.RowId > 0)
|
||||||
|
.ToDictionary(x => x.RowId, x => x.MaxSeals);
|
||||||
|
|
||||||
_framework.Update += FrameworkUpdate;
|
_framework.Update += FrameworkUpdate;
|
||||||
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
||||||
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
|
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
|
||||||
_clientState.Login += Login;
|
_clientState.Login += Login;
|
||||||
_clientState.Logout += Logout;
|
_clientState.Logout += Logout;
|
||||||
_chatGui.ChatMessage += ChatMessage;
|
|
||||||
_commandManager.AddHandler("/deliveroo", new CommandInfo(ProcessCommand)
|
_commandManager.AddHandler("/deliveroo", new CommandInfo(ProcessCommand)
|
||||||
{
|
{
|
||||||
HelpMessage = "Open the configuration"
|
HelpMessage = "Open the configuration"
|
||||||
@ -114,54 +100,6 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
|
|
||||||
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
|
||||||
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesNoPostSetup);
|
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesNoPostSetup);
|
||||||
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "GrandCompanySupplyReward", GrandCompanySupplyRewardPostSetup);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MigrateConfiguration()
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0612 // Type or member is obsolete
|
|
||||||
if (_configuration.Version == 1)
|
|
||||||
{
|
|
||||||
_configuration.ItemsAvailableToPurchase = _configuration.ItemsAvailableForPurchase.Select(x =>
|
|
||||||
new Configuration.PurchaseOption
|
|
||||||
{
|
|
||||||
ItemId = x,
|
|
||||||
SameQuantityForAllLists = false,
|
|
||||||
}).ToList();
|
|
||||||
_configuration.Version = 2;
|
|
||||||
_pluginInterface.SavePluginConfig(_configuration);
|
|
||||||
}
|
|
||||||
#pragma warning restore CS0612 // Type or member is obsolete
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ChatMessage(XivChatType type, int timestamp, ref SeString sender, ref SeString message,
|
|
||||||
ref bool isHandled)
|
|
||||||
{
|
|
||||||
if (_configuration.PauseAtRank <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (type != _gameStrings.RankUpFcType)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var match = _gameStrings.RankUpFc.Match(message.ToString());
|
|
||||||
if (!match.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var group in match.Groups.Values)
|
|
||||||
{
|
|
||||||
if (int.TryParse(group.Value, out int rank) && rank == _configuration.PauseAtRank)
|
|
||||||
{
|
|
||||||
_turnInWindow.State = false;
|
|
||||||
_pluginLog.Information($"Pausing GC delivery, FC reached rank {rank}");
|
|
||||||
DeliveryResult = new MessageDeliveryResult
|
|
||||||
{
|
|
||||||
Message = new SeStringBuilder()
|
|
||||||
.Append($"Pausing Deliveroo, your FC reached rank {rank}.")
|
|
||||||
.Build(),
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal CharacterConfiguration? CharacterConfiguration { get; set; }
|
internal CharacterConfiguration? CharacterConfiguration { get; set; }
|
||||||
@ -180,35 +118,6 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DateTime ContinueAt { private get; set; } = DateTime.MinValue;
|
|
||||||
internal List<PurchaseItemRequest> ItemsToPurchaseNow { get; private set; } = new();
|
|
||||||
internal int LastTurnInListSize { get; set; } = int.MaxValue;
|
|
||||||
internal IDeliveryResult? DeliveryResult { get; set; }
|
|
||||||
|
|
||||||
internal bool TurnInState
|
|
||||||
{
|
|
||||||
set => _turnInWindow.State = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal string TurnInError
|
|
||||||
{
|
|
||||||
set => _turnInWindow.Error = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int EffectiveReservedSealCount
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (CharacterConfiguration is { IgnoreMinimumSealsToKeep: true })
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return _configuration.ReserveDifferentSealCountAtMaxRank &&
|
|
||||||
_gameFunctions.GetSealCap() == _gameFunctions.MaxSealCap
|
|
||||||
? _configuration.ReservedSealCountAtMaxRank
|
|
||||||
: _configuration.ReservedSealCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Login()
|
private void Login()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -218,11 +127,11 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
{
|
{
|
||||||
if (CharacterConfiguration.CachedPlayerName != _clientState.LocalPlayer!.Name.ToString() ||
|
if (CharacterConfiguration.CachedPlayerName != _clientState.LocalPlayer!.Name.ToString() ||
|
||||||
CharacterConfiguration.CachedWorldName !=
|
CharacterConfiguration.CachedWorldName !=
|
||||||
_clientState.LocalPlayer.HomeWorld.Value.Name.ToString())
|
_clientState.LocalPlayer.HomeWorld.GameData!.Name.ToString())
|
||||||
{
|
{
|
||||||
CharacterConfiguration.CachedPlayerName = _clientState.LocalPlayer!.Name.ToString();
|
CharacterConfiguration.CachedPlayerName = _clientState.LocalPlayer!.Name.ToString();
|
||||||
CharacterConfiguration.CachedWorldName =
|
CharacterConfiguration.CachedWorldName =
|
||||||
_clientState.LocalPlayer.HomeWorld.Value.Name.ToString();
|
_clientState.LocalPlayer.HomeWorld.GameData!.Name.ToString();
|
||||||
|
|
||||||
CharacterConfiguration.Save(_pluginInterface);
|
CharacterConfiguration.Save(_pluginInterface);
|
||||||
}
|
}
|
||||||
@ -242,7 +151,7 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Logout(int type, int code)
|
private void Logout()
|
||||||
{
|
{
|
||||||
CharacterConfiguration = null;
|
CharacterConfiguration = null;
|
||||||
}
|
}
|
||||||
@ -253,62 +162,59 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
if (!_clientState.IsLoggedIn ||
|
if (!_clientState.IsLoggedIn ||
|
||||||
_clientState.TerritoryType is not 128 and not 130 and not 132 ||
|
_clientState.TerritoryType is not 128 and not 130 and not 132 ||
|
||||||
_condition[ConditionFlag.OccupiedInCutSceneEvent] ||
|
_condition[ConditionFlag.OccupiedInCutSceneEvent] ||
|
||||||
_gameFunctions.GetDistanceToNpc(_gameFunctions.GetQuartermasterId(), out IGameObject? quartermaster) >= 7f ||
|
GetDistanceToNpc(GetQuartermasterId(), out GameObject? quartermaster) >= 7f ||
|
||||||
_gameFunctions.GetDistanceToNpc(_gameFunctions.GetPersonnelOfficerId(), out IGameObject? personnelOfficer) >=
|
GetDistanceToNpc(GetPersonnelOfficerId(), out GameObject? personnelOfficer) >= 7f ||
|
||||||
7f ||
|
|
||||||
CharacterConfiguration is { DisableForCharacter: true } ||
|
CharacterConfiguration is { DisableForCharacter: true } ||
|
||||||
_configWindow.IsOpen)
|
_configWindow.IsOpen)
|
||||||
{
|
{
|
||||||
_turnInWindow.IsOpen = false;
|
_turnInWindow.IsOpen = false;
|
||||||
_turnInWindow.State = false;
|
_turnInWindow.State = false;
|
||||||
StopTurnIn();
|
if (CurrentStage != Stage.Stopped)
|
||||||
|
{
|
||||||
|
_externalPluginHandler.Restore();
|
||||||
|
CurrentStage = Stage.Stopped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (DateTime.Now > ContinueAt)
|
else if (DateTime.Now > _continueAt)
|
||||||
{
|
{
|
||||||
_turnInWindow.IsOpen = true;
|
_turnInWindow.IsOpen = true;
|
||||||
_turnInWindow.Multiplier = _gameFunctions.GetSealMultiplier();
|
_turnInWindow.Multiplier = GetSealMultiplier();
|
||||||
|
|
||||||
if (!_turnInWindow.State)
|
if (!_turnInWindow.State)
|
||||||
{
|
{
|
||||||
StopTurnIn();
|
if (CurrentStage != Stage.Stopped)
|
||||||
|
{
|
||||||
|
_externalPluginHandler.Restore();
|
||||||
|
CurrentStage = Stage.Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (_turnInWindow.State && CurrentStage == Stage.Stopped)
|
else if (_turnInWindow.State && CurrentStage == Stage.Stopped)
|
||||||
{
|
{
|
||||||
CurrentStage = Stage.TargetPersonnelOfficer;
|
CurrentStage = Stage.TargetPersonnelOfficer;
|
||||||
ItemsToPurchaseNow = _turnInWindow.SelectedItems;
|
_itemsToPurchaseNow = _turnInWindow.SelectedItems;
|
||||||
DeliveryResult = new MessageDeliveryResult();
|
if (_itemsToPurchaseNow.Count > 0)
|
||||||
_supplyHandler.ResetTurnInErrorHandling();
|
|
||||||
if (ItemsToPurchaseNow.Count > 0)
|
|
||||||
{
|
{
|
||||||
_pluginLog.Information("Items to purchase:");
|
_pluginLog.Information("Items to purchase:");
|
||||||
foreach (var item in ItemsToPurchaseNow)
|
foreach (var item in _itemsToPurchaseNow)
|
||||||
_pluginLog.Information($" {item.Name} (limit = {item.EffectiveLimit})");
|
_pluginLog.Information($" {item.Name} (limit = {item.EffectiveLimit})");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_pluginLog.Information("No items to purchase configured or available");
|
_pluginLog.Information("No items to purchase configured or available");
|
||||||
|
|
||||||
|
|
||||||
var nextItem = _exchangeHandler.GetNextItemToPurchase();
|
var nextItem = GetNextItemToPurchase();
|
||||||
if (nextItem != null && _gameFunctions.GetCurrentSealCount() >=
|
if (nextItem != null && GetCurrentSealCount() >= _configuration.ReservedSealCount + nextItem.SealCost)
|
||||||
EffectiveReservedSealCount + nextItem.SealCost)
|
|
||||||
CurrentStage = Stage.TargetQuartermaster;
|
CurrentStage = Stage.TargetQuartermaster;
|
||||||
|
|
||||||
if (_gameGui.TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList",
|
if (_gameGui.TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList", out var gcSupplyList) &&
|
||||||
out var gcSupplyList) &&
|
|
||||||
LAddon.IsAddonReady(&gcSupplyList->AtkUnitBase))
|
LAddon.IsAddonReady(&gcSupplyList->AtkUnitBase))
|
||||||
CurrentStage = Stage.SelectExpertDeliveryTab;
|
CurrentStage = Stage.SelectExpertDeliveryTab;
|
||||||
|
|
||||||
if (_gameGui.TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var gcExchange) &&
|
if (_gameGui.TryGetAddonByName<AtkUnitBase>("GrandCompanyExchange", out var gcExchange) &&
|
||||||
LAddon.IsAddonReady(gcExchange))
|
LAddon.IsAddonReady(gcExchange))
|
||||||
CurrentStage = Stage.SelectRewardTier;
|
CurrentStage = Stage.SelectRewardTier;
|
||||||
|
|
||||||
if (_gameGui.TryGetAddonByName<AddonSelectString>("SelectString", out var addonSelectString) &&
|
|
||||||
LAddon.IsAddonReady(&addonSelectString->AtkUnitBase))
|
|
||||||
{
|
|
||||||
if (SelectStringPostSetup(addonSelectString, Stage.OpenGcSupply))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CurrentStage != Stage.Stopped && CurrentStage != Stage.RequestStop && !_externalPluginHandler.Saved)
|
if (CurrentStage != Stage.Stopped && CurrentStage != Stage.RequestStop && !_externalPluginHandler.Saved)
|
||||||
@ -317,7 +223,7 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
switch (CurrentStage)
|
switch (CurrentStage)
|
||||||
{
|
{
|
||||||
case Stage.TargetPersonnelOfficer:
|
case Stage.TargetPersonnelOfficer:
|
||||||
_supplyHandler.InteractWithPersonnelOfficer(personnelOfficer!, quartermaster!);
|
InteractWithPersonnelOfficer(personnelOfficer!, quartermaster!);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.OpenGcSupply:
|
case Stage.OpenGcSupply:
|
||||||
@ -325,48 +231,43 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.SelectExpertDeliveryTab:
|
case Stage.SelectExpertDeliveryTab:
|
||||||
_supplyHandler.SelectExpertDeliveryTab();
|
SelectExpertDeliveryTab();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.SelectItemToTurnIn:
|
case Stage.SelectItemToTurnIn:
|
||||||
_supplyHandler.SelectItemToTurnIn();
|
SelectItemToTurnIn();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.TurnInSelected:
|
case Stage.TurnInSelected:
|
||||||
// see GrandCompanySupplyReward
|
TurnInSelectedItem();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.FinalizeTurnIn:
|
case Stage.FinalizeTurnIn:
|
||||||
case Stage.SingleFinalizeTurnIn:
|
FinalizeTurnInItem();
|
||||||
_supplyHandler.FinalizeTurnInItem();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.CloseGcSupplySelectString:
|
case Stage.CloseGcSupply:
|
||||||
// see SelectStringPostSetup
|
// see SelectStringPostSetup
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.CloseGcSupplySelectStringThenStop:
|
case Stage.CloseGcSupplyThenStop:
|
||||||
// see SelectStringPostSetup
|
// see SelectStringPostSetup
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.CloseGcSupplyWindowThenStop:
|
|
||||||
_supplyHandler.CloseGcSupplyWindow();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Stage.TargetQuartermaster:
|
case Stage.TargetQuartermaster:
|
||||||
_exchangeHandler.InteractWithQuartermaster(personnelOfficer!, quartermaster!);
|
InteractWithQuartermaster(personnelOfficer!, quartermaster!);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.SelectRewardTier:
|
case Stage.SelectRewardTier:
|
||||||
_exchangeHandler.SelectRewardTier();
|
SelectRewardTier();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.SelectRewardSubCategory:
|
case Stage.SelectRewardSubCategory:
|
||||||
_exchangeHandler.SelectRewardSubCategory();
|
SelectRewardSubCategory();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.SelectReward:
|
case Stage.SelectReward:
|
||||||
_exchangeHandler.SelectReward();
|
SelectReward();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.ConfirmReward:
|
case Stage.ConfirmReward:
|
||||||
@ -374,13 +275,14 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.CloseGcExchange:
|
case Stage.CloseGcExchange:
|
||||||
_exchangeHandler.CloseGcExchange();
|
CloseGcExchange();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Stage.RequestStop:
|
case Stage.RequestStop:
|
||||||
StopTurnIn();
|
_externalPluginHandler.Restore();
|
||||||
break;
|
CurrentStage = Stage.Stopped;
|
||||||
|
|
||||||
|
break;
|
||||||
case Stage.Stopped:
|
case Stage.Stopped:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -391,72 +293,20 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopTurnIn()
|
|
||||||
{
|
|
||||||
if (CurrentStage != Stage.Stopped)
|
|
||||||
{
|
|
||||||
_externalPluginHandler.Restore();
|
|
||||||
CurrentStage = Stage.Stopped;
|
|
||||||
|
|
||||||
if (DeliveryResult is null or MessageDeliveryResult)
|
|
||||||
{
|
|
||||||
var text = (DeliveryResult as MessageDeliveryResult)?.Message ?? "Delivery completed.";
|
|
||||||
var message = _configuration.ChatType switch
|
|
||||||
{
|
|
||||||
XivChatType.Say
|
|
||||||
or XivChatType.Shout
|
|
||||||
or XivChatType.TellOutgoing
|
|
||||||
or XivChatType.TellIncoming
|
|
||||||
or XivChatType.Party
|
|
||||||
or XivChatType.Alliance
|
|
||||||
or (>= XivChatType.Ls1 and <= XivChatType.Ls8)
|
|
||||||
or XivChatType.FreeCompany
|
|
||||||
or XivChatType.NoviceNetwork
|
|
||||||
or XivChatType.Yell
|
|
||||||
or XivChatType.CrossParty
|
|
||||||
or XivChatType.PvPTeam
|
|
||||||
or XivChatType.CrossLinkShell1
|
|
||||||
or XivChatType.NPCDialogue
|
|
||||||
or XivChatType.NPCDialogueAnnouncements
|
|
||||||
or (>= XivChatType.CrossLinkShell2 and <= XivChatType.CrossLinkShell8)
|
|
||||||
=> new XivChatEntry
|
|
||||||
{
|
|
||||||
Message = text,
|
|
||||||
Type = _configuration.ChatType,
|
|
||||||
Name = new SeStringBuilder().AddUiForeground("Deliveroo", 52).Build(),
|
|
||||||
},
|
|
||||||
_ => new XivChatEntry
|
|
||||||
{
|
|
||||||
Message = new SeStringBuilder().AddUiForeground("[Deliveroo] ", 52)
|
|
||||||
.Append(text)
|
|
||||||
.Build(),
|
|
||||||
Type = _configuration.ChatType,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
_chatGui.Print(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeliveryResult = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "GrandCompanySupplyReward", GrandCompanySupplyRewardPostSetup);
|
|
||||||
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesNoPostSetup);
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectYesno", SelectYesNoPostSetup);
|
||||||
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
|
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectString", SelectStringPostSetup);
|
||||||
|
|
||||||
_commandManager.RemoveHandler("/deliveroo");
|
_commandManager.RemoveHandler("/deliveroo");
|
||||||
_chatGui.ChatMessage -= ChatMessage;
|
|
||||||
_clientState.Logout -= Logout;
|
_clientState.Logout -= Logout;
|
||||||
_clientState.Login -= Login;
|
_clientState.Login -= Login;
|
||||||
_pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle;
|
_pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle;
|
||||||
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
||||||
_framework.Update -= FrameworkUpdate;
|
_framework.Update -= FrameworkUpdate;
|
||||||
|
|
||||||
_gameFunctions.Dispose();
|
_externalPluginHandler.Restore();
|
||||||
_externalPluginHandler.Dispose();
|
|
||||||
_iconCache.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessCommand(string command, string arguments)
|
private void ProcessCommand(string command, string arguments)
|
||||||
|
48
Deliveroo/External/AllaganToolsIpc.cs
vendored
48
Deliveroo/External/AllaganToolsIpc.cs
vendored
@ -1,48 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Ipc;
|
|
||||||
using Dalamud.Plugin.Ipc.Exceptions;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
||||||
|
|
||||||
namespace Deliveroo.External;
|
|
||||||
|
|
||||||
internal sealed class AllaganToolsIpc
|
|
||||||
{
|
|
||||||
private readonly IPluginLog _pluginLog;
|
|
||||||
|
|
||||||
private static readonly uint[] RetainerInventoryTypes = new[]
|
|
||||||
{
|
|
||||||
InventoryType.RetainerPage1,
|
|
||||||
InventoryType.RetainerPage2,
|
|
||||||
InventoryType.RetainerPage3,
|
|
||||||
InventoryType.RetainerPage4,
|
|
||||||
InventoryType.RetainerPage5,
|
|
||||||
InventoryType.RetainerPage6,
|
|
||||||
InventoryType.RetainerPage7,
|
|
||||||
}
|
|
||||||
.Select(x => (uint)x).ToArray();
|
|
||||||
|
|
||||||
private readonly ICallGateSubscriber<uint, bool, uint[], uint> _itemCountOwned;
|
|
||||||
|
|
||||||
public AllaganToolsIpc(IDalamudPluginInterface pluginInterface, IPluginLog pluginLog)
|
|
||||||
{
|
|
||||||
_pluginLog = pluginLog;
|
|
||||||
_itemCountOwned = pluginInterface.GetIpcSubscriber<uint, bool, uint[], uint>("AllaganTools.ItemCountOwned");
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetRetainerItemCount(uint itemId)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
uint itemCount = _itemCountOwned.InvokeFunc(itemId, true, RetainerInventoryTypes);
|
|
||||||
_pluginLog.Verbose($"Found {itemCount} items in retainer inventories for itemId {itemId}");
|
|
||||||
return itemCount;
|
|
||||||
}
|
|
||||||
catch (IpcError)
|
|
||||||
{
|
|
||||||
_pluginLog.Warning("Could not query allagantools for retainer inventory counts");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
46
Deliveroo/External/DeliverooIpc.cs
vendored
46
Deliveroo/External/DeliverooIpc.cs
vendored
@ -1,46 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Ipc;
|
|
||||||
|
|
||||||
namespace Deliveroo.External;
|
|
||||||
|
|
||||||
internal sealed class DeliverooIpc : IDisposable
|
|
||||||
{
|
|
||||||
private const string TurnInStarted = "Deliveroo.TurnInStarted";
|
|
||||||
private const string TurnInStopped = "Deliveroo.TurnInStopped";
|
|
||||||
private const string IsTurnInRunning = "Deliveroo.IsTurnInRunning";
|
|
||||||
|
|
||||||
private readonly ICallGateProvider<bool> _isTurnInRunning;
|
|
||||||
private readonly ICallGateProvider<object> _turnInStarted;
|
|
||||||
private readonly ICallGateProvider<object> _turnInStopped;
|
|
||||||
|
|
||||||
private bool _running;
|
|
||||||
|
|
||||||
public DeliverooIpc(IDalamudPluginInterface pluginInterface)
|
|
||||||
{
|
|
||||||
_isTurnInRunning = pluginInterface.GetIpcProvider<bool>(IsTurnInRunning);
|
|
||||||
_turnInStarted = pluginInterface.GetIpcProvider<object>(TurnInStarted);
|
|
||||||
_turnInStopped = pluginInterface.GetIpcProvider<object>(TurnInStopped);
|
|
||||||
|
|
||||||
_isTurnInRunning.RegisterFunc(CheckIsTurnInRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartTurnIn()
|
|
||||||
{
|
|
||||||
_running = true;
|
|
||||||
_turnInStarted.SendMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopTurnIn()
|
|
||||||
{
|
|
||||||
_running = false;
|
|
||||||
_turnInStopped.SendMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckIsTurnInRunning() => _running;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_isTurnInRunning.UnregisterFunc();
|
|
||||||
}
|
|
||||||
}
|
|
94
Deliveroo/External/ExternalPluginHandler.cs
vendored
94
Deliveroo/External/ExternalPluginHandler.cs
vendored
@ -1,34 +1,25 @@
|
|||||||
using System;
|
using Dalamud.Plugin;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using LLib;
|
||||||
|
|
||||||
namespace Deliveroo.External;
|
namespace Deliveroo.External;
|
||||||
|
|
||||||
internal sealed class ExternalPluginHandler : IDisposable
|
internal sealed class ExternalPluginHandler
|
||||||
{
|
{
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
|
||||||
private readonly IGameConfig _gameConfig;
|
|
||||||
private readonly Configuration _configuration;
|
|
||||||
private readonly IPluginLog _pluginLog;
|
private readonly IPluginLog _pluginLog;
|
||||||
private readonly DeliverooIpc _deliverooIpc;
|
private readonly YesAlreadyIpc _yesAlreadyIpc;
|
||||||
private readonly PandoraIpc _pandoraIpc;
|
private readonly PandoraIpc _pandoraIpc;
|
||||||
private readonly AllaganToolsIpc _allaganToolsIpc;
|
|
||||||
|
|
||||||
|
private bool? _yesAlreadyState;
|
||||||
private bool? _pandoraState;
|
private bool? _pandoraState;
|
||||||
private SystemConfigState? _limitFrameRateWhenClientInactive;
|
|
||||||
private SystemConfigState? _uncapFrameRate;
|
|
||||||
|
|
||||||
public ExternalPluginHandler(IDalamudPluginInterface pluginInterface, IGameConfig gameConfig,
|
public ExternalPluginHandler(DalamudPluginInterface pluginInterface, IFramework framework, IPluginLog pluginLog)
|
||||||
Configuration configuration, IPluginLog pluginLog)
|
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
|
||||||
_gameConfig = gameConfig;
|
|
||||||
_configuration = configuration;
|
|
||||||
_pluginLog = pluginLog;
|
_pluginLog = pluginLog;
|
||||||
_deliverooIpc = new DeliverooIpc(pluginInterface);
|
|
||||||
|
var dalamudReflector = new DalamudReflector(pluginInterface, framework, pluginLog);
|
||||||
|
_yesAlreadyIpc = new YesAlreadyIpc(dalamudReflector);
|
||||||
_pandoraIpc = new PandoraIpc(pluginInterface, pluginLog);
|
_pandoraIpc = new PandoraIpc(pluginInterface, pluginLog);
|
||||||
_allaganToolsIpc = new AllaganToolsIpc(pluginInterface, pluginLog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Saved { get; private set; }
|
public bool Saved { get; private set; }
|
||||||
@ -42,21 +33,15 @@ internal sealed class ExternalPluginHandler : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
_pluginLog.Information("Saving external plugin state...");
|
_pluginLog.Information("Saving external plugin state...");
|
||||||
_deliverooIpc.StartTurnIn();
|
|
||||||
SaveYesAlreadyState();
|
SaveYesAlreadyState();
|
||||||
SavePandoraState();
|
SavePandoraState();
|
||||||
SaveGameConfig();
|
|
||||||
Saved = true;
|
Saved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveYesAlreadyState()
|
private void SaveYesAlreadyState()
|
||||||
{
|
{
|
||||||
if (_pluginInterface.TryGetData<HashSet<string>>("YesAlready.StopRequests", out var data) &&
|
_yesAlreadyState = _yesAlreadyIpc.DisableIfNecessary();
|
||||||
!data.Contains(nameof(Deliveroo)))
|
_pluginLog.Information($"Previous yesalready state: {_yesAlreadyState}");
|
||||||
{
|
|
||||||
_pluginLog.Debug("Disabling YesAlready");
|
|
||||||
data.Add(nameof(Deliveroo));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SavePandoraState()
|
private void SavePandoraState()
|
||||||
@ -65,40 +50,24 @@ internal sealed class ExternalPluginHandler : IDisposable
|
|||||||
_pluginLog.Info($"Previous pandora feature state: {_pandoraState}");
|
_pluginLog.Info($"Previous pandora feature state: {_pandoraState}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveGameConfig()
|
|
||||||
{
|
|
||||||
if (!_configuration.DisableFrameLimiter)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_limitFrameRateWhenClientInactive ??=
|
|
||||||
new SystemConfigState(_gameConfig, SystemConfigState.ConfigFpsInactive, 0);
|
|
||||||
|
|
||||||
if (_configuration.UncapFrameRate)
|
|
||||||
_uncapFrameRate ??= new SystemConfigState(_gameConfig, SystemConfigState.ConfigFps, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Restore()
|
public void Restore()
|
||||||
{
|
{
|
||||||
if (Saved)
|
if (Saved)
|
||||||
{
|
{
|
||||||
RestoreYesAlready();
|
RestoreYesAlready();
|
||||||
RestorePandora();
|
RestorePandora();
|
||||||
RestoreGameConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Saved = false;
|
Saved = false;
|
||||||
|
_yesAlreadyState = null;
|
||||||
_pandoraState = null;
|
_pandoraState = null;
|
||||||
_deliverooIpc.StopTurnIn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RestoreYesAlready()
|
private void RestoreYesAlready()
|
||||||
{
|
{
|
||||||
if (_pluginInterface.TryGetData<HashSet<string>>("YesAlready.StopRequests", out var data) &&
|
_pluginLog.Information($"Restoring previous yesalready state: {_yesAlreadyState}");
|
||||||
data.Contains(nameof(Deliveroo)))
|
if (_yesAlreadyState == true)
|
||||||
{
|
_yesAlreadyIpc.Enable();
|
||||||
_pluginLog.Debug("Restoring YesAlready");
|
|
||||||
data.Remove(nameof(Deliveroo));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RestorePandora()
|
private void RestorePandora()
|
||||||
@ -107,37 +76,4 @@ internal sealed class ExternalPluginHandler : IDisposable
|
|||||||
if (_pandoraState == true)
|
if (_pandoraState == true)
|
||||||
_pandoraIpc.Enable();
|
_pandoraIpc.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RestoreGameConfig()
|
|
||||||
{
|
|
||||||
_uncapFrameRate?.Restore(_gameConfig);
|
|
||||||
_uncapFrameRate = null;
|
|
||||||
|
|
||||||
_limitFrameRateWhenClientInactive?.Restore(_gameConfig);
|
|
||||||
_limitFrameRateWhenClientInactive = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_deliverooIpc.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetRetainerItemCount(uint itemId) => _allaganToolsIpc.GetRetainerItemCount(itemId);
|
|
||||||
|
|
||||||
private sealed record SystemConfigState(string Key, uint OldValue)
|
|
||||||
{
|
|
||||||
public const string ConfigFps = "Fps";
|
|
||||||
public const string ConfigFpsInactive = "FPSInActive";
|
|
||||||
|
|
||||||
public SystemConfigState(IGameConfig gameConfig, string key, uint newValue)
|
|
||||||
: this(key, gameConfig.System.GetUInt(key))
|
|
||||||
{
|
|
||||||
gameConfig.System.Set(key, newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Restore(IGameConfig gameConfig)
|
|
||||||
{
|
|
||||||
gameConfig.System.Set(Key, OldValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
2
Deliveroo/External/PandoraIpc.cs
vendored
2
Deliveroo/External/PandoraIpc.cs
vendored
@ -13,7 +13,7 @@ internal sealed class PandoraIpc
|
|||||||
private readonly ICallGateSubscriber<string, bool?> _getEnabled;
|
private readonly ICallGateSubscriber<string, bool?> _getEnabled;
|
||||||
private readonly ICallGateSubscriber<string, bool, object?> _setEnabled;
|
private readonly ICallGateSubscriber<string, bool, object?> _setEnabled;
|
||||||
|
|
||||||
public PandoraIpc(IDalamudPluginInterface pluginInterface, IPluginLog pluginLog)
|
public PandoraIpc(DalamudPluginInterface pluginInterface, IPluginLog pluginLog)
|
||||||
{
|
{
|
||||||
_pluginLog = pluginLog;
|
_pluginLog = pluginLog;
|
||||||
_getEnabled = pluginInterface.GetIpcSubscriber<string, bool?>("PandorasBox.GetFeatureEnabled");
|
_getEnabled = pluginInterface.GetIpcSubscriber<string, bool?>("PandorasBox.GetFeatureEnabled");
|
||||||
|
53
Deliveroo/External/YesAlreadyIpc.cs
vendored
Normal file
53
Deliveroo/External/YesAlreadyIpc.cs
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using LLib;
|
||||||
|
|
||||||
|
namespace Deliveroo.External;
|
||||||
|
|
||||||
|
internal sealed class YesAlreadyIpc
|
||||||
|
{
|
||||||
|
private readonly DalamudReflector _dalamudReflector;
|
||||||
|
|
||||||
|
public YesAlreadyIpc(DalamudReflector dalamudReflector)
|
||||||
|
{
|
||||||
|
_dalamudReflector = dalamudReflector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object? GetConfiguration()
|
||||||
|
{
|
||||||
|
if (_dalamudReflector.TryGetDalamudPlugin("Yes Already", out var plugin))
|
||||||
|
{
|
||||||
|
var pluginService = plugin!.GetType().Assembly.GetType("YesAlready.Service");
|
||||||
|
return pluginService!.GetProperty("Configuration", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool? DisableIfNecessary()
|
||||||
|
{
|
||||||
|
object? configuration = GetConfiguration();
|
||||||
|
if (configuration == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var property = configuration.GetType().GetProperty("Enabled")!;
|
||||||
|
bool enabled = (bool)property.GetValue(configuration)!;
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
property.SetValue(configuration, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enable()
|
||||||
|
{
|
||||||
|
object? configuration = GetConfiguration();
|
||||||
|
if (configuration == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
var property = configuration.GetType().GetProperty("Enabled")!;
|
||||||
|
property.SetValue(configuration, true);
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +1,33 @@
|
|||||||
using System.Data;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using LLib;
|
|
||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.CustomSheets;
|
||||||
using Lumina.Text.ReadOnly;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using Lumina.Text;
|
||||||
|
using Lumina.Text.Payloads;
|
||||||
|
|
||||||
namespace Deliveroo.GameData;
|
namespace Deliveroo.GameData;
|
||||||
|
|
||||||
internal sealed class GameStrings
|
internal sealed class GameStrings
|
||||||
{
|
{
|
||||||
|
private readonly IDataManager _dataManager;
|
||||||
|
private readonly IPluginLog _pluginLog;
|
||||||
|
|
||||||
public GameStrings(IDataManager dataManager, IPluginLog pluginLog)
|
public GameStrings(IDataManager dataManager, IPluginLog pluginLog)
|
||||||
{
|
{
|
||||||
UndertakeSupplyAndProvisioningMission =
|
_dataManager = dataManager;
|
||||||
dataManager.GetString<ComDefGrandCompanyOfficer>("TEXT_COMDEFGRANDCOMPANYOFFICER_00073_A4_002", pluginLog)
|
_pluginLog = pluginLog;
|
||||||
?? throw new ConstraintException($"Unable to resolve {nameof(UndertakeSupplyAndProvisioningMission)}");
|
|
||||||
ClosePersonnelOfficerTalk =
|
|
||||||
dataManager.GetString<ComDefGrandCompanyOfficer>("TEXT_COMDEFGRANDCOMPANYOFFICER_00073_A4_004", pluginLog)
|
|
||||||
?? throw new ConstraintException($"Unable to resolve {nameof(ClosePersonnelOfficerTalk)}");
|
|
||||||
ExchangeItems = dataManager.GetRegex<Addon>(3290, addon => addon.Text, pluginLog)
|
|
||||||
?? throw new ConstraintException($"Unable to resolve {nameof(ExchangeItems)}");
|
|
||||||
TradeHighQualityItem =
|
|
||||||
dataManager.GetString<Addon>(102434, addon => addon.Text, pluginLog)
|
|
||||||
?? throw new ConstraintException($"Unable to resolve {nameof(TradeHighQualityItem)}");
|
|
||||||
|
|
||||||
var rankUpFc = dataManager.GetExcelSheet<LogMessage>().GetRow(3123);
|
UndertakeSupplyAndProvisioningMission =
|
||||||
RankUpFc = rankUpFc.GetRegex(logMessage => logMessage.Text, pluginLog)
|
GetDialogue<ComDefGrandCompanyOfficer>("TEXT_COMDEFGRANDCOMPANYOFFICER_00073_A4_002");
|
||||||
?? throw new ConstraintException($"Unable to resolve {nameof(RankUpFc)}");
|
ClosePersonnelOfficerTalk =
|
||||||
RankUpFcType = (XivChatType)rankUpFc.LogKind;
|
GetDialogue<ComDefGrandCompanyOfficer>("TEXT_COMDEFGRANDCOMPANYOFFICER_00073_A4_004");
|
||||||
|
ExchangeItems = GetRegex<Addon>(4928, addon => addon.Text)
|
||||||
|
?? throw new Exception($"Unable to resolve {nameof(ExchangeItems)}");
|
||||||
|
TradeHighQualityItem = GetString<Addon>(102434, addon => addon.Text)
|
||||||
|
?? throw new Exception($"Unable to resolve {nameof(TradeHighQualityItem)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -37,21 +35,54 @@ internal sealed class GameStrings
|
|||||||
public string ClosePersonnelOfficerTalk { get; }
|
public string ClosePersonnelOfficerTalk { get; }
|
||||||
public Regex ExchangeItems { get; }
|
public Regex ExchangeItems { get; }
|
||||||
public string TradeHighQualityItem { get; }
|
public string TradeHighQualityItem { get; }
|
||||||
public Regex RankUpFc { get; }
|
|
||||||
public XivChatType RankUpFcType { get; }
|
private string GetDialogue<T>(string key)
|
||||||
|
where T : QuestDialogueText
|
||||||
|
{
|
||||||
|
string result = _dataManager.GetExcelSheet<T>()!
|
||||||
|
.Single(x => x.Key == key)
|
||||||
|
.Value
|
||||||
|
.ToString();
|
||||||
|
_pluginLog.Verbose($"{typeof(T).Name}.{key} => {result}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeString? GetSeString<T>(uint rowId, Func<T, SeString?> mapper)
|
||||||
|
where T : ExcelRow
|
||||||
|
{
|
||||||
|
var row = _dataManager.GetExcelSheet<T>()?.GetRow(rowId);
|
||||||
|
if (row == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return mapper(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetString<T>(uint rowId, Func<T, SeString?> mapper)
|
||||||
|
where T : ExcelRow
|
||||||
|
{
|
||||||
|
string? text = GetSeString(rowId, mapper)?.ToString();
|
||||||
|
|
||||||
|
_pluginLog.Verbose($"{typeof(T).Name}.{rowId} => {text}");
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Regex? GetRegex<T>(uint rowId, Func<T, SeString?> mapper)
|
||||||
|
where T : ExcelRow
|
||||||
|
{
|
||||||
|
SeString? text = GetSeString(rowId, mapper);
|
||||||
|
string regex = string.Join("", text.Payloads.Select(payload =>
|
||||||
|
{
|
||||||
|
if (payload is TextPayload)
|
||||||
|
return Regex.Escape(payload.RawString);
|
||||||
|
else
|
||||||
|
return ".*";
|
||||||
|
}));
|
||||||
|
_pluginLog.Verbose($"{typeof(T).Name}.{rowId} => /{regex}/");
|
||||||
|
return new Regex(regex);
|
||||||
|
}
|
||||||
|
|
||||||
[Sheet("custom/000/ComDefGrandCompanyOfficer_00073")]
|
[Sheet("custom/000/ComDefGrandCompanyOfficer_00073")]
|
||||||
[SuppressMessage("Performance", "CA1812")]
|
private class ComDefGrandCompanyOfficer : QuestDialogueText
|
||||||
private readonly struct ComDefGrandCompanyOfficer(ExcelPage page, uint offset, uint row)
|
|
||||||
: IQuestDialogueText, IExcelRow<ComDefGrandCompanyOfficer>
|
|
||||||
{
|
{
|
||||||
public uint RowId => row;
|
|
||||||
|
|
||||||
public ReadOnlySeString Key => page.ReadString(offset, offset);
|
|
||||||
public ReadOnlySeString Value => page.ReadString(offset + 4, offset);
|
|
||||||
|
|
||||||
static ComDefGrandCompanyOfficer IExcelRow<ComDefGrandCompanyOfficer>.Create(ExcelPage page, uint offset,
|
|
||||||
uint row) =>
|
|
||||||
new(page, offset, row);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
using System;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
||||||
|
|
||||||
namespace Deliveroo.GameData
|
|
||||||
{
|
|
||||||
internal sealed class GcRankInfo
|
|
||||||
{
|
|
||||||
public required string NameTwinAddersMale { private get; init; }
|
|
||||||
public required string NameTwinAddersFemale { private get; init; }
|
|
||||||
public required string NameMaelstromMale { private get; init; }
|
|
||||||
public required string NameMaelstromFemale { private get; init; }
|
|
||||||
public required string NameImmortalFlamesMale { private get; init; }
|
|
||||||
public required string NameImmortalFlamesFemale { private get; init; }
|
|
||||||
|
|
||||||
public required uint MaxSeals { get; init; }
|
|
||||||
public required uint RequiredSeals { get; init; }
|
|
||||||
public required byte RequiredHuntingLog { get; init; }
|
|
||||||
|
|
||||||
public string GetName(GrandCompany grandCompany, bool female)
|
|
||||||
{
|
|
||||||
return (grandCompany, female) switch
|
|
||||||
{
|
|
||||||
(GrandCompany.TwinAdder, false) => NameTwinAddersMale,
|
|
||||||
(GrandCompany.TwinAdder, true) => NameTwinAddersFemale,
|
|
||||||
(GrandCompany.Maelstrom, false) => NameMaelstromMale,
|
|
||||||
(GrandCompany.Maelstrom, true) => NameMaelstromFemale,
|
|
||||||
(GrandCompany.ImmortalFlames, false) => NameImmortalFlamesMale,
|
|
||||||
(GrandCompany.ImmortalFlames, true) => NameImmortalFlamesFemale,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(grandCompany) + "," + nameof(female)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,29 +10,24 @@ internal sealed class GcRewardItem : IEquatable<GcRewardItem>
|
|||||||
{
|
{
|
||||||
ItemId = 0,
|
ItemId = 0,
|
||||||
Name = "---",
|
Name = "---",
|
||||||
IconId = 0,
|
|
||||||
GrandCompanies = new List<GrandCompany>().AsReadOnly(),
|
GrandCompanies = new List<GrandCompany>().AsReadOnly(),
|
||||||
Tier = RewardTier.First,
|
Tier = RewardTier.First,
|
||||||
SubCategory = RewardSubCategory.Unknown,
|
SubCategory = RewardSubCategory.Unknown,
|
||||||
RequiredRank = 0,
|
RequiredRank = 0,
|
||||||
StackSize = 0,
|
StackSize = 0,
|
||||||
SealCost = 100_000,
|
SealCost = 100_000,
|
||||||
InventoryLimit = int.MaxValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public required uint ItemId { get; init; }
|
public required uint ItemId { get; init; }
|
||||||
public required string Name { get; init; }
|
public required string Name { get; init; }
|
||||||
public required ushort IconId { get; init; }
|
|
||||||
public required IReadOnlyList<GrandCompany> GrandCompanies { get; init; }
|
public required IReadOnlyList<GrandCompany> GrandCompanies { get; init; }
|
||||||
public required RewardTier Tier { get; init; }
|
public required RewardTier Tier { get; init; }
|
||||||
public required RewardSubCategory SubCategory { get; init; }
|
public required RewardSubCategory SubCategory { get; init; }
|
||||||
public required uint RequiredRank { get; init; }
|
public required uint RequiredRank { get; init; }
|
||||||
public required uint StackSize { get; init; }
|
public required uint StackSize { get; init; }
|
||||||
public required uint SealCost { get; init; }
|
public required uint SealCost { get; init; }
|
||||||
public required uint InventoryLimit { get; init; }
|
|
||||||
|
|
||||||
public bool IsValid() => ItemId > 0 && GrandCompanies.Count > 0;
|
public bool IsValid() => ItemId > 0 && GrandCompanies.Count > 0;
|
||||||
public bool Limited => GrandCompanies.Count < 3;
|
|
||||||
|
|
||||||
public bool Equals(GcRewardItem? other)
|
public bool Equals(GcRewardItem? other)
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
||||||
|
|
||||||
namespace Deliveroo.GameData;
|
namespace Deliveroo.GameData;
|
||||||
@ -10,40 +10,34 @@ internal sealed class GcRewardsCache
|
|||||||
{
|
{
|
||||||
public GcRewardsCache(IDataManager dataManager)
|
public GcRewardsCache(IDataManager dataManager)
|
||||||
{
|
{
|
||||||
var categories = dataManager.GetExcelSheet<GCScripShopCategory>()
|
var categories = dataManager.GetExcelSheet<GCScripShopCategory>()!
|
||||||
.Where(x => x.RowId > 0)
|
.Where(x => x.RowId > 0)
|
||||||
.ToDictionary(x => x.RowId,
|
.ToDictionary(x => x.RowId,
|
||||||
x =>
|
x =>
|
||||||
(GrandCompany: (GrandCompany)x.GrandCompany.RowId,
|
(GrandCompany: (GrandCompany)x.GrandCompany.Row,
|
||||||
Tier: (RewardTier)x.Tier,
|
Tier: (RewardTier)x.Tier,
|
||||||
SubCategory: (RewardSubCategory)x.SubCategory));
|
SubCategory: (RewardSubCategory)x.SubCategory));
|
||||||
|
|
||||||
Rewards = dataManager.GetSubrowExcelSheet<GCScripShopItem>()
|
Rewards = dataManager.GetExcelSheet<GCScripShopItem>()!
|
||||||
.SelectMany(x => x)
|
.Where(x => x.RowId > 0 && x.Item.Row > 0)
|
||||||
.Where(x => x.RowId > 0 && x.Item.RowId > 0)
|
|
||||||
.GroupBy(item =>
|
.GroupBy(item =>
|
||||||
{
|
{
|
||||||
var category = categories[item.RowId];
|
var category = categories[item.RowId];
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
ItemId = item.Item.RowId,
|
ItemId = item.Item.Row,
|
||||||
Name = item.Item.Value.Name.ToString(),
|
Name = item.Item.Value!.Name.ToString(),
|
||||||
IconId = item.Item.RowId == ItemIds.Venture ? 25917 : item.Item.Value.Icon,
|
|
||||||
category.Tier,
|
category.Tier,
|
||||||
category.SubCategory,
|
category.SubCategory,
|
||||||
RequiredRank = item.RequiredGrandCompanyRank.RowId,
|
RequiredRank = item.RequiredGrandCompanyRank.Row,
|
||||||
item.Item.Value.StackSize,
|
item.Item!.Value.StackSize,
|
||||||
SealCost = item.CostGCSeals,
|
SealCost = item.CostGCSeals,
|
||||||
InventoryLimit = item.Item.Value.IsUnique ? 1
|
|
||||||
: item.Item.RowId == ItemIds.Venture ? item.Item.Value.StackSize
|
|
||||||
: int.MaxValue,
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.Select(item => new GcRewardItem
|
.Select(item => new GcRewardItem
|
||||||
{
|
{
|
||||||
ItemId = item.Key.ItemId,
|
ItemId = item.Key.ItemId,
|
||||||
Name = item.Key.Name,
|
Name = item.Key.Name,
|
||||||
IconId = (ushort)item.Key.IconId,
|
|
||||||
Tier = item.Key.Tier,
|
Tier = item.Key.Tier,
|
||||||
SubCategory = item.Key.SubCategory,
|
SubCategory = item.Key.SubCategory,
|
||||||
RequiredRank = item.Key.RequiredRank,
|
RequiredRank = item.Key.RequiredRank,
|
||||||
@ -52,7 +46,6 @@ internal sealed class GcRewardsCache
|
|||||||
GrandCompanies = item.Select(x => categories[x.RowId].GrandCompany)
|
GrandCompanies = item.Select(x => categories[x.RowId].GrandCompany)
|
||||||
.ToList()
|
.ToList()
|
||||||
.AsReadOnly(),
|
.AsReadOnly(),
|
||||||
InventoryLimit = item.Key.InventoryLimit,
|
|
||||||
})
|
})
|
||||||
.ToList()
|
.ToList()
|
||||||
.AsReadOnly();
|
.AsReadOnly();
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Deliveroo.GameData;
|
|
||||||
|
|
||||||
internal static class InternalConfiguration
|
|
||||||
{
|
|
||||||
public static readonly IReadOnlyList<uint> QuickVentureExclusiveItems = new List<uint>
|
|
||||||
{
|
|
||||||
// Weapons
|
|
||||||
1662, // Thormoen's Pride
|
|
||||||
1799, // Sibold's Reach
|
|
||||||
1870, // Gerbald's Redspike
|
|
||||||
1731, // Symon's Honeyclaws
|
|
||||||
1940, // Alesone's Songbow
|
|
||||||
2132, // Aubriest's Whisper
|
|
||||||
2043, // Chiran Zabran's Tempest
|
|
||||||
|
|
||||||
2271, // Thormoen's Purpose
|
|
||||||
2290, // Aubriest's Allegory
|
|
||||||
|
|
||||||
// Armor
|
|
||||||
2752, // Peregrine Helm
|
|
||||||
2820, // Red Onion Helm
|
|
||||||
2823, // Veteran's Pot Helm
|
|
||||||
2822, // Explorer's Bandana
|
|
||||||
2821, // Explorer's Calot
|
|
||||||
3170, // Explorer's Tabard
|
|
||||||
3168, // Veteran's Acton
|
|
||||||
3167, // Explorer's Tunic
|
|
||||||
3171, // Mage's Halfrobe
|
|
||||||
3676, // Spiked Armguards
|
|
||||||
3644, // Mage's Halfgloves
|
|
||||||
3418, // Thormoen's Subligar
|
|
||||||
3420, // Explorer's Breeches
|
|
||||||
3419, // Mage's Chausses
|
|
||||||
3864, // Explorer's Sabatons
|
|
||||||
3865, // Explorer's Moccasins
|
|
||||||
3863, // Mage's Pattens
|
|
||||||
|
|
||||||
// Accessories
|
|
||||||
4256, // Stonewall Earrings
|
|
||||||
4249, // Explorer's Earrings
|
|
||||||
4250, // Mage's Earrings
|
|
||||||
4257, // Blessed Earrings
|
|
||||||
4359, // Stonewall Choker
|
|
||||||
4357, // Explorer's Choker
|
|
||||||
4358, // Mage's Choker
|
|
||||||
4498, // Stonewall Ring
|
|
||||||
4483, // Explorer's Ring
|
|
||||||
4484, // Mage's Ring
|
|
||||||
4499, // Blessed Ring
|
|
||||||
}
|
|
||||||
.ToList()
|
|
||||||
.AsReadOnly();
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Lumina.Excel.Sheets;
|
|
||||||
|
|
||||||
namespace Deliveroo.GameData;
|
|
||||||
|
|
||||||
internal sealed class ItemCache
|
|
||||||
{
|
|
||||||
private readonly Dictionary<string, HashSet<uint>> _itemNamesToIds = new();
|
|
||||||
|
|
||||||
public ItemCache(IDataManager dataManager)
|
|
||||||
{
|
|
||||||
foreach (var item in dataManager.GetExcelSheet<Item>())
|
|
||||||
{
|
|
||||||
string name = item.Name.ToString();
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (_itemNamesToIds.TryGetValue(name, out HashSet<uint>? itemIds))
|
|
||||||
itemIds.Add(item.RowId);
|
|
||||||
else
|
|
||||||
_itemNamesToIds.Add(name, [item.RowId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashSet<uint> GetItemIdFromItemName(string name) =>
|
|
||||||
_itemNamesToIds.TryGetValue(name, out var itemIds) ? itemIds : [];
|
|
||||||
}
|
|
@ -1,8 +1,11 @@
|
|||||||
namespace Deliveroo.GameData;
|
using System.Runtime.InteropServices;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||||
|
|
||||||
|
namespace Deliveroo.GameData;
|
||||||
|
|
||||||
internal sealed class TurnInItem
|
internal sealed class TurnInItem
|
||||||
{
|
{
|
||||||
public required uint ItemId { get; init; }
|
public required int ItemId { get; init; }
|
||||||
public required string Name { get; init; }
|
public required string Name { get; init; }
|
||||||
public required int SealsWithBonus { get; init; }
|
public required int SealsWithBonus { get; init; }
|
||||||
public required int SealsWithoutBonus { get; init; }
|
public required int SealsWithoutBonus { get; init; }
|
||||||
|
@ -1,244 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Dalamud.Game.ClientState.Objects;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
|
||||||
using Dalamud.Memory;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Deliveroo.External;
|
|
||||||
using Deliveroo.GameData;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Control;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
||||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.Excel;
|
|
||||||
using Lumina.Excel;
|
|
||||||
using Lumina.Excel.Sheets;
|
|
||||||
using Lumina.Text.ReadOnly;
|
|
||||||
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
|
||||||
|
|
||||||
namespace Deliveroo;
|
|
||||||
|
|
||||||
internal sealed class GameFunctions : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IObjectTable _objectTable;
|
|
||||||
private readonly IClientState _clientState;
|
|
||||||
private readonly ITargetManager _targetManager;
|
|
||||||
private readonly ExternalPluginHandler _externalPluginHandler;
|
|
||||||
private readonly IPluginLog _pluginLog;
|
|
||||||
private readonly ReadOnlyDictionary<uint, GcRankInfo> _gcRankInfo;
|
|
||||||
private readonly Dictionary<uint, int> _retainerItemCache = new();
|
|
||||||
|
|
||||||
public GameFunctions(IObjectTable objectTable, IClientState clientState, ITargetManager targetManager,
|
|
||||||
IDataManager dataManager, ExternalPluginHandler externalPluginHandler, IPluginLog pluginLog)
|
|
||||||
{
|
|
||||||
_objectTable = objectTable;
|
|
||||||
_clientState = clientState;
|
|
||||||
_targetManager = targetManager;
|
|
||||||
_externalPluginHandler = externalPluginHandler;
|
|
||||||
_pluginLog = pluginLog;
|
|
||||||
|
|
||||||
|
|
||||||
_gcRankInfo = dataManager.GetExcelSheet<GrandCompanyRank>().Where(x => x.RowId > 0)
|
|
||||||
.ToDictionary(x => x.RowId, x => new GcRankInfo
|
|
||||||
{
|
|
||||||
NameTwinAddersMale = ExtractRankName<GCRankGridaniaMaleText>(dataManager, x.RowId, r => r.Singular),
|
|
||||||
NameTwinAddersFemale = ExtractRankName<GCRankGridaniaFemaleText>(dataManager, x.RowId, r => r.Singular),
|
|
||||||
NameMaelstromMale = ExtractRankName<GCRankLimsaMaleText>(dataManager, x.RowId, r => r.Singular),
|
|
||||||
NameMaelstromFemale = ExtractRankName<GCRankLimsaFemaleText>(dataManager, x.RowId, r => r.Singular),
|
|
||||||
NameImmortalFlamesMale = ExtractRankName<GCRankUldahMaleText>(dataManager, x.RowId, r => r.Singular),
|
|
||||||
NameImmortalFlamesFemale =
|
|
||||||
ExtractRankName<GCRankUldahFemaleText>(dataManager, x.RowId, r => r.Singular),
|
|
||||||
MaxSeals = x.MaxSeals,
|
|
||||||
RequiredSeals = x.RequiredSeals,
|
|
||||||
RequiredHuntingLog = x.Unknown0,
|
|
||||||
})
|
|
||||||
.AsReadOnly();
|
|
||||||
|
|
||||||
_clientState.Logout += Logout;
|
|
||||||
_clientState.TerritoryChanged += TerritoryChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ExtractRankName<T>(IDataManager dataManager, uint rankId, Func<T, ReadOnlySeString> func)
|
|
||||||
where T : struct, IExcelRow<T>
|
|
||||||
{
|
|
||||||
return func(dataManager.GetExcelSheet<T>().GetRow(rankId)).ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void Logout(int type, int code)
|
|
||||||
{
|
|
||||||
_retainerItemCache.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TerritoryChanged(ushort territoryType)
|
|
||||||
{
|
|
||||||
// there is no GC area that is in the same zone as a retainer bell, so this should be often enough.
|
|
||||||
_retainerItemCache.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void InteractWithTarget(IGameObject obj)
|
|
||||||
{
|
|
||||||
_pluginLog.Information($"Setting target to {obj}");
|
|
||||||
if (_targetManager.Target == null || _targetManager.Target.EntityId != obj.EntityId)
|
|
||||||
{
|
|
||||||
_targetManager.Target = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
TargetSystem.Instance()->InteractWithObject(
|
|
||||||
(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)obj.Address, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe int GetCurrentSealCount()
|
|
||||||
{
|
|
||||||
InventoryManager* inventoryManager = InventoryManager.Instance();
|
|
||||||
switch ((GrandCompany)PlayerState.Instance()->GrandCompany)
|
|
||||||
{
|
|
||||||
case GrandCompany.Maelstrom:
|
|
||||||
return inventoryManager->GetInventoryItemCount(20, false, false, false);
|
|
||||||
case GrandCompany.TwinAdder:
|
|
||||||
return inventoryManager->GetInventoryItemCount(21, false, false, false);
|
|
||||||
case GrandCompany.ImmortalFlames:
|
|
||||||
return inventoryManager->GetInventoryItemCount(22, false, false, false);
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe GrandCompany GetGrandCompany() => (GrandCompany)PlayerState.Instance()->GrandCompany;
|
|
||||||
|
|
||||||
public unsafe byte GetGrandCompanyRank() => PlayerState.Instance()->GetGrandCompanyRank();
|
|
||||||
|
|
||||||
public float GetDistanceToNpc(int npcId, out IGameObject? o)
|
|
||||||
{
|
|
||||||
foreach (var obj in _objectTable)
|
|
||||||
{
|
|
||||||
if (obj.ObjectKind == ObjectKind.EventNpc && obj is ICharacter c)
|
|
||||||
{
|
|
||||||
if (c.DataId == npcId)
|
|
||||||
{
|
|
||||||
o = obj;
|
|
||||||
return Vector3.Distance(_clientState.LocalPlayer?.Position ?? Vector3.Zero, c.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
o = null;
|
|
||||||
return float.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetNpcId(IGameObject obj)
|
|
||||||
{
|
|
||||||
return Marshal.ReadInt32(obj.Address + 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetPersonnelOfficerId()
|
|
||||||
{
|
|
||||||
return GetGrandCompany() switch
|
|
||||||
{
|
|
||||||
GrandCompany.Maelstrom => 0xF4B94,
|
|
||||||
GrandCompany.ImmortalFlames => 0xF4B97,
|
|
||||||
GrandCompany.TwinAdder => 0xF4B9A,
|
|
||||||
_ => int.MaxValue,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetQuartermasterId()
|
|
||||||
{
|
|
||||||
return GetGrandCompany() switch
|
|
||||||
{
|
|
||||||
GrandCompany.Maelstrom => 0xF4B93,
|
|
||||||
GrandCompany.ImmortalFlames => 0xF4B96,
|
|
||||||
GrandCompany.TwinAdder => 0xF4B99,
|
|
||||||
_ => int.MaxValue,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GetSealCap() => _gcRankInfo.TryGetValue(GetGrandCompanyRank(), out var rank) ? rank.MaxSeals : 0;
|
|
||||||
|
|
||||||
public uint MaxSealCap => _gcRankInfo[11].MaxSeals;
|
|
||||||
|
|
||||||
public uint GetSealsRequiredForNextRank()
|
|
||||||
=> _gcRankInfo.GetValueOrDefault(GetGrandCompanyRank())?.RequiredSeals ?? 0;
|
|
||||||
|
|
||||||
public byte GetRequiredHuntingLogForNextRank()
|
|
||||||
=> _gcRankInfo.GetValueOrDefault(GetGrandCompanyRank() + 1u)?.RequiredHuntingLog ?? 0;
|
|
||||||
|
|
||||||
public string? GetNextGrandCompanyRankName()
|
|
||||||
{
|
|
||||||
bool female = _clientState.LocalPlayer!.Customize[(int)CustomizeIndex.Gender] == 1;
|
|
||||||
GrandCompany grandCompany = GetGrandCompany();
|
|
||||||
return _gcRankInfo.GetValueOrDefault(GetGrandCompanyRank() + 1u)?.GetName(grandCompany, female);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe int GetItemCount(uint itemId, bool checkRetainerInventory)
|
|
||||||
{
|
|
||||||
InventoryManager* inventoryManager = InventoryManager.Instance();
|
|
||||||
int count = inventoryManager->GetInventoryItemCount(itemId, false, false, false);
|
|
||||||
|
|
||||||
if (checkRetainerInventory)
|
|
||||||
{
|
|
||||||
if (!_retainerItemCache.TryGetValue(itemId, out int retainerCount))
|
|
||||||
{
|
|
||||||
_retainerItemCache[itemId] = retainerCount = (int)_externalPluginHandler.GetRetainerItemCount(itemId);
|
|
||||||
}
|
|
||||||
|
|
||||||
count += retainerCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public decimal GetSealMultiplier()
|
|
||||||
{
|
|
||||||
// priority seal allowance
|
|
||||||
if (_clientState.LocalPlayer!.StatusList.Any(x => x.StatusId == 1078))
|
|
||||||
return 1.15m;
|
|
||||||
|
|
||||||
// seal sweetener 1/2
|
|
||||||
var fcStatus = _clientState.LocalPlayer!.StatusList.FirstOrDefault(x => x.StatusId == 414);
|
|
||||||
if (fcStatus != null)
|
|
||||||
{
|
|
||||||
return 1m + fcStatus.StackCount / 100m;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This returns ALL items that can be turned in, regardless of filter settings.
|
|
||||||
/// </summary>
|
|
||||||
public unsafe List<TurnInItem> BuildTurnInList(AgentGrandCompanySupply* agent)
|
|
||||||
{
|
|
||||||
List<TurnInItem> list = new();
|
|
||||||
for (int i = 11 /* skip over provisioning items */; i < agent->NumItems; ++i)
|
|
||||||
{
|
|
||||||
GrandCompanyItem item = agent->ItemArray[i];
|
|
||||||
|
|
||||||
// this includes all items, even if they don't match the filter
|
|
||||||
list.Add(new TurnInItem
|
|
||||||
{
|
|
||||||
ItemId = item.ItemId,
|
|
||||||
Name = MemoryHelper.ReadSeString(&item.ItemName).ToString(),
|
|
||||||
SealsWithBonus = (int)Math.Round(item.SealReward * GetSealMultiplier(), MidpointRounding.AwayFromZero),
|
|
||||||
SealsWithoutBonus = item.SealReward,
|
|
||||||
ItemUiCategory = item.ItemUiCategory,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return list.OrderByDescending(x => x.SealsWithBonus)
|
|
||||||
.ThenBy(x => x.ItemUiCategory)
|
|
||||||
.ThenBy(x => x.ItemId)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_clientState.TerritoryChanged -= TerritoryChanged;
|
|
||||||
_clientState.Logout -= Logout;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
using Dalamud.Game.Text.SeStringHandling;
|
|
||||||
|
|
||||||
namespace Deliveroo;
|
|
||||||
|
|
||||||
internal interface IDeliveryResult;
|
|
||||||
|
|
||||||
internal sealed class MessageDeliveryResult : IDeliveryResult
|
|
||||||
{
|
|
||||||
public SeString? Message { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class NoDeliveryResult : IDeliveryResult;
|
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using Deliveroo.GameData;
|
||||||
using Deliveroo.GameData;
|
|
||||||
|
|
||||||
namespace Deliveroo;
|
namespace Deliveroo;
|
||||||
|
|
||||||
@ -7,14 +6,9 @@ internal sealed class PurchaseItemRequest
|
|||||||
{
|
{
|
||||||
public required uint ItemId { get; init; }
|
public required uint ItemId { get; init; }
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public required uint EffectiveLimit { get; set; }
|
public required uint EffectiveLimit { get; init; }
|
||||||
public required uint SealCost { get; init; }
|
public required uint SealCost { get; init; }
|
||||||
public required RewardTier Tier { get; init; }
|
public required RewardTier Tier { get; init; }
|
||||||
public required RewardSubCategory SubCategory { get; init; }
|
public required RewardSubCategory SubCategory { get; init; }
|
||||||
public required uint StackSize { get; init; }
|
public required uint StackSize { get; init; }
|
||||||
public required Configuration.PurchaseType Type { get; init; }
|
|
||||||
public required bool CheckRetainerInventory { get; init; }
|
|
||||||
|
|
||||||
public Action<int>? OnPurchase { get; set; }
|
|
||||||
public long TemporaryPurchaseQuantity { get; set; }
|
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,8 @@ internal enum Stage
|
|||||||
SelectItemToTurnIn,
|
SelectItemToTurnIn,
|
||||||
TurnInSelected,
|
TurnInSelected,
|
||||||
FinalizeTurnIn,
|
FinalizeTurnIn,
|
||||||
CloseGcSupplySelectString,
|
CloseGcSupply,
|
||||||
CloseGcSupplySelectStringThenStop,
|
CloseGcSupplyThenStop,
|
||||||
CloseGcSupplyWindowThenStop,
|
|
||||||
|
|
||||||
TargetQuartermaster,
|
TargetQuartermaster,
|
||||||
SelectRewardTier,
|
SelectRewardTier,
|
||||||
@ -21,6 +20,4 @@ internal enum Stage
|
|||||||
|
|
||||||
RequestStop,
|
RequestStop,
|
||||||
Stopped,
|
Stopped,
|
||||||
|
|
||||||
SingleFinalizeTurnIn,
|
|
||||||
}
|
}
|
||||||
|
@ -2,49 +2,32 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
using Dalamud.Interface.Textures.TextureWraps;
|
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
|
||||||
using Deliveroo.GameData;
|
using Deliveroo.GameData;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using LLib;
|
using LLib;
|
||||||
using LLib.ImGui;
|
|
||||||
|
|
||||||
namespace Deliveroo.Windows;
|
namespace Deliveroo.Windows;
|
||||||
|
|
||||||
internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
internal sealed class ConfigWindow : Window
|
||||||
{
|
{
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
private readonly DeliverooPlugin _plugin;
|
private readonly DeliverooPlugin _plugin;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly GcRewardsCache _gcRewardsCache;
|
private readonly GcRewardsCache _gcRewardsCache;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly IPluginLog _pluginLog;
|
private readonly IPluginLog _pluginLog;
|
||||||
private readonly IconCache _iconCache;
|
|
||||||
private readonly GameFunctions _gameFunctions;
|
|
||||||
|
|
||||||
private readonly IReadOnlyDictionary<uint, GcRewardItem> _itemLookup;
|
private readonly IReadOnlyDictionary<uint, GcRewardItem> _itemLookup;
|
||||||
|
private uint _dragDropSource;
|
||||||
|
|
||||||
private readonly List<(VirtualKey Key, string Label)> _keyCodes =
|
public ConfigWindow(DalamudPluginInterface pluginInterface, DeliverooPlugin plugin, Configuration configuration,
|
||||||
[
|
GcRewardsCache gcRewardsCache, IClientState clientState, IPluginLog pluginLog)
|
||||||
(VirtualKey.NO_KEY, "Disabled"),
|
|
||||||
(VirtualKey.CONTROL, "CTRL"),
|
|
||||||
(VirtualKey.SHIFT, "SHIFT"),
|
|
||||||
(VirtualKey.MENU, "ALT"),
|
|
||||||
];
|
|
||||||
|
|
||||||
private string _searchString = string.Empty;
|
|
||||||
private Configuration.PurchaseOption? _dragDropSource;
|
|
||||||
|
|
||||||
public ConfigWindow(IDalamudPluginInterface pluginInterface, DeliverooPlugin plugin, Configuration configuration,
|
|
||||||
GcRewardsCache gcRewardsCache, IClientState clientState, IPluginLog pluginLog, IconCache iconCache,
|
|
||||||
GameFunctions gameFunctions)
|
|
||||||
: base("Deliveroo - Configuration###DeliverooConfig")
|
: base("Deliveroo - Configuration###DeliverooConfig")
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
@ -53,25 +36,23 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
|||||||
_gcRewardsCache = gcRewardsCache;
|
_gcRewardsCache = gcRewardsCache;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_pluginLog = pluginLog;
|
_pluginLog = pluginLog;
|
||||||
_iconCache = iconCache;
|
|
||||||
_gameFunctions = gameFunctions;
|
|
||||||
|
|
||||||
_itemLookup = _gcRewardsCache.RewardLookup;
|
_itemLookup = _gcRewardsCache.RewardLookup;
|
||||||
|
|
||||||
Size = new Vector2(440, 300);
|
Size = new Vector2(420, 300);
|
||||||
SizeCondition = ImGuiCond.FirstUseEver;
|
SizeCondition = ImGuiCond.Appearing;
|
||||||
|
|
||||||
SizeConstraints = new WindowSizeConstraints
|
SizeConstraints = new WindowSizeConstraints
|
||||||
{
|
{
|
||||||
MinimumSize = new Vector2(440, 300),
|
MinimumSize = new Vector2(420, 300),
|
||||||
MaximumSize = new Vector2(9999, 9999),
|
MaximumSize = new Vector2(9999, 9999),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public WindowConfig WindowConfig => _configuration.ConfigWindowConfig;
|
|
||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
|
LImGui.AddPatreonIcon(_pluginInterface);
|
||||||
|
|
||||||
if (_configuration.AddVentureIfNoItemToPurchaseSelected())
|
if (_configuration.AddVentureIfNoItemToPurchaseSelected())
|
||||||
Save();
|
Save();
|
||||||
|
|
||||||
@ -89,80 +70,49 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
|||||||
{
|
{
|
||||||
if (ImGui.BeginTabItem("Items for Auto-Buy"))
|
if (ImGui.BeginTabItem("Items for Auto-Buy"))
|
||||||
{
|
{
|
||||||
Configuration.PurchaseOption? itemToRemove = null;
|
uint? itemToRemove = null;
|
||||||
Configuration.PurchaseOption? itemToAdd = null;
|
uint? itemToAdd = null;
|
||||||
int indexToAdd = 0;
|
int indexToAdd = 0;
|
||||||
if (ImGui.BeginChild("Items", new Vector2(-1, -ImGui.GetFrameHeightWithSpacing()), true,
|
if (ImGui.BeginChild("Items", new Vector2(-1, -30), true, ImGuiWindowFlags.NoSavedSettings))
|
||||||
ImGuiWindowFlags.NoSavedSettings))
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _configuration.ItemsAvailableToPurchase.Count; ++i)
|
for (int i = 0; i < _configuration.ItemsAvailableForPurchase.Count; ++i)
|
||||||
{
|
{
|
||||||
var purchaseOption = _configuration.ItemsAvailableToPurchase[i];
|
uint itemId = _configuration.ItemsAvailableForPurchase[i];
|
||||||
ImGui.PushID($"###Item{i}");
|
ImGui.PushID($"###Item{i}");
|
||||||
ImGui.BeginDisabled(
|
ImGui.BeginDisabled(
|
||||||
_configuration.ItemsAvailableToPurchase.Count == 1 && purchaseOption.ItemId == ItemIds.Venture);
|
_configuration.ItemsAvailableForPurchase.Count == 1 && itemId == ItemIds.Venture);
|
||||||
|
|
||||||
var item = _itemLookup[purchaseOption.ItemId];
|
ImGui.Selectable(_itemLookup[itemId].Name);
|
||||||
using (var icon = _iconCache.GetIcon(item.IconId))
|
|
||||||
|
if (ImGui.BeginDragDropSource())
|
||||||
{
|
{
|
||||||
Vector2 pos = ImGui.GetCursorPos();
|
ImGui.SetDragDropPayload("DeliverooDragDrop", nint.Zero, 0);
|
||||||
Vector2 iconSize = new Vector2(ImGui.GetTextLineHeight() + ImGui.GetStyle().ItemSpacing.Y);
|
_dragDropSource = itemId;
|
||||||
|
|
||||||
if (icon != null)
|
ImGui.EndDragDropSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.BeginDragDropTarget())
|
||||||
|
{
|
||||||
|
if (_dragDropSource > 0 &&
|
||||||
|
ImGui.AcceptDragDropPayload("DeliverooDragDrop").NativePtr != null)
|
||||||
{
|
{
|
||||||
ImGui.SetCursorPos(pos + new Vector2(iconSize.X + ImGui.GetStyle().FramePadding.X,
|
itemToAdd = _dragDropSource;
|
||||||
ImGui.GetStyle().ItemSpacing.Y / 2));
|
indexToAdd = i;
|
||||||
|
|
||||||
|
_dragDropSource = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Selectable(
|
ImGui.EndDragDropTarget();
|
||||||
$"{item.Name}{(item.Limited ? $" {SeIconChar.Hyadelyn.ToIconString()}" : "")}{(purchaseOption.SameQuantityForAllLists ? $" {((SeIconChar)57412).ToIconString()} (Limit: {purchaseOption.GlobalLimit:N0})" : "")}",
|
}
|
||||||
false, ImGuiSelectableFlags.SpanAllColumns);
|
|
||||||
|
|
||||||
if (ImGui.BeginDragDropSource())
|
ImGui.OpenPopupOnItemClick($"###ctx{i}", ImGuiPopupFlags.MouseButtonRight);
|
||||||
{
|
if (ImGui.BeginPopup($"###ctx{i}"))
|
||||||
ImGui.SetDragDropPayload("DeliverooDragDrop", nint.Zero, 0);
|
{
|
||||||
_dragDropSource = purchaseOption;
|
if (ImGui.Selectable($"Remove {_itemLookup[itemId].Name}"))
|
||||||
|
itemToRemove = itemId;
|
||||||
|
|
||||||
ImGui.EndDragDropSource();
|
ImGui.EndPopup();
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.BeginDragDropTarget())
|
|
||||||
{
|
|
||||||
if (_dragDropSource != null &&
|
|
||||||
ImGui.AcceptDragDropPayload("DeliverooDragDrop").NativePtr != null)
|
|
||||||
{
|
|
||||||
itemToAdd = _dragDropSource;
|
|
||||||
indexToAdd = i;
|
|
||||||
|
|
||||||
_dragDropSource = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDragDropTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.OpenPopupOnItemClick($"###ctx{i}", ImGuiPopupFlags.MouseButtonRight);
|
|
||||||
if (ImGui.BeginPopup($"###ctx{i}"))
|
|
||||||
{
|
|
||||||
bool sameQuantityForAllLists = purchaseOption.SameQuantityForAllLists;
|
|
||||||
if (ImGui.MenuItem("Use same quantity for global and character-specific lists", null,
|
|
||||||
ref sameQuantityForAllLists))
|
|
||||||
{
|
|
||||||
purchaseOption.SameQuantityForAllLists = sameQuantityForAllLists;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.MenuItem($"Remove {_itemLookup[purchaseOption.ItemId].Name}"))
|
|
||||||
itemToRemove = purchaseOption;
|
|
||||||
|
|
||||||
ImGui.EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (icon != null)
|
|
||||||
{
|
|
||||||
ImGui.SameLine(0, 0);
|
|
||||||
ImGui.SetCursorPos(pos);
|
|
||||||
ImGui.Image(icon.ImGuiHandle, iconSize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
@ -174,63 +124,33 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
|||||||
|
|
||||||
if (itemToRemove != null)
|
if (itemToRemove != null)
|
||||||
{
|
{
|
||||||
_configuration.ItemsAvailableToPurchase.Remove(itemToRemove);
|
_configuration.ItemsAvailableForPurchase.Remove(itemToRemove.Value);
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemToAdd != null)
|
if (itemToAdd != null)
|
||||||
{
|
{
|
||||||
_configuration.ItemsAvailableToPurchase.Remove(itemToAdd);
|
_configuration.ItemsAvailableForPurchase.Remove(itemToAdd.Value);
|
||||||
_configuration.ItemsAvailableToPurchase.Insert(indexToAdd, itemToAdd);
|
_configuration.ItemsAvailableForPurchase.Insert(indexToAdd, itemToAdd.Value);
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<(uint ItemId, string Name, ushort IconId, bool Limited)> comboValues = _gcRewardsCache.Rewards
|
List<(uint ItemId, string Name)> comboValues = _gcRewardsCache.Rewards
|
||||||
.Where(x => x.SubCategory is RewardSubCategory.Materials or RewardSubCategory.Materiel)
|
.Where(x => x.SubCategory is RewardSubCategory.Materials or RewardSubCategory.Materiel)
|
||||||
.Where(x => _configuration.ItemsAvailableToPurchase.All(y => y.ItemId != x.ItemId))
|
.Where(x => x.StackSize > 1)
|
||||||
.Select(x => (x.ItemId, x.Name, x.IconId, x.Limited))
|
.Where(x => !_configuration.ItemsAvailableForPurchase.Contains(x.ItemId))
|
||||||
|
.Select(x => (x.ItemId, x.Name))
|
||||||
.OrderBy(x => x.Name)
|
.OrderBy(x => x.Name)
|
||||||
.ThenBy(x => x.GetHashCode())
|
.ThenBy(x => x.GetHashCode())
|
||||||
.ToList();
|
.ToList();
|
||||||
|
comboValues.Insert(0, (0, ""));
|
||||||
|
|
||||||
if (ImGui.BeginCombo($"##ItemSelection", "Add Item...", ImGuiComboFlags.HeightLarge))
|
int currentItem = 0;
|
||||||
|
if (ImGui.Combo("Add Item", ref currentItem, comboValues.Select(x => x.Name).ToArray(), comboValues.Count)
|
||||||
|
&& comboValues[currentItem].ItemId != GcRewardItem.None.ItemId)
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
_configuration.ItemsAvailableForPurchase.Add(comboValues[currentItem].ItemId);
|
||||||
bool addFirst = ImGui.InputTextWithHint("", "Filter...", ref _searchString, 256,
|
Save();
|
||||||
ImGuiInputTextFlags.AutoSelectAll | ImGuiInputTextFlags.EnterReturnsTrue);
|
|
||||||
|
|
||||||
foreach (var item in comboValues.Where(x =>
|
|
||||||
x.Name.Contains(_searchString, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
using (var icon = _iconCache.GetIcon(item.IconId))
|
|
||||||
{
|
|
||||||
if (icon != null)
|
|
||||||
{
|
|
||||||
ImGui.Image(icon.ImGuiHandle, new Vector2(ImGui.GetFrameHeight()));
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetStyle().FramePadding.X);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool addThis =
|
|
||||||
ImGui.Selectable(
|
|
||||||
$"{item.Name}{(item.Limited ? $" {SeIconChar.Hyadelyn.ToIconString()}" : "")}##SelectVenture{item.IconId}");
|
|
||||||
if (addThis || addFirst)
|
|
||||||
{
|
|
||||||
_configuration.ItemsAvailableToPurchase.Add(new Configuration.PurchaseOption
|
|
||||||
{ ItemId = item.ItemId });
|
|
||||||
|
|
||||||
if (addFirst)
|
|
||||||
{
|
|
||||||
addFirst = false;
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndCombo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
@ -244,7 +164,7 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
|||||||
if (_clientState is { IsLoggedIn: true, LocalContentId: > 0 })
|
if (_clientState is { IsLoggedIn: true, LocalContentId: > 0 })
|
||||||
{
|
{
|
||||||
string currentCharacterName = _clientState.LocalPlayer!.Name.ToString();
|
string currentCharacterName = _clientState.LocalPlayer!.Name.ToString();
|
||||||
string currentWorldName = _clientState.LocalPlayer.HomeWorld.Value.Name.ToString();
|
string currentWorldName = _clientState.LocalPlayer.HomeWorld.GameData!.Name.ToString();
|
||||||
ImGui.Text($"Current Character: {currentCharacterName} @ {currentWorldName}");
|
ImGui.Text($"Current Character: {currentCharacterName} @ {currentWorldName}");
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
@ -288,20 +208,9 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
|||||||
charConfiguration.Save(_pluginInterface);
|
charConfiguration.Save(_pluginInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
if (ImGui.IsItemHovered())
|
||||||
ImGuiComponents.HelpMarker(
|
ImGui.SetTooltip(
|
||||||
"The default filter for all characters is 'Hide Gear Set Items', but you may want to override this to hide all Armoury Chest items (regardless of whether they're part of a gear set) e.g. for your main character.");
|
"The default filter for all characters is 'Hide Gear Set Items', but you may want to override this to hide all Armoury Chest items (regardless of whether they're part of a gear set) e.g. for your main character.");
|
||||||
|
|
||||||
bool ignoreMinimumSealsToKeep = charConfiguration.IgnoreMinimumSealsToKeep;
|
|
||||||
if (ImGui.Checkbox("Ignore 'Minimum Seals to keep' setting", ref ignoreMinimumSealsToKeep))
|
|
||||||
{
|
|
||||||
charConfiguration.IgnoreMinimumSealsToKeep = ignoreMinimumSealsToKeep;
|
|
||||||
charConfiguration.Save(_pluginInterface);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGuiComponents.HelpMarker(
|
|
||||||
"When enabled, all GC seals will be spent. This is effectively the same as setting 'Minimum Seals to keep' to 0.");
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
@ -352,115 +261,18 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
|||||||
{
|
{
|
||||||
if (ImGui.BeginTabItem("Additional Settings"))
|
if (ImGui.BeginTabItem("Additional Settings"))
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 120);
|
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 100);
|
||||||
int reservedSealCount = _configuration.ReservedSealCount;
|
int reservedSealCount = _configuration.ReservedSealCount;
|
||||||
if (ImGui.InputInt("Minimum Seals to keep (e.g. for Squadron Missions)", ref reservedSealCount, 1000))
|
if (ImGui.InputInt("Minimum Seals to keep (e.g. for Squadron Missions)", ref reservedSealCount, 1000))
|
||||||
{
|
{
|
||||||
_configuration.ReservedSealCount =
|
_configuration.ReservedSealCount = Math.Max(0, Math.Min(90_000, reservedSealCount));
|
||||||
Math.Max(0, Math.Min((int)_gameFunctions.MaxSealCap, reservedSealCount));
|
|
||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.BeginDisabled(reservedSealCount <= 0);
|
|
||||||
bool reserveDifferentSealCountAtMaxRank = _configuration.ReserveDifferentSealCountAtMaxRank;
|
|
||||||
if (ImGui.Checkbox("Use a different amount at max rank", ref reserveDifferentSealCountAtMaxRank))
|
|
||||||
{
|
|
||||||
_configuration.ReserveDifferentSealCountAtMaxRank = reserveDifferentSealCountAtMaxRank;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reserveDifferentSealCountAtMaxRank)
|
|
||||||
{
|
|
||||||
float indentSize = ImGui.GetFrameHeight() + ImGui.GetStyle().ItemInnerSpacing.X;
|
|
||||||
ImGui.Indent(indentSize);
|
|
||||||
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 100);
|
|
||||||
int reservedSealCountAtMaxRank = _configuration.ReservedSealCountAtMaxRank;
|
|
||||||
if (ImGui.InputInt("Minimum seals to keep at max rank", ref reservedSealCountAtMaxRank))
|
|
||||||
{
|
|
||||||
_configuration.ReservedSealCountAtMaxRank = Math.Max(0,
|
|
||||||
Math.Min((int)_gameFunctions.MaxSealCap, reservedSealCountAtMaxRank));
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Unindent(indentSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 120);
|
|
||||||
int selectedKey = Math.Max(0, _keyCodes.FindIndex(x => x.Key == _configuration.QuickTurnInKey));
|
|
||||||
if (ImGui.Combo("Quick Turn-In Key", ref selectedKey, _keyCodes.Select(x => x.Label).ToArray(),
|
|
||||||
_keyCodes.Count))
|
|
||||||
{
|
|
||||||
_configuration.QuickTurnInKey = _keyCodes[selectedKey].Key;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 120);
|
|
||||||
var xivChatTypes = Enum.GetValues<XivChatType>()
|
|
||||||
.Where(x => x != XivChatType.StandardEmote)
|
|
||||||
.ToArray();
|
|
||||||
var selectedChatType = Array.IndexOf(xivChatTypes, _configuration.ChatType);
|
|
||||||
string[] chatTypeNames = xivChatTypes
|
|
||||||
.Select(t => t.GetAttribute<XivChatTypeInfoAttribute>()?.FancyName ?? t.ToString())
|
|
||||||
.ToArray();
|
|
||||||
if (ImGui.Combo("Chat channel for status updates", ref selectedChatType, chatTypeNames,
|
|
||||||
chatTypeNames.Length))
|
|
||||||
{
|
|
||||||
_configuration.ChatType = xivChatTypes[selectedChatType];
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 120);
|
|
||||||
List<(int Rank, string Name)> rankUpComboValues = Enumerable.Range(1, 30)
|
|
||||||
.Select(x => x == 1 ? (0, "---") : (x, $"Rank {x}"))
|
|
||||||
.ToList();
|
|
||||||
int pauseAtRank = Math.Max(rankUpComboValues.FindIndex(x => x.Rank == _configuration.PauseAtRank), 0);
|
|
||||||
if (ImGui.Combo("Pause when reaching selected FC Rank", ref pauseAtRank,
|
|
||||||
rankUpComboValues.Select(x => x.Name).ToArray(),
|
|
||||||
rankUpComboValues.Count))
|
|
||||||
{
|
|
||||||
_configuration.PauseAtRank = rankUpComboValues[pauseAtRank].Rank;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 120);
|
|
||||||
string[] behaviorOnOtherWorldNames = { "---", "Show Warning", "Disable Turn-In" };
|
|
||||||
int behaviorOnOtherWorld = (int)_configuration.BehaviorOnOtherWorld;
|
|
||||||
if (ImGui.Combo("Behavior when not on Home World", ref behaviorOnOtherWorld,
|
|
||||||
behaviorOnOtherWorldNames, behaviorOnOtherWorldNames.Length))
|
|
||||||
{
|
|
||||||
_configuration.BehaviorOnOtherWorld = (Configuration.EBehaviorOnOtherWorld)behaviorOnOtherWorld;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
bool disableFrameLimiter = _configuration.DisableFrameLimiter;
|
|
||||||
if (ImGui.Checkbox("Disable the game setting 'Limit frame rate when client is inactive'",
|
|
||||||
ref disableFrameLimiter))
|
|
||||||
{
|
|
||||||
_configuration.DisableFrameLimiter = disableFrameLimiter;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Indent();
|
|
||||||
ImGui.BeginDisabled(!disableFrameLimiter);
|
|
||||||
bool uncapFrameRate = _configuration.UncapFrameRate;
|
|
||||||
if (ImGui.Checkbox("Set frame rate to uncapped", ref uncapFrameRate))
|
|
||||||
{
|
|
||||||
_configuration.UncapFrameRate = uncapFrameRate;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
ImGui.Unindent();
|
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void Save() => _pluginInterface.SavePluginConfig(_configuration);
|
private void Save() => _pluginInterface.SavePluginConfig(_configuration);
|
||||||
|
|
||||||
public void SaveWindowConfig() => Save();
|
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,11 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
using Dalamud.Interface.Textures.TextureWraps;
|
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Deliveroo.GameData;
|
using Deliveroo.GameData;
|
||||||
@ -18,64 +15,28 @@ using FFXIVClientStructs.FFXIV.Client.Game;
|
|||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using LLib;
|
using LLib;
|
||||||
using LLib.ImGui;
|
|
||||||
|
|
||||||
namespace Deliveroo.Windows;
|
namespace Deliveroo.Windows;
|
||||||
|
|
||||||
internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configuration.MinimizableWindowConfig>
|
internal sealed class TurnInWindow : Window
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyList<InventoryType> InventoryTypes = new[]
|
|
||||||
{
|
|
||||||
InventoryType.Inventory1,
|
|
||||||
InventoryType.Inventory2,
|
|
||||||
InventoryType.Inventory3,
|
|
||||||
InventoryType.Inventory4,
|
|
||||||
InventoryType.ArmoryMainHand,
|
|
||||||
InventoryType.ArmoryOffHand,
|
|
||||||
InventoryType.ArmoryHead,
|
|
||||||
InventoryType.ArmoryBody,
|
|
||||||
InventoryType.ArmoryHands,
|
|
||||||
InventoryType.ArmoryLegs,
|
|
||||||
InventoryType.ArmoryFeets,
|
|
||||||
InventoryType.ArmoryEar,
|
|
||||||
InventoryType.ArmoryNeck,
|
|
||||||
InventoryType.ArmoryWrist,
|
|
||||||
InventoryType.ArmoryRings,
|
|
||||||
InventoryType.EquippedItems
|
|
||||||
}.AsReadOnly();
|
|
||||||
|
|
||||||
private static readonly string[] StockingTypeLabels = { "Purchase Once", "Keep in Stock" };
|
|
||||||
|
|
||||||
private readonly DeliverooPlugin _plugin;
|
private readonly DeliverooPlugin _plugin;
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly ICondition _condition;
|
private readonly ICondition _condition;
|
||||||
private readonly IClientState _clientState;
|
|
||||||
private readonly GcRewardsCache _gcRewardsCache;
|
private readonly GcRewardsCache _gcRewardsCache;
|
||||||
private readonly ConfigWindow _configWindow;
|
private readonly ConfigWindow _configWindow;
|
||||||
private readonly IconCache _iconCache;
|
|
||||||
private readonly IKeyState _keyState;
|
|
||||||
private readonly GameFunctions _gameFunctions;
|
|
||||||
private readonly TitleBarButton _minimizeButton;
|
|
||||||
|
|
||||||
private bool _state;
|
public TurnInWindow(DeliverooPlugin plugin, DalamudPluginInterface pluginInterface, Configuration configuration,
|
||||||
private Guid? _draggedItem;
|
ICondition condition, GcRewardsCache gcRewardsCache, ConfigWindow configWindow)
|
||||||
|
|
||||||
public TurnInWindow(DeliverooPlugin plugin, IDalamudPluginInterface pluginInterface, Configuration configuration,
|
|
||||||
ICondition condition, IClientState clientState, GcRewardsCache gcRewardsCache, ConfigWindow configWindow,
|
|
||||||
IconCache iconCache, IKeyState keyState, GameFunctions gameFunctions)
|
|
||||||
: base("GC Delivery###DeliverooTurnIn")
|
: base("GC Delivery###DeliverooTurnIn")
|
||||||
{
|
{
|
||||||
_plugin = plugin;
|
_plugin = plugin;
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_condition = condition;
|
_condition = condition;
|
||||||
_clientState = clientState;
|
|
||||||
_gcRewardsCache = gcRewardsCache;
|
_gcRewardsCache = gcRewardsCache;
|
||||||
_configWindow = configWindow;
|
_configWindow = configWindow;
|
||||||
_iconCache = iconCache;
|
|
||||||
_keyState = keyState;
|
|
||||||
_gameFunctions = gameFunctions;
|
|
||||||
|
|
||||||
Position = new Vector2(100, 100);
|
Position = new Vector2(100, 100);
|
||||||
PositionCondition = ImGuiCond.FirstUseEver;
|
PositionCondition = ImGuiCond.FirstUseEver;
|
||||||
@ -86,74 +47,17 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
MaximumSize = new Vector2(500, 999),
|
MaximumSize = new Vector2(500, 999),
|
||||||
};
|
};
|
||||||
|
|
||||||
State = false;
|
Flags = ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoCollapse;
|
||||||
ShowCloseButton = false;
|
ShowCloseButton = false;
|
||||||
AllowClickthrough = false;
|
|
||||||
|
|
||||||
_minimizeButton = new TitleBarButton
|
|
||||||
{
|
|
||||||
Icon = FontAwesomeIcon.Minus,
|
|
||||||
Priority = int.MinValue,
|
|
||||||
IconOffset = new Vector2(1.5f, 1),
|
|
||||||
Click = _ =>
|
|
||||||
{
|
|
||||||
IsMinimized = !IsMinimized;
|
|
||||||
_minimizeButton!.Icon = IsMinimized ? FontAwesomeIcon.WindowMaximize : FontAwesomeIcon.Minus;
|
|
||||||
},
|
|
||||||
AvailableClickthrough = true,
|
|
||||||
};
|
|
||||||
TitleBarButtons.Insert(0, _minimizeButton);
|
|
||||||
|
|
||||||
TitleBarButtons.Add(new TitleBarButton
|
|
||||||
{
|
|
||||||
Icon = FontAwesomeIcon.Cog,
|
|
||||||
IconOffset = new Vector2(1.5f, 1),
|
|
||||||
Click = _ => configWindow.IsOpen = true,
|
|
||||||
Priority = int.MinValue,
|
|
||||||
ShowTooltip = () =>
|
|
||||||
{
|
|
||||||
ImGui.BeginTooltip();
|
|
||||||
ImGui.Text("Open Configuration");
|
|
||||||
ImGui.EndTooltip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public Configuration.MinimizableWindowConfig WindowConfig => _configuration.TurnInWindowConfig;
|
|
||||||
|
|
||||||
private bool IsMinimized
|
|
||||||
{
|
|
||||||
get => WindowConfig.IsMinimized;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
WindowConfig.IsMinimized = value;
|
|
||||||
SaveWindowConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool State
|
|
||||||
{
|
|
||||||
get => _state;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_state = value;
|
|
||||||
if (value)
|
|
||||||
Flags = ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoCollapse;
|
|
||||||
else
|
|
||||||
Flags = ImGuiWindowFlags.AlwaysAutoResize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool State { get; set; }
|
||||||
public decimal Multiplier { private get; set; }
|
public decimal Multiplier { private get; set; }
|
||||||
public string Error { private get; set; } = string.Empty;
|
public string Error { private get; set; } = string.Empty;
|
||||||
|
|
||||||
private bool UseCharacterSpecificItemsToPurchase =>
|
private bool UseCharacterSpecificItemsToPurchase =>
|
||||||
_plugin.CharacterConfiguration is { OverrideItemsToPurchase: true };
|
_plugin.CharacterConfiguration is { OverrideItemsToPurchase: true };
|
||||||
|
|
||||||
private bool IsOnHomeWorld =>
|
|
||||||
_clientState.LocalPlayer == null ||
|
|
||||||
_clientState.LocalPlayer.HomeWorld.RowId == _clientState.LocalPlayer.CurrentWorld.RowId;
|
|
||||||
|
|
||||||
private IItemsToPurchase ItemsWrapper => UseCharacterSpecificItemsToPurchase
|
private IItemsToPurchase ItemsWrapper => UseCharacterSpecificItemsToPurchase
|
||||||
? new CharacterSpecificItemsToPurchase(_plugin.CharacterConfiguration!, _pluginInterface)
|
? new CharacterSpecificItemsToPurchase(_plugin.CharacterConfiguration!, _pluginInterface)
|
||||||
: new GlobalItemsToPurchase(_configuration, _pluginInterface);
|
: new GlobalItemsToPurchase(_configuration, _pluginInterface);
|
||||||
@ -162,50 +66,28 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
GrandCompany grandCompany = _gameFunctions.GetGrandCompany();
|
GrandCompany grandCompany = _plugin.GetGrandCompany();
|
||||||
if (grandCompany == GrandCompany.None)
|
if (grandCompany == GrandCompany.None)
|
||||||
return new List<PurchaseItemRequest>();
|
return new List<PurchaseItemRequest>();
|
||||||
|
|
||||||
var rank = _gameFunctions.GetGrandCompanyRank();
|
var rank = _plugin.GetGrandCompanyRank();
|
||||||
return ItemsWrapper.GetItemsToPurchase()
|
return ItemsWrapper.GetItemsToPurchase()
|
||||||
.Where(x => x.ItemId != GcRewardItem.None.ItemId)
|
.Where(x => x.ItemId != 0)
|
||||||
.Where(x => x.Enabled)
|
|
||||||
.Where(x => x.Type == Configuration.PurchaseType.KeepStocked || x.Limit > 0)
|
|
||||||
.Select(x => new { Item = x, Reward = _gcRewardsCache.GetReward(x.ItemId) })
|
.Select(x => new { Item = x, Reward = _gcRewardsCache.GetReward(x.ItemId) })
|
||||||
.Where(x => x.Reward.GrandCompanies.Contains(grandCompany))
|
.Where(x => x.Reward.GrandCompanies.Contains(grandCompany))
|
||||||
.Where(x => x.Reward.RequiredRank <= rank)
|
.Where(x => x.Reward.RequiredRank <= rank)
|
||||||
.Select(x =>
|
.Select(x => new PurchaseItemRequest
|
||||||
{
|
{
|
||||||
var purchaseOption = _configuration.ItemsAvailableToPurchase.Where(y => y.SameQuantityForAllLists)
|
ItemId = x.Item.ItemId,
|
||||||
.FirstOrDefault(y => y.ItemId == x.Item.ItemId);
|
Name = x.Reward.Name,
|
||||||
int limit = purchaseOption?.GlobalLimit ?? x.Item.Limit;
|
EffectiveLimit = CalculateEffectiveLimit(
|
||||||
var request = new PurchaseItemRequest
|
x.Item.ItemId,
|
||||||
{
|
x.Item.Limit <= 0 ? uint.MaxValue : (uint)x.Item.Limit,
|
||||||
ItemId = x.Item.ItemId,
|
x.Reward.StackSize),
|
||||||
Name = x.Reward.Name,
|
SealCost = x.Reward.SealCost,
|
||||||
EffectiveLimit = CalculateEffectiveLimit(
|
Tier = x.Reward.Tier,
|
||||||
x.Item.ItemId,
|
SubCategory = x.Reward.SubCategory,
|
||||||
limit <= 0 ? uint.MaxValue : (uint)limit,
|
StackSize = x.Reward.StackSize,
|
||||||
x.Reward.StackSize,
|
|
||||||
x.Reward.InventoryLimit),
|
|
||||||
SealCost = x.Reward.SealCost,
|
|
||||||
Tier = x.Reward.Tier,
|
|
||||||
SubCategory = x.Reward.SubCategory,
|
|
||||||
StackSize = x.Reward.StackSize,
|
|
||||||
Type = x.Item.Type,
|
|
||||||
CheckRetainerInventory = x.Item.CheckRetainerInventory,
|
|
||||||
};
|
|
||||||
if (x.Item.Type == Configuration.PurchaseType.PurchaseOneTime)
|
|
||||||
{
|
|
||||||
request.OnPurchase = qty =>
|
|
||||||
{
|
|
||||||
request.EffectiveLimit -= (uint)qty;
|
|
||||||
x.Item.Limit -= qty;
|
|
||||||
ItemsWrapper.Save();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return request;
|
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
@ -213,7 +95,9 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
|
|
||||||
public override unsafe void Draw()
|
public override unsafe void Draw()
|
||||||
{
|
{
|
||||||
GrandCompany grandCompany = _gameFunctions.GetGrandCompany();
|
LImGui.AddPatreonIcon(_pluginInterface);
|
||||||
|
|
||||||
|
GrandCompany grandCompany = _plugin.GetGrandCompany();
|
||||||
if (grandCompany == GrandCompany.None)
|
if (grandCompany == GrandCompany.None)
|
||||||
{
|
{
|
||||||
// not sure we should ever get here
|
// not sure we should ever get here
|
||||||
@ -221,19 +105,10 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_gameFunctions.GetGrandCompanyRank() < 6)
|
if (_plugin.GetGrandCompanyRank() < 6)
|
||||||
{
|
{
|
||||||
State = false;
|
State = false;
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed, "You do not have the required rank for Expert Delivery.");
|
ImGui.TextColored(ImGuiColors.DalamudRed, "You do not have the required rank for Expert Delivery.");
|
||||||
|
|
||||||
DrawNextRankPrequesites();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (_configuration.BehaviorOnOtherWorld == Configuration.EBehaviorOnOtherWorld.DisableTurnIn &&
|
|
||||||
!IsOnHomeWorld)
|
|
||||||
{
|
|
||||||
State = false;
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed, "Turn-in disabled, you are not on your home world.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,133 +118,51 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
State = state;
|
State = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
float indentSize = ImGui.GetFrameHeight() + ImGui.GetStyle().ItemInnerSpacing.X;
|
ImGui.SameLine();
|
||||||
|
if (ImGuiComponents.IconButton("###OpenConfig", FontAwesomeIcon.Cog))
|
||||||
|
_configWindow.IsOpen = true;
|
||||||
|
|
||||||
|
ImGui.Indent(27);
|
||||||
if (!string.IsNullOrEmpty(Error))
|
if (!string.IsNullOrEmpty(Error))
|
||||||
{
|
{
|
||||||
using (ImRaii.PushIndent(indentSize))
|
ImGui.TextColored(ImGuiColors.DalamudRed, Error);
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed, Error);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (ImRaii.PushIndent(indentSize))
|
if (Multiplier == 1m)
|
||||||
{
|
{
|
||||||
if (_configuration.BehaviorOnOtherWorld == Configuration.EBehaviorOnOtherWorld.Warning &&
|
ImGui.TextColored(ImGuiColors.DalamudYellow, "You do not have an active seal buff.");
|
||||||
!IsOnHomeWorld)
|
}
|
||||||
{
|
else
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed,
|
{
|
||||||
"You are not on your home world and will not earn FC points.");
|
ImGui.TextColored(ImGuiColors.HealerGreen, $"Current Buff: {(Multiplier - 1m) * 100:N0}%%");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Multiplier == 1m)
|
if (Multiplier <= 1.10m)
|
||||||
|
{
|
||||||
|
InventoryManager* inventoryManager = InventoryManager.Instance();
|
||||||
|
if (inventoryManager->GetInventoryItemCount(ItemIds.PrioritySealAllowance) > 0)
|
||||||
{
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DalamudYellow, "You do not have an active seal buff.");
|
ImGui.BeginDisabled(_condition[ConditionFlag.OccupiedInQuestEvent] || _condition[ConditionFlag.Casting]);
|
||||||
}
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Bolt, "Use Priority Seal Allowance (15%)"))
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextColored(ImGuiColors.HealerGreen, $"Current Buff: {(Multiplier - 1m) * 100:N0}%%");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Multiplier <= 1.10m)
|
|
||||||
{
|
|
||||||
InventoryManager* inventoryManager = InventoryManager.Instance();
|
|
||||||
AgentInventoryContext* agentInventoryContext = AgentInventoryContext.Instance();
|
|
||||||
if (inventoryManager->GetInventoryItemCount(ItemIds.PrioritySealAllowance) > 0)
|
|
||||||
{
|
{
|
||||||
ImGui.BeginDisabled(_condition[ConditionFlag.OccupiedInQuestEvent] ||
|
AgentInventoryContext.Instance()->UseItem(ItemIds.PrioritySealAllowance);
|
||||||
_condition[ConditionFlag.Casting] ||
|
|
||||||
agentInventoryContext == null);
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Bolt,
|
|
||||||
"Use Priority Seal Allowance (15%)"))
|
|
||||||
{
|
|
||||||
agentInventoryContext->UseItem(ItemIds.PrioritySealAllowance);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
}
|
}
|
||||||
|
ImGui.EndDisabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsMinimized)
|
ImGui.Unindent(27);
|
||||||
{
|
|
||||||
ImGui.Separator();
|
|
||||||
using (ImRaii.Disabled(state))
|
|
||||||
DrawItemsToBuy(grandCompany);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_configuration.QuickTurnInKey != VirtualKey.NO_KEY)
|
|
||||||
{
|
|
||||||
var key = _configuration.QuickTurnInKey switch
|
|
||||||
{
|
|
||||||
VirtualKey.MENU => "ALT",
|
|
||||||
VirtualKey.SHIFT => "SHIFT",
|
|
||||||
VirtualKey.CONTROL => "CTRL",
|
|
||||||
_ => _configuration.QuickTurnInKey.ToString()
|
|
||||||
};
|
|
||||||
if (!State && _keyState[_configuration.QuickTurnInKey])
|
|
||||||
{
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.TextColored(ImGuiColors.HealerGreen, "Click an item to turn it in without confirmation");
|
|
||||||
}
|
|
||||||
else if (!IsMinimized)
|
|
||||||
{
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Text($"Hold '{key}' when clicking an item to turn it in without confirmation.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsMinimized)
|
|
||||||
{
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.Text($"Debug (State): {_plugin.CurrentStage}");
|
ImGui.BeginDisabled(state);
|
||||||
|
|
||||||
|
DrawItemsToBuy(grandCompany);
|
||||||
|
|
||||||
|
ImGui.EndDisabled();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe void DrawNextRankPrequesites()
|
ImGui.Separator();
|
||||||
{
|
ImGui.Text($"Debug (State): {_plugin.CurrentStage}");
|
||||||
string? rankName = _gameFunctions.GetNextGrandCompanyRankName();
|
|
||||||
if (rankName != null)
|
|
||||||
{
|
|
||||||
int currentSeals = _gameFunctions.GetCurrentSealCount();
|
|
||||||
uint requiredSeals = _gameFunctions.GetSealsRequiredForNextRank();
|
|
||||||
|
|
||||||
int currentHuntingLog =
|
|
||||||
MonsterNoteManager.Instance()->RankData[(int)_gameFunctions.GetGrandCompany() + 7]
|
|
||||||
.Rank;
|
|
||||||
byte requiredHuntingLog = _gameFunctions.GetRequiredHuntingLogForNextRank();
|
|
||||||
|
|
||||||
bool enoughSeals = currentSeals >= requiredSeals;
|
|
||||||
bool enoughHuntingLog = currentHuntingLog >= requiredHuntingLog;
|
|
||||||
|
|
||||||
if (enoughSeals && enoughHuntingLog)
|
|
||||||
ImGui.TextColored(ImGuiColors.HealerGreen, $"You meet all requirements to rank up to {rankName}.");
|
|
||||||
else
|
|
||||||
ImGui.Text($"Ranking up to {rankName} requires:");
|
|
||||||
|
|
||||||
ImGui.Indent();
|
|
||||||
if (enoughSeals)
|
|
||||||
{
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
|
|
||||||
ImGui.BulletText($"{currentSeals:N0} / {requiredSeals:N0} GC seals");
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ImGui.BulletText($"{currentSeals:N0} / {requiredSeals:N0} GC seals");
|
|
||||||
|
|
||||||
if (requiredHuntingLog > 0)
|
|
||||||
{
|
|
||||||
if (enoughHuntingLog)
|
|
||||||
{
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
|
|
||||||
ImGui.BulletText($"Complete Hunting Log #{requiredHuntingLog}");
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ImGui.BulletText($"Complete Hunting Log #{requiredHuntingLog}");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Unindent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawItemsToBuy(GrandCompany grandCompany)
|
private void DrawItemsToBuy(GrandCompany grandCompany)
|
||||||
@ -377,29 +170,18 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
var itemsWrapper = ItemsWrapper;
|
var itemsWrapper = ItemsWrapper;
|
||||||
ImGui.Text($"Items to buy ({itemsWrapper.Name}):");
|
ImGui.Text($"Items to buy ({itemsWrapper.Name}):");
|
||||||
|
|
||||||
List<(GcRewardItem Item, string NameWithoutRetainers, string NameWithRetainers)> comboValues = new()
|
List<(uint ItemId, string Name, IReadOnlyList<GrandCompany> GrandCompanies, uint Rank)> comboValues = new()
|
||||||
{
|
{
|
||||||
(GcRewardItem.None, GcRewardItem.None.Name, GcRewardItem.None.Name),
|
(GcRewardItem.None.ItemId, GcRewardItem.None.Name, new List<GrandCompany>(), GcRewardItem.None.RequiredRank)
|
||||||
};
|
};
|
||||||
foreach (Configuration.PurchaseOption purchaseOption in _configuration.ItemsAvailableToPurchase)
|
foreach (uint itemId in _configuration.ItemsAvailableForPurchase)
|
||||||
{
|
{
|
||||||
var gcReward = _gcRewardsCache.GetReward(purchaseOption.ItemId);
|
var gcReward = _gcRewardsCache.GetReward(itemId);
|
||||||
int itemCountWithoutRetainers = _gameFunctions.GetItemCount(purchaseOption.ItemId, false);
|
int itemCount = _plugin.GetItemCount(itemId);
|
||||||
int itemCountWithRetainers = _gameFunctions.GetItemCount(purchaseOption.ItemId, true);
|
string itemName = gcReward.Name;
|
||||||
string itemNameWithoutRetainers = gcReward.Name;
|
if (itemCount > 0)
|
||||||
string itemNameWithRetainers = gcReward.Name;
|
itemName += $" ({itemCount:N0})";
|
||||||
|
comboValues.Add((itemId, itemName, gcReward.GrandCompanies, gcReward.RequiredRank));
|
||||||
if (purchaseOption.SameQuantityForAllLists)
|
|
||||||
{
|
|
||||||
itemNameWithoutRetainers += $" {((SeIconChar)57412).ToIconString()}";
|
|
||||||
itemNameWithRetainers += $" {((SeIconChar)57412).ToIconString()}";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemCountWithoutRetainers > 0)
|
|
||||||
itemNameWithoutRetainers += $" ({itemCountWithoutRetainers:N0})";
|
|
||||||
if (itemCountWithRetainers > 0)
|
|
||||||
itemNameWithRetainers += $" ({itemCountWithRetainers:N0})";
|
|
||||||
comboValues.Add((gcReward, itemNameWithoutRetainers, itemNameWithRetainers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemsWrapper.GetItemsToPurchase().Count == 0)
|
if (itemsWrapper.GetItemsToPurchase().Count == 0)
|
||||||
@ -408,249 +190,49 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
itemsWrapper.Save();
|
itemsWrapper.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
Configuration.PurchasePriority? itemToRemove = null;
|
int? itemToRemove = null;
|
||||||
Configuration.PurchasePriority? itemToAdd = null;
|
|
||||||
int indexToAdd = 0;
|
|
||||||
|
|
||||||
float width = ImGui.GetContentRegionAvail().X;
|
|
||||||
List<(Vector2 TopLeft, Vector2 BottomRight)> itemPositions = [];
|
|
||||||
|
|
||||||
for (int i = 0; i < itemsWrapper.GetItemsToPurchase().Count; ++i)
|
for (int i = 0; i < itemsWrapper.GetItemsToPurchase().Count; ++i)
|
||||||
{
|
{
|
||||||
DrawItemToBuy(grandCompany, i, itemsWrapper, comboValues, width, ref itemToRemove, itemPositions);
|
ImGui.PushID($"ItemToBuy{i}");
|
||||||
}
|
var item = itemsWrapper.GetItemsToPurchase()[i];
|
||||||
|
int comboValueIndex = comboValues.FindIndex(x => x.ItemId == item.ItemId);
|
||||||
if (!ImGui.IsMouseDragging(ImGuiMouseButton.Left))
|
if (comboValueIndex < 0)
|
||||||
_draggedItem = null;
|
|
||||||
else if (_draggedItem != null)
|
|
||||||
{
|
|
||||||
var items = itemsWrapper.GetItemsToPurchase().ToList();
|
|
||||||
var draggedItem = items.Single(x => x.InternalId == _draggedItem);
|
|
||||||
int oldIndex = items.IndexOf(draggedItem);
|
|
||||||
|
|
||||||
var (topLeft, bottomRight) = itemPositions[oldIndex];
|
|
||||||
ImGui.GetWindowDrawList().AddRect(topLeft, bottomRight, ImGui.GetColorU32(ImGuiColors.DalamudGrey), 3f,
|
|
||||||
ImDrawFlags.RoundCornersAll);
|
|
||||||
|
|
||||||
int newIndex = itemPositions.FindIndex(x => ImGui.IsMouseHoveringRect(x.TopLeft, x.BottomRight, true));
|
|
||||||
if (newIndex >= 0 && oldIndex != newIndex)
|
|
||||||
{
|
{
|
||||||
itemToAdd = items.Single(x => x.InternalId == _draggedItem);
|
item.ItemId = 0;
|
||||||
indexToAdd = newIndex;
|
item.Limit = 0;
|
||||||
}
|
itemsWrapper.Save();
|
||||||
}
|
|
||||||
|
|
||||||
if (itemToRemove != null)
|
comboValueIndex = 0;
|
||||||
{
|
|
||||||
itemsWrapper.Remove(itemToRemove);
|
|
||||||
itemsWrapper.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemToAdd != null)
|
|
||||||
{
|
|
||||||
itemsWrapper.Remove(itemToAdd);
|
|
||||||
itemsWrapper.Insert(indexToAdd, itemToAdd);
|
|
||||||
itemsWrapper.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_configuration.ItemsAvailableToPurchase.Any(x =>
|
|
||||||
itemsWrapper.GetItemsToPurchase().All(y => x.ItemId != y.ItemId)))
|
|
||||||
{
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Item"))
|
|
||||||
ImGui.OpenPopup("##AddItem");
|
|
||||||
|
|
||||||
if (ImGui.BeginPopupContextItem("##AddItem", ImGuiPopupFlags.NoOpenOverItems))
|
|
||||||
{
|
|
||||||
foreach (var purchaseOption in _configuration.ItemsAvailableToPurchase.Distinct())
|
|
||||||
{
|
|
||||||
if (_gcRewardsCache.RewardLookup.TryGetValue(purchaseOption.ItemId, out var reward))
|
|
||||||
{
|
|
||||||
if (ImGui.MenuItem($"{reward.Name}##{purchaseOption.ItemId}"))
|
|
||||||
{
|
|
||||||
itemsWrapper.Add(new Configuration.PurchasePriority
|
|
||||||
{ ItemId = purchaseOption.ItemId, Limit = 0 });
|
|
||||||
itemsWrapper.Save();
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndPopup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
if (ImGui.Combo("", ref comboValueIndex, comboValues.Select(x => x.Name).ToArray(), comboValues.Count))
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Cog, "Configure available Items"))
|
|
||||||
_configWindow.IsOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawItemToBuy(GrandCompany grandCompany, int i, IItemsToPurchase itemsWrapper,
|
|
||||||
List<(GcRewardItem Item, string NameWithoutRetainers, string NameWithRetainers)> comboValues, float width,
|
|
||||||
ref Configuration.PurchasePriority? itemToRemove, List<(Vector2 TopLeft, Vector2 BottomRight)> itemPositions)
|
|
||||||
{
|
|
||||||
Vector2 topLeft = ImGui.GetCursorScreenPos() + new Vector2(0, -ImGui.GetStyle().ItemSpacing.Y / 2);
|
|
||||||
|
|
||||||
ImGui.PushID($"ItemToBuy{i}");
|
|
||||||
Configuration.PurchasePriority item = itemsWrapper.GetItemsToPurchase()[i];
|
|
||||||
|
|
||||||
float indentX = ImGui.GetCursorPosX();
|
|
||||||
bool enabled = item.Enabled;
|
|
||||||
int popColors = 0;
|
|
||||||
if (!enabled)
|
|
||||||
{
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0.5f, 0.35f, 1f));
|
|
||||||
popColors++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.Button($"{item.GetIcon()}"))
|
|
||||||
ImGui.OpenPopup($"Configure{i}");
|
|
||||||
|
|
||||||
ImGui.PopStyleColor(popColors);
|
|
||||||
|
|
||||||
if (ImGui.BeginPopup($"Configure{i}"))
|
|
||||||
{
|
|
||||||
if (ImGui.Checkbox($"Enabled##Enabled{i}", ref enabled))
|
|
||||||
{
|
{
|
||||||
item.Enabled = enabled;
|
item.ItemId = comboValues[comboValueIndex].ItemId;
|
||||||
itemsWrapper.Save();
|
itemsWrapper.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(375 * ImGuiHelpers.GlobalScale);
|
if (itemsWrapper.GetItemsToPurchase().Count >= 2)
|
||||||
int type = (int)item.Type;
|
|
||||||
if (ImGui.Combo($"##Type{i}", ref type, StockingTypeLabels, StockingTypeLabels.Length))
|
|
||||||
{
|
{
|
||||||
item.Type = (Configuration.PurchaseType)type;
|
ImGui.SameLine();
|
||||||
if (item.Type != Configuration.PurchaseType.KeepStocked)
|
if (ImGuiComponents.IconButton($"###Remove{i}", FontAwesomeIcon.Times))
|
||||||
item.CheckRetainerInventory = false;
|
itemToRemove = i;
|
||||||
itemsWrapper.Save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.Type == Configuration.PurchaseType.KeepStocked && item.ItemId != ItemIds.Venture)
|
ImGui.Indent(27);
|
||||||
{
|
|
||||||
bool checkRetainerInventory = item.CheckRetainerInventory;
|
|
||||||
if (ImGui.Checkbox("Check Retainer Inventory for items (requires AllaganTools)",
|
|
||||||
ref checkRetainerInventory))
|
|
||||||
{
|
|
||||||
item.CheckRetainerInventory = checkRetainerInventory;
|
|
||||||
itemsWrapper.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine(0, 3);
|
|
||||||
ImGui.BeginDisabled(!enabled);
|
|
||||||
int comboValueIndex = comboValues.FindIndex(x => x.Item.ItemId == item.ItemId);
|
|
||||||
if (comboValueIndex < 0)
|
|
||||||
{
|
|
||||||
item.ItemId = 0;
|
|
||||||
item.Limit = 0;
|
|
||||||
itemsWrapper.Save();
|
|
||||||
|
|
||||||
comboValueIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var comboItem = comboValues[comboValueIndex];
|
|
||||||
using (var icon = _iconCache.GetIcon(comboItem.Item.IconId))
|
|
||||||
{
|
|
||||||
if (icon != null)
|
|
||||||
{
|
|
||||||
ImGui.Image(icon.ImGuiHandle, new Vector2(ImGui.GetFrameHeight()));
|
|
||||||
ImGui.SameLine(0, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
indentX = ImGui.GetCursorPosX() - indentX;
|
|
||||||
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
ImGui.SetNextItemWidth(width -
|
|
||||||
ImGui.GetStyle().WindowPadding.X -
|
|
||||||
ImGui.CalcTextSize(FontAwesomeIcon.Circle.ToIconString()).X -
|
|
||||||
ImGui.CalcTextSize(FontAwesomeIcon.ArrowsUpDown.ToIconString()).X -
|
|
||||||
ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X -
|
|
||||||
ImGui.GetStyle().FramePadding.X * 8 -
|
|
||||||
ImGui.GetStyle().ItemSpacing.X * 2 - 3 * 3);
|
|
||||||
ImGui.PopFont();
|
|
||||||
|
|
||||||
if (ImGui.Combo("", ref comboValueIndex,
|
|
||||||
comboValues.Select(x => item.CheckRetainerInventory ? x.NameWithRetainers : x.NameWithoutRetainers)
|
|
||||||
.ToArray(), comboValues.Count))
|
|
||||||
{
|
|
||||||
comboItem = comboValues[comboValueIndex];
|
|
||||||
item.ItemId = comboItem.Item.ItemId;
|
|
||||||
itemsWrapper.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
if (itemsWrapper.GetItemsToPurchase().Count >= 2)
|
|
||||||
{
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X +
|
|
||||||
ImGui.GetStyle().WindowPadding.X -
|
|
||||||
ImGui.CalcTextSize(FontAwesomeIcon.ArrowsUpDown.ToIconString()).X -
|
|
||||||
ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X -
|
|
||||||
ImGui.GetStyle().FramePadding.X * 4 -
|
|
||||||
ImGui.GetStyle().ItemSpacing.X);
|
|
||||||
ImGui.PopFont();
|
|
||||||
|
|
||||||
if (_draggedItem == item.InternalId)
|
|
||||||
{
|
|
||||||
ImGuiComponents.IconButton("##Move", FontAwesomeIcon.ArrowsUpDown,
|
|
||||||
ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.ButtonActive)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ImGuiComponents.IconButton("##Move", FontAwesomeIcon.ArrowsUpDown);
|
|
||||||
|
|
||||||
if (_draggedItem == null && ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
|
|
||||||
_draggedItem = item.InternalId;
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X +
|
|
||||||
ImGui.GetStyle().WindowPadding.X -
|
|
||||||
ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X -
|
|
||||||
ImGui.GetStyle().FramePadding.X * 2);
|
|
||||||
ImGui.PopFont();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGuiComponents.IconButton($"###Remove{i}", FontAwesomeIcon.Times))
|
|
||||||
itemToRemove = item;
|
|
||||||
|
|
||||||
if (enabled)
|
|
||||||
{
|
|
||||||
ImGui.Indent(indentX);
|
|
||||||
if (comboValueIndex > 0)
|
if (comboValueIndex > 0)
|
||||||
{
|
{
|
||||||
var purchaseOption =
|
|
||||||
_configuration.ItemsAvailableToPurchase
|
|
||||||
.Where(x => x.SameQuantityForAllLists)
|
|
||||||
.FirstOrDefault(x => x.ItemId == item.ItemId);
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 130);
|
ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 130);
|
||||||
int limit = Math.Min(purchaseOption?.GlobalLimit ?? item.Limit, (int)comboItem.Item.InventoryLimit);
|
int limit = item.Limit;
|
||||||
int stepSize = comboItem.Item.StackSize < 99 ? 1 : 50;
|
if (item.ItemId == ItemIds.Venture)
|
||||||
string label = item.Type == Configuration.PurchaseType.KeepStocked
|
limit = Math.Min(limit, 65_000);
|
||||||
? "Maximum items to buy"
|
|
||||||
: "Remaining items to buy";
|
if (ImGui.InputInt("Maximum items to buy", ref limit, 50, 500))
|
||||||
if (ImGui.InputInt(label, ref limit, stepSize, stepSize * 10))
|
|
||||||
{
|
{
|
||||||
int newLimit = Math.Min(Math.Max(0, limit), (int)comboItem.Item.InventoryLimit);
|
item.Limit = Math.Max(0, limit);
|
||||||
if (purchaseOption != null)
|
if (item.ItemId == ItemIds.Venture)
|
||||||
{
|
item.Limit = Math.Min(item.Limit, 65_000);
|
||||||
purchaseOption.GlobalLimit = newLimit;
|
|
||||||
_pluginInterface.SavePluginConfig(_configuration);
|
itemsWrapper.Save();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item.Limit = newLimit;
|
|
||||||
itemsWrapper.Save();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (item.Limit != 0)
|
else if (item.Limit != 0)
|
||||||
@ -661,63 +243,73 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
|
|
||||||
if (comboValueIndex > 0)
|
if (comboValueIndex > 0)
|
||||||
{
|
{
|
||||||
if (!comboItem.Item.GrandCompanies.Contains(grandCompany))
|
var comboItem = comboValues[comboValueIndex];
|
||||||
|
if (!comboItem.GrandCompanies.Contains(grandCompany))
|
||||||
{
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed,
|
ImGui.TextColored(ImGuiColors.DalamudRed,
|
||||||
"This item will be skipped, as you are in the wrong Grand Company.");
|
"This item will be skipped, as you are in the wrong Grand Company.");
|
||||||
}
|
}
|
||||||
else if (comboItem.Item.RequiredRank > _gameFunctions.GetGrandCompanyRank())
|
else if (comboItem.Rank > _plugin.GetGrandCompanyRank())
|
||||||
{
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed,
|
ImGui.TextColored(ImGuiColors.DalamudRed,
|
||||||
"This item will be skipped, your rank isn't high enough to buy it.");
|
"This item will be skipped, your rank isn't high enough to buy it.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Unindent(indentX);
|
ImGui.Unindent(27);
|
||||||
|
ImGui.PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.PopID();
|
if (itemToRemove != null)
|
||||||
|
{
|
||||||
|
itemsWrapper.RemoveAt(itemToRemove.Value);
|
||||||
|
itemsWrapper.Save();
|
||||||
|
}
|
||||||
|
|
||||||
Vector2 bottomRight = new Vector2(topLeft.X + width,
|
if (_configuration.ItemsAvailableForPurchase.Any(x =>
|
||||||
ImGui.GetCursorScreenPos().Y - ImGui.GetStyle().ItemSpacing.Y + 2);
|
itemsWrapper.GetItemsToPurchase().All(y => x != y.ItemId)))
|
||||||
itemPositions.Add((topLeft, bottomRight));
|
{
|
||||||
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Item"))
|
||||||
|
{
|
||||||
|
itemsWrapper.Add(new Configuration.PurchasePriority { ItemId = GcRewardItem.None.ItemId, Limit = 0 });
|
||||||
|
itemsWrapper.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe uint CalculateEffectiveLimit(uint itemId, uint limit, uint stackSize, uint inventoryLimit)
|
private unsafe uint CalculateEffectiveLimit(uint itemId, uint limit, uint stackSize)
|
||||||
{
|
{
|
||||||
if (itemId == ItemIds.Venture)
|
if (itemId == ItemIds.Venture)
|
||||||
return Math.Min(limit, inventoryLimit);
|
return Math.Min(limit, 65_000);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
uint slotsThatCanBeUsed = 0;
|
uint slotsThatCanBeUsed = 0;
|
||||||
InventoryManager* inventoryManager = InventoryManager.Instance();
|
InventoryManager* inventoryManager = InventoryManager.Instance();
|
||||||
foreach (var inventoryType in InventoryTypes)
|
for (InventoryType inventoryType = InventoryType.Inventory1;
|
||||||
|
inventoryType <= InventoryType.Inventory4;
|
||||||
|
++inventoryType)
|
||||||
{
|
{
|
||||||
var container = inventoryManager->GetInventoryContainer(inventoryType);
|
var container = inventoryManager->GetInventoryContainer(inventoryType);
|
||||||
for (int i = 0; i < container->Size; ++i)
|
for (int i = 0; i < container->Size; ++i)
|
||||||
{
|
{
|
||||||
var item = container->GetInventorySlot(i);
|
var item = container->GetInventorySlot(i);
|
||||||
if (item == null || item->ItemId == 0 || item->ItemId == itemId)
|
if (item == null || item->ItemID == 0 || item->ItemID == itemId)
|
||||||
{
|
{
|
||||||
slotsThatCanBeUsed++;
|
slotsThatCanBeUsed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Math.Min(Math.Min(limit, slotsThatCanBeUsed * stackSize), inventoryLimit);
|
return Math.Min(limit, slotsThatCanBeUsed * stackSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveWindowConfig() => _pluginInterface.SavePluginConfig(_configuration);
|
|
||||||
|
|
||||||
private interface IItemsToPurchase
|
private interface IItemsToPurchase
|
||||||
{
|
{
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
|
||||||
IReadOnlyList<Configuration.PurchasePriority> GetItemsToPurchase();
|
IReadOnlyList<Configuration.PurchasePriority> GetItemsToPurchase();
|
||||||
void Add(Configuration.PurchasePriority purchasePriority);
|
void Add(Configuration.PurchasePriority purchasePriority);
|
||||||
void Insert(int index, Configuration.PurchasePriority purchasePriority);
|
|
||||||
void Remove(Configuration.PurchasePriority purchasePriority);
|
|
||||||
void RemoveAt(int index);
|
void RemoveAt(int index);
|
||||||
void Save();
|
void Save();
|
||||||
}
|
}
|
||||||
@ -725,10 +317,10 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
private sealed class CharacterSpecificItemsToPurchase : IItemsToPurchase
|
private sealed class CharacterSpecificItemsToPurchase : IItemsToPurchase
|
||||||
{
|
{
|
||||||
private readonly CharacterConfiguration _characterConfiguration;
|
private readonly CharacterConfiguration _characterConfiguration;
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
|
|
||||||
public CharacterSpecificItemsToPurchase(CharacterConfiguration characterConfiguration,
|
public CharacterSpecificItemsToPurchase(CharacterConfiguration characterConfiguration,
|
||||||
IDalamudPluginInterface pluginInterface)
|
DalamudPluginInterface pluginInterface)
|
||||||
{
|
{
|
||||||
_characterConfiguration = characterConfiguration;
|
_characterConfiguration = characterConfiguration;
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
@ -742,12 +334,6 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
public void Add(Configuration.PurchasePriority purchasePriority)
|
public void Add(Configuration.PurchasePriority purchasePriority)
|
||||||
=> _characterConfiguration.ItemsToPurchase.Add(purchasePriority);
|
=> _characterConfiguration.ItemsToPurchase.Add(purchasePriority);
|
||||||
|
|
||||||
public void Insert(int index, Configuration.PurchasePriority purchasePriority)
|
|
||||||
=> _characterConfiguration.ItemsToPurchase.Insert(index, purchasePriority);
|
|
||||||
|
|
||||||
public void Remove(Configuration.PurchasePriority purchasePriority)
|
|
||||||
=> _characterConfiguration.ItemsToPurchase.Remove(purchasePriority);
|
|
||||||
|
|
||||||
public void RemoveAt(int index)
|
public void RemoveAt(int index)
|
||||||
=> _characterConfiguration.ItemsToPurchase.RemoveAt(index);
|
=> _characterConfiguration.ItemsToPurchase.RemoveAt(index);
|
||||||
|
|
||||||
@ -758,9 +344,9 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
private sealed class GlobalItemsToPurchase : IItemsToPurchase
|
private sealed class GlobalItemsToPurchase : IItemsToPurchase
|
||||||
{
|
{
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
|
|
||||||
public GlobalItemsToPurchase(Configuration configuration, IDalamudPluginInterface pluginInterface)
|
public GlobalItemsToPurchase(Configuration configuration, DalamudPluginInterface pluginInterface)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
@ -774,12 +360,6 @@ internal sealed class TurnInWindow : LWindow, IPersistableWindowConfig<Configura
|
|||||||
public void Add(Configuration.PurchasePriority purchasePriority)
|
public void Add(Configuration.PurchasePriority purchasePriority)
|
||||||
=> _configuration.ItemsToPurchase.Add(purchasePriority);
|
=> _configuration.ItemsToPurchase.Add(purchasePriority);
|
||||||
|
|
||||||
public void Insert(int index, Configuration.PurchasePriority purchasePriority)
|
|
||||||
=> _configuration.ItemsToPurchase.Insert(index, purchasePriority);
|
|
||||||
|
|
||||||
public void Remove(Configuration.PurchasePriority purchasePriority)
|
|
||||||
=> _configuration.ItemsToPurchase.Remove(purchasePriority);
|
|
||||||
|
|
||||||
public void RemoveAt(int index)
|
public void RemoveAt(int index)
|
||||||
=> _configuration.ItemsToPurchase.RemoveAt(index);
|
=> _configuration.ItemsToPurchase.RemoveAt(index);
|
||||||
|
|
||||||
|
@ -1,86 +1,15 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"net8.0-windows7.0": {
|
"net7.0-windows7.0": {
|
||||||
"DalamudPackager": {
|
"DalamudPackager": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[11.0.0, )",
|
"requested": "[2.1.12, )",
|
||||||
"resolved": "11.0.0",
|
"resolved": "2.1.12",
|
||||||
"contentHash": "bjT7XUlhIJSmsE/O76b7weUX+evvGQctbQB8aKXt94o+oPWxHpCepxAGMs7Thow3AzCyqWs7cOpp9/2wcgRRQA=="
|
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
|
||||||
},
|
|
||||||
"DotNet.ReproducibleBuilds": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[1.1.1, )",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.GitHub": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.GitLab": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.Gitea": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[8.0.0, )",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "KOBodmDnlWGIqZt2hT47Q69TIoGhIApDVLCyyj9TT5ct8ju16AbHYcB4XeknoHX562wO1pMS/1DfBIZK+V+sxg==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
|
||||||
"Microsoft.SourceLink.Common": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Build.Tasks.Git": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.AzureRepos.Git": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.Bitbucket.Git": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.Common": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.GitHub": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.GitLab": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"llib": {
|
"llib": {
|
||||||
"type": "Project",
|
"type": "Project"
|
||||||
"dependencies": {
|
|
||||||
"DalamudPackager": "[11.0.0, )"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
LLib
2
LLib
@ -1 +1 @@
|
|||||||
Subproject commit b581e2ea2a61f44ed3f0cb4f6ea8cc1595525544
|
Subproject commit abbbec4f26b1a8903b0cd7aa04f00d557602eaf3
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "8.0.0",
|
"version": "7.0.0",
|
||||||
"rollForward": "latestMinor",
|
"rollForward": "latestMinor",
|
||||||
"allowPrerelease": false
|
"allowPrerelease": false
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user