Compare commits
No commits in common. "master" and "v2.6" have entirely different histories.
@ -1,7 +0,0 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Allagan/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gatherbtn/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gatherfish/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gathermin/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=relog/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Teamcraft/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,65 @@
|
|||||||
<Project Sdk="Dalamud.NET.Sdk/9.0.2">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>5.8</Version>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
|
<Version>2.6</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>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
|
||||||
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
</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="..\AutoRetainerAPI\AutoRetainerAPI\AutoRetainerAPI.csproj" />
|
<ProjectReference Include="..\AutoRetainerAPI\AutoRetainerAPI\AutoRetainerAPI.csproj" />
|
||||||
<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>
|
||||||
|
@ -44,57 +44,14 @@ partial class AutoRetainerControlPlugin
|
|||||||
save = true;
|
save = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_clientState.LocalContentId == registeredCharacterId)
|
List<string> seenRetainers = new();
|
||||||
|
foreach (var retainerData in offlineCharacterData.RetainerData)
|
||||||
{
|
{
|
||||||
var unlockedFolkloreBooks = _gameCache.FolkloreBooks.Values.Where(x => x.IsUnlocked()).Select(x => x.ItemId).ToHashSet();
|
var retainer = character.Retainers.SingleOrDefault(x => x.Name == retainerData.Name);
|
||||||
if (character.UnlockedFolkloreBooks != unlockedFolkloreBooks)
|
|
||||||
{
|
|
||||||
character.UnlockedFolkloreBooks = unlockedFolkloreBooks;
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove retainers without name
|
|
||||||
save |= character.Retainers.RemoveAll(x => string.IsNullOrEmpty(x.Name)) > 0;
|
|
||||||
|
|
||||||
// migrate legacy retainers
|
|
||||||
foreach (var legacyRetainer in character.Retainers.Where(x => x.RetainerContentId == 0))
|
|
||||||
{
|
|
||||||
_pluginLog.Information($"Migrating retainer '{legacyRetainer.Name}' (char: {character})");
|
|
||||||
var retainerData =
|
|
||||||
offlineCharacterData.RetainerData.SingleOrDefault(x => legacyRetainer.Name == x.Name);
|
|
||||||
if (retainerData != null)
|
|
||||||
{
|
|
||||||
_pluginLog.Information(
|
|
||||||
$"Assigning contentId {retainerData.RetainerID} to retainer {retainerData.Name}");
|
|
||||||
legacyRetainer.RetainerContentId = retainerData.RetainerID;
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var retainersWithoutContentId = character.Retainers.Where(c => c.RetainerContentId == 0).ToList();
|
|
||||||
if (retainersWithoutContentId.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var retainer in retainersWithoutContentId)
|
|
||||||
{
|
|
||||||
_pluginLog.Warning($"Removing retainer {retainer.Name} without contentId");
|
|
||||||
character.Retainers.Remove(retainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ulong> unknownRetainerIds = offlineCharacterData.RetainerData.Select(x => x.RetainerID).Where(x => x != 0).ToList();
|
|
||||||
foreach (var retainerData in offlineCharacterData.RetainerData.Where(x => !string.IsNullOrEmpty(x.Name)))
|
|
||||||
{
|
|
||||||
unknownRetainerIds.Remove(retainerData.RetainerID);
|
|
||||||
|
|
||||||
var retainer = character.Retainers.SingleOrDefault(x => x.RetainerContentId == retainerData.RetainerID);
|
|
||||||
if (retainer == null)
|
if (retainer == null)
|
||||||
{
|
{
|
||||||
retainer = new Configuration.RetainerConfiguration
|
retainer = new Configuration.RetainerConfiguration
|
||||||
{
|
{
|
||||||
RetainerContentId = retainerData.RetainerID,
|
|
||||||
Name = retainerData.Name,
|
Name = retainerData.Name,
|
||||||
Managed = false,
|
Managed = false,
|
||||||
};
|
};
|
||||||
@ -103,11 +60,7 @@ partial class AutoRetainerControlPlugin
|
|||||||
character.Retainers.Add(retainer);
|
character.Retainers.Add(retainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retainer.Name != retainerData.Name)
|
seenRetainers.Add(retainer.Name);
|
||||||
{
|
|
||||||
retainer.Name = retainerData.Name;
|
|
||||||
save = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retainer.DisplayOrder != retainerData.DisplayOrder)
|
if (retainer.DisplayOrder != retainerData.DisplayOrder)
|
||||||
{
|
{
|
||||||
@ -160,17 +113,9 @@ partial class AutoRetainerControlPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unknownRetainerIds.Count > 0)
|
if (character.Retainers.RemoveAll(x => !seenRetainers.Contains(x.Name)) > 0)
|
||||||
{
|
|
||||||
foreach (var retainerId in unknownRetainerIds)
|
|
||||||
{
|
|
||||||
_pluginLog.Warning($"Removing unknown retainer with contentId {retainerId}");
|
|
||||||
character.Retainers.RemoveAll(c => c.RetainerContentId == retainerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
save = true;
|
save = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (save)
|
if (save)
|
||||||
_pluginInterface.SavePluginConfig(_configuration);
|
_pluginInterface.SavePluginConfig(_configuration);
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using ARControl.External;
|
|
||||||
using ARControl.GameData;
|
using ARControl.GameData;
|
||||||
using ARControl.Windows;
|
using ARControl.Windows;
|
||||||
using AutoRetainerAPI;
|
using AutoRetainerAPI;
|
||||||
@ -26,7 +25,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
private const int QuickVentureId = 395;
|
private const int QuickVentureId = 395;
|
||||||
private readonly WindowSystem _windowSystem = new(nameof(AutoRetainerControlPlugin));
|
private readonly WindowSystem _windowSystem = new(nameof(AutoRetainerControlPlugin));
|
||||||
|
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly IChatGui _chatGui;
|
private readonly IChatGui _chatGui;
|
||||||
private readonly ICommandManager _commandManager;
|
private readonly ICommandManager _commandManager;
|
||||||
@ -36,18 +35,13 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
private readonly GameCache _gameCache;
|
private readonly GameCache _gameCache;
|
||||||
private readonly IconCache _iconCache;
|
private readonly IconCache _iconCache;
|
||||||
private readonly VentureResolver _ventureResolver;
|
private readonly VentureResolver _ventureResolver;
|
||||||
private readonly AllaganToolsIpc _allaganToolsIpc;
|
|
||||||
private readonly ConfigWindow _configWindow;
|
private readonly ConfigWindow _configWindow;
|
||||||
private readonly AutoRetainerApi _autoRetainerApi;
|
private readonly AutoRetainerApi _autoRetainerApi;
|
||||||
private readonly AutoRetainerReflection _autoRetainerReflection;
|
|
||||||
|
|
||||||
public AutoRetainerControlPlugin(IDalamudPluginInterface pluginInterface, IDataManager dataManager,
|
public AutoRetainerControlPlugin(DalamudPluginInterface pluginInterface, IDataManager dataManager,
|
||||||
IClientState clientState, IChatGui chatGui, ICommandManager commandManager, ITextureProvider textureProvider,
|
IClientState clientState, IChatGui chatGui, ICommandManager commandManager, ITextureProvider textureProvider,
|
||||||
IFramework framework, IPluginLog pluginLog)
|
IPluginLog pluginLog)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(pluginInterface);
|
|
||||||
ArgumentNullException.ThrowIfNull(dataManager);
|
|
||||||
|
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_chatGui = chatGui;
|
_chatGui = chatGui;
|
||||||
@ -59,16 +53,13 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
_gameCache = new GameCache(dataManager);
|
_gameCache = new GameCache(dataManager);
|
||||||
_iconCache = new IconCache(textureProvider);
|
_iconCache = new IconCache(textureProvider);
|
||||||
_ventureResolver = new VentureResolver(_gameCache, _pluginLog);
|
_ventureResolver = new VentureResolver(_gameCache, _pluginLog);
|
||||||
DiscardHelperIpc discardHelperIpc = new(_pluginInterface);
|
|
||||||
_allaganToolsIpc = new AllaganToolsIpc(pluginInterface, pluginLog);
|
|
||||||
_configWindow =
|
_configWindow =
|
||||||
new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _iconCache,
|
new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _iconCache,
|
||||||
discardHelperIpc, _allaganToolsIpc, _pluginLog);
|
_pluginLog);
|
||||||
_windowSystem.AddWindow(_configWindow);
|
_windowSystem.AddWindow(_configWindow);
|
||||||
|
|
||||||
ECommonsMain.Init(_pluginInterface, this);
|
ECommonsMain.Init(_pluginInterface, this);
|
||||||
_autoRetainerApi = new();
|
_autoRetainerApi = new();
|
||||||
_autoRetainerReflection = new AutoRetainerReflection(pluginInterface, framework, pluginLog, _autoRetainerApi);
|
|
||||||
|
|
||||||
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
||||||
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
|
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
|
||||||
@ -81,19 +72,8 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (_autoRetainerApi.Ready)
|
if (_autoRetainerApi.Ready)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Sync();
|
Sync();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_pluginLog.Error(e, "Unable to sync characters");
|
|
||||||
_chatGui.PrintError(
|
|
||||||
"Unable to synchronize characters with AutoRetainer, plugin might not work properly.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendRetainerToVenture(string retainerName)
|
private void SendRetainerToVenture(string retainerName)
|
||||||
{
|
{
|
||||||
@ -104,12 +84,6 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
|
|
||||||
private unsafe uint? GetNextVenture(string retainerName, bool dryRun)
|
private unsafe uint? GetNextVenture(string retainerName, bool dryRun)
|
||||||
{
|
{
|
||||||
if (!_autoRetainerReflection.ShouldReassign)
|
|
||||||
{
|
|
||||||
_pluginLog.Information("AutoRetainer is configured to not reassign ventures, so we are not checking any venture lists.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ch = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == _clientState.LocalContentId);
|
var ch = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == _clientState.LocalContentId);
|
||||||
if (ch == null)
|
if (ch == null)
|
||||||
{
|
{
|
||||||
@ -141,13 +115,11 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
|
|
||||||
if (ch.Ventures == 0)
|
if (ch.Ventures == 0)
|
||||||
{
|
{
|
||||||
_pluginLog.Warning(
|
_pluginLog.Warning("Could not assign a next venture from venture list, as the character has no ventures left.");
|
||||||
"Could not assign a next venture from venture list, as the character has no ventures left.");
|
|
||||||
}
|
}
|
||||||
else if (ch.Ventures <= _configuration.Misc.VenturesToKeep)
|
else if (ch.Ventures <= _configuration.Misc.VenturesToKeep)
|
||||||
{
|
{
|
||||||
_pluginLog.Warning(
|
_pluginLog.Warning($"Could not assign a next venture from venture list, character only has {ch.Ventures} left, configuration says to only send out above {_configuration.Misc.VenturesToKeep} ventures.");
|
||||||
$"Could not assign a next venture from venture list, character only has {ch.Ventures} left, configuration says to only send out above {_configuration.Misc.VenturesToKeep} ventures.");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -202,12 +174,11 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
{
|
{
|
||||||
QueuedItem = x,
|
QueuedItem = x,
|
||||||
InventoryCount = inventoryManager->GetInventoryItemCount(x.ItemId) +
|
InventoryCount = inventoryManager->GetInventoryItemCount(x.ItemId) +
|
||||||
venturesInProgress.GetValueOrDefault(x.ItemId, 0) +
|
(venturesInProgress.TryGetValue(x.ItemId, out int inProgress)
|
||||||
(list.CheckRetainerInventory
|
? inProgress
|
||||||
? (int)_allaganToolsIpc.GetRetainerItemCount(x.ItemId)
|
|
||||||
: 0),
|
: 0),
|
||||||
})
|
})
|
||||||
.Where(x => x.InventoryCount < x.RequestedCount)
|
.Where(x => x.InventoryCount <= x.RequestedCount)
|
||||||
.ToList()
|
.ToList()
|
||||||
.AsReadOnly();
|
.AsReadOnly();
|
||||||
|
|
||||||
@ -237,8 +208,28 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_configuration.ConfigUiOptions.ShowAssignmentChatMessages || dryRun)
|
_chatGui.Print(
|
||||||
PrintNextVentureMessage(retainerName, venture, reward, list);
|
new SeString(new UIForegroundPayload(579))
|
||||||
|
.Append(SeIconChar.Collectible.ToIconString())
|
||||||
|
.Append(new UIForegroundPayload(0))
|
||||||
|
.Append($" Sending retainer ")
|
||||||
|
.Append(new UIForegroundPayload(1))
|
||||||
|
.Append(retainerName)
|
||||||
|
.Append(new UIForegroundPayload(0))
|
||||||
|
.Append(" to collect ")
|
||||||
|
.Append(new UIForegroundPayload(1))
|
||||||
|
.Append($"{reward.Quantity}x ")
|
||||||
|
.Append(new ItemPayload(venture.ItemId))
|
||||||
|
.Append(venture.Name)
|
||||||
|
.Append(RawPayload.LinkTerminator)
|
||||||
|
.Append(new UIForegroundPayload(0))
|
||||||
|
.Append(" for ")
|
||||||
|
.Append(new UIForegroundPayload(1))
|
||||||
|
.Append($"{list.Name} {list.GetIcon()}")
|
||||||
|
.Append(new UIForegroundPayload(0))
|
||||||
|
.Append("."));
|
||||||
|
_pluginLog.Information(
|
||||||
|
$"Setting AR to use venture {venture.RowId}, which should retrieve {reward.Quantity}x {venture.Name}");
|
||||||
|
|
||||||
if (!dryRun)
|
if (!dryRun)
|
||||||
{
|
{
|
||||||
@ -263,7 +254,21 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
// fallback: managed but no venture found/
|
// fallback: managed but no venture found/
|
||||||
if (retainer.LastVenture != QuickVentureId)
|
if (retainer.LastVenture != QuickVentureId)
|
||||||
{
|
{
|
||||||
PrintEndOfListMessage(retainerName, retainer);
|
_chatGui.Print(
|
||||||
|
new SeString(new UIForegroundPayload(579))
|
||||||
|
.Append(SeIconChar.Collectible.ToIconString())
|
||||||
|
.Append(new UIForegroundPayload(0))
|
||||||
|
.Append($" No tasks left for retainer ")
|
||||||
|
.Append(new UIForegroundPayload(1))
|
||||||
|
.Append(retainerName)
|
||||||
|
.Append(new UIForegroundPayload(0))
|
||||||
|
.Append(", sending to ")
|
||||||
|
.Append(new UIForegroundPayload(1))
|
||||||
|
.Append("Quick Venture")
|
||||||
|
.Append(new UIForegroundPayload(0))
|
||||||
|
.Append("."));
|
||||||
|
_pluginLog.Information($"No tasks left (previous venture = {retainer.LastVenture}), using QV");
|
||||||
|
|
||||||
if (!dryRun)
|
if (!dryRun)
|
||||||
{
|
{
|
||||||
retainer.HasVenture = true;
|
retainer.HasVenture = true;
|
||||||
@ -287,50 +292,6 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrintNextVentureMessage(string retainerName, Venture venture, VentureReward reward, Configuration.ItemList list)
|
|
||||||
{
|
|
||||||
_chatGui.Print(
|
|
||||||
new SeString(new UIForegroundPayload(579))
|
|
||||||
.Append(SeIconChar.Collectible.ToIconString())
|
|
||||||
.Append(new UIForegroundPayload(0))
|
|
||||||
.Append($" Sending retainer ")
|
|
||||||
.Append(new UIForegroundPayload(1))
|
|
||||||
.Append(retainerName)
|
|
||||||
.Append(new UIForegroundPayload(0))
|
|
||||||
.Append(" to collect ")
|
|
||||||
.Append(new UIForegroundPayload(1))
|
|
||||||
.Append($"{reward.Quantity}x ")
|
|
||||||
.Append(new ItemPayload(venture.ItemId))
|
|
||||||
.Append(venture.Name)
|
|
||||||
.Append(RawPayload.LinkTerminator)
|
|
||||||
.Append(new UIForegroundPayload(0))
|
|
||||||
.Append(" for ")
|
|
||||||
.Append(new UIForegroundPayload(1))
|
|
||||||
.Append($"{list.Name} {list.GetIcon()}")
|
|
||||||
.Append(new UIForegroundPayload(0))
|
|
||||||
.Append("."));
|
|
||||||
_pluginLog.Information(
|
|
||||||
$"Setting AR to use venture {venture.RowId}, which should retrieve {reward.Quantity}x {venture.Name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PrintEndOfListMessage(string retainerName, Configuration.RetainerConfiguration retainer)
|
|
||||||
{
|
|
||||||
_chatGui.Print(
|
|
||||||
new SeString(new UIForegroundPayload(579))
|
|
||||||
.Append(SeIconChar.Collectible.ToIconString())
|
|
||||||
.Append(new UIForegroundPayload(0))
|
|
||||||
.Append($" No tasks left for retainer ")
|
|
||||||
.Append(new UIForegroundPayload(1))
|
|
||||||
.Append(retainerName)
|
|
||||||
.Append(new UIForegroundPayload(0))
|
|
||||||
.Append(", sending to ")
|
|
||||||
.Append(new UIForegroundPayload(1))
|
|
||||||
.Append("Quick Venture")
|
|
||||||
.Append(new UIForegroundPayload(0))
|
|
||||||
.Append("."));
|
|
||||||
_pluginLog.Information($"No tasks left (previous venture = {retainer.LastVenture}), using QV");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This treats the retainer who is currently doing the venture as 'in-progress', since I believe the
|
/// This treats the retainer who is currently doing the venture as 'in-progress', since I believe the
|
||||||
/// relevant event is fired BEFORE the venture rewards are collected.
|
/// relevant event is fired BEFORE the venture rewards are collected.
|
||||||
@ -399,7 +360,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
{
|
{
|
||||||
if (arguments == "sync")
|
if (arguments == "sync")
|
||||||
Sync();
|
Sync();
|
||||||
else if (arguments.StartsWith("dnv", StringComparison.Ordinal))
|
else if (arguments.StartsWith("dnv"))
|
||||||
{
|
{
|
||||||
var ch = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == _clientState.LocalContentId);
|
var ch = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == _clientState.LocalContentId);
|
||||||
if (ch == null || ch.Type == Configuration.CharacterType.NotManaged || ch.Retainers.Count == 0)
|
if (ch == null || ch.Type == Configuration.CharacterType.NotManaged || ch.Retainers.Count == 0)
|
||||||
@ -413,10 +374,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
if (s.Length > 1)
|
if (s.Length > 1)
|
||||||
retainerName = ch.Retainers.SingleOrDefault(x => x.Name.EqualsIgnoreCase(s[1]))?.Name;
|
retainerName = ch.Retainers.SingleOrDefault(x => x.Name.EqualsIgnoreCase(s[1]))?.Name;
|
||||||
else
|
else
|
||||||
retainerName = ch.Retainers
|
retainerName = ch.Retainers.MinBy(x => x.DisplayOrder)?.Name;
|
||||||
.OrderBy(x => x.DisplayOrder)
|
|
||||||
.ThenBy(x => x.RetainerContentId)
|
|
||||||
.FirstOrDefault()?.Name;
|
|
||||||
|
|
||||||
if (retainerName == null)
|
if (retainerName == null)
|
||||||
{
|
{
|
||||||
@ -450,7 +408,6 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
||||||
|
|
||||||
_iconCache.Dispose();
|
_iconCache.Dispose();
|
||||||
_autoRetainerReflection.Dispose();
|
|
||||||
_autoRetainerApi.Dispose();
|
_autoRetainerApi.Dispose();
|
||||||
ECommonsMain.Dispose();
|
ECommonsMain.Dispose();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Dalamud.Configuration;
|
using Dalamud.Configuration;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace ARControl;
|
namespace ARControl;
|
||||||
|
|
||||||
@ -22,7 +21,6 @@ internal sealed class Configuration : IPluginConfiguration
|
|||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public required ListType Type { get; set; } = ListType.CollectOneTime;
|
public required ListType Type { get; set; } = ListType.CollectOneTime;
|
||||||
public required ListPriority Priority { get; set; } = ListPriority.InOrder;
|
public required ListPriority Priority { get; set; } = ListPriority.InOrder;
|
||||||
public bool CheckRetainerInventory { get; set; }
|
|
||||||
public List<QueuedItem> Items { get; set; } = new();
|
public List<QueuedItem> Items { get; set; } = new();
|
||||||
|
|
||||||
public string GetIcon()
|
public string GetIcon()
|
||||||
@ -54,9 +52,6 @@ internal sealed class Configuration : IPluginConfiguration
|
|||||||
|
|
||||||
public sealed class QueuedItem
|
public sealed class QueuedItem
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
|
||||||
public Guid InternalId { get; } = Guid.NewGuid();
|
|
||||||
|
|
||||||
public required uint ItemId { get; set; }
|
public required uint ItemId { get; set; }
|
||||||
public required int RemainingQuantity { get; set; }
|
public required int RemainingQuantity { get; set; }
|
||||||
}
|
}
|
||||||
@ -81,7 +76,6 @@ internal sealed class Configuration : IPluginConfiguration
|
|||||||
|
|
||||||
public List<RetainerConfiguration> Retainers { get; set; } = new();
|
public List<RetainerConfiguration> Retainers { get; set; } = new();
|
||||||
public HashSet<uint> GatheredItems { get; set; } = new();
|
public HashSet<uint> GatheredItems { get; set; } = new();
|
||||||
public HashSet<uint> UnlockedFolkloreBooks { get; set; } = new();
|
|
||||||
|
|
||||||
public override string ToString() => $"{CharacterName} @ {WorldName}";
|
public override string ToString() => $"{CharacterName} @ {WorldName}";
|
||||||
}
|
}
|
||||||
@ -103,7 +97,6 @@ internal sealed class Configuration : IPluginConfiguration
|
|||||||
|
|
||||||
public sealed class RetainerConfiguration
|
public sealed class RetainerConfiguration
|
||||||
{
|
{
|
||||||
public ulong RetainerContentId { get; set; }
|
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public required bool Managed { get; set; }
|
public required bool Managed { get; set; }
|
||||||
public int DisplayOrder { get; set; }
|
public int DisplayOrder { get; set; }
|
||||||
@ -126,6 +119,6 @@ internal sealed class Configuration : IPluginConfiguration
|
|||||||
public bool ShowVentureListContents { get; set; } = true;
|
public bool ShowVentureListContents { get; set; } = true;
|
||||||
public bool CheckGatheredItemsPerCharacter { get; set; }
|
public bool CheckGatheredItemsPerCharacter { get; set; }
|
||||||
public bool OnlyShowMissingGatheredItems { get; set; }
|
public bool OnlyShowMissingGatheredItems { get; set; }
|
||||||
public bool ShowAssignmentChatMessages { get; set; } = true;
|
public bool WrapAroundWhenReordering { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
77
ARControl/External/AllaganToolsIpc.cs
vendored
77
ARControl/External/AllaganToolsIpc.cs
vendored
@ -1,77 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Ipc;
|
|
||||||
using Dalamud.Plugin.Ipc.Exceptions;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
||||||
|
|
||||||
namespace ARControl.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,
|
|
||||||
InventoryType.RetainerCrystals,
|
|
||||||
}
|
|
||||||
.Select(x => (uint)x).ToArray();
|
|
||||||
|
|
||||||
private readonly ICallGateSubscriber<ulong,HashSet<ulong[]>> _getClownItems;
|
|
||||||
private readonly ICallGateSubscriber<uint, bool, uint[], uint> _itemCountOwned;
|
|
||||||
|
|
||||||
public AllaganToolsIpc(IDalamudPluginInterface pluginInterface, IPluginLog pluginLog)
|
|
||||||
{
|
|
||||||
_pluginLog = pluginLog;
|
|
||||||
_getClownItems = pluginInterface.GetIpcSubscriber<ulong, HashSet<ulong[]>>("AllaganTools.GetCharacterItems");
|
|
||||||
_itemCountOwned = pluginInterface.GetIpcSubscriber<uint, bool, uint[], uint>("AllaganTools.ItemCountOwned");
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<(uint ItemId, uint Quantity)> GetCharacterItems(ulong contentId)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
HashSet<ulong[]> items = _getClownItems.InvokeFunc(contentId);
|
|
||||||
_pluginLog.Information($"CID: {contentId}, Items: {items.Count}");
|
|
||||||
|
|
||||||
return items.Select(x => (ItemId: (uint)x[2], Quantity: (uint)x[3]))
|
|
||||||
.GroupBy(x => x.ItemId)
|
|
||||||
.Select(x => (x.Key, (uint)x.Sum(y => y.Quantity)))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
catch (TargetInvocationException e)
|
|
||||||
{
|
|
||||||
_pluginLog.Information(e, $"Unable to retrieve items for character {contentId}");
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
catch (IpcError)
|
|
||||||
{
|
|
||||||
_pluginLog.Warning("Could not query allagantools for character items");
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
68
ARControl/External/AutoRetainerReflection.cs
vendored
68
ARControl/External/AutoRetainerReflection.cs
vendored
@ -1,68 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Reflection;
|
|
||||||
using AutoRetainerAPI;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using LLib;
|
|
||||||
|
|
||||||
namespace ARControl.External
|
|
||||||
{
|
|
||||||
internal sealed class AutoRetainerReflection : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IPluginLog _pluginLog;
|
|
||||||
private readonly AutoRetainerApi _autoRetainerApi;
|
|
||||||
private readonly DalamudReflector _reflector;
|
|
||||||
|
|
||||||
public AutoRetainerReflection(IDalamudPluginInterface pluginInterface, IFramework framework,
|
|
||||||
IPluginLog pluginLog, AutoRetainerApi autoRetainerApi)
|
|
||||||
{
|
|
||||||
_pluginLog = pluginLog;
|
|
||||||
_autoRetainerApi = autoRetainerApi;
|
|
||||||
_reflector = new DalamudReflector(pluginInterface, framework, pluginLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SuppressMessage("Performance", "CA2000", Justification = "Should not dispose other plugins")]
|
|
||||||
public bool ShouldReassign
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_autoRetainerApi.Ready &&
|
|
||||||
_reflector.TryGetDalamudPlugin("AutoRetainer", out var autoRetainer, false, true))
|
|
||||||
{
|
|
||||||
var config =
|
|
||||||
autoRetainer.GetType().GetProperty("C", BindingFlags.Static | BindingFlags.NonPublic)!
|
|
||||||
.GetValue(null);
|
|
||||||
if (config == null)
|
|
||||||
{
|
|
||||||
_pluginLog.Warning("Could not retrieve AR config");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dontReassign = (bool)config.GetType()
|
|
||||||
.GetField("_dontReassign", BindingFlags.Instance | BindingFlags.Public)!
|
|
||||||
.GetValue(config)!;
|
|
||||||
_pluginLog.Verbose($"DontReassign is set to {dontReassign}");
|
|
||||||
return !dontReassign;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_pluginLog.Warning("Could not check if reassign is enabled, AutoRetainer not loaded");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_pluginLog.Warning(e, "Unable to check if reassign is enabled");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_reflector.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
30
ARControl/External/DiscardHelperIpc.cs
vendored
30
ARControl/External/DiscardHelperIpc.cs
vendored
@ -1,30 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Ipc;
|
|
||||||
using Dalamud.Plugin.Ipc.Exceptions;
|
|
||||||
|
|
||||||
namespace ARControl.External;
|
|
||||||
|
|
||||||
internal sealed class DiscardHelperIpc
|
|
||||||
{
|
|
||||||
private readonly ICallGateSubscriber<IReadOnlySet<uint>> _itemsToDiscard;
|
|
||||||
|
|
||||||
public DiscardHelperIpc(IDalamudPluginInterface pluginInterface)
|
|
||||||
{
|
|
||||||
_itemsToDiscard = pluginInterface.GetIpcSubscriber<IReadOnlySet<uint>>("ARDiscard.GetItemsToDiscard");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlySet<uint> GetItemsToDiscard()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _itemsToDiscard.InvokeFunc();
|
|
||||||
}
|
|
||||||
catch (IpcError)
|
|
||||||
{
|
|
||||||
// ignore
|
|
||||||
return ImmutableHashSet<uint>.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
|
||||||
|
|
||||||
namespace ARControl.GameData;
|
|
||||||
|
|
||||||
internal sealed class FolkloreBook
|
|
||||||
{
|
|
||||||
public required uint ItemId { get; init; }
|
|
||||||
public required string Name { get; init; }
|
|
||||||
public required IReadOnlyList<ushort> GatheringSubCategories { get; init; }
|
|
||||||
public List<uint> GatheringItemIds { get; } = [];
|
|
||||||
public ushort TomeId { get; set; }
|
|
||||||
|
|
||||||
public unsafe bool IsUnlocked()
|
|
||||||
{
|
|
||||||
return PlayerState.Instance()->IsFolkloreBookUnlocked(TomeId);
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,74 +22,9 @@ internal sealed class GameCache
|
|||||||
.OrderBy(x => x.Name)
|
.OrderBy(x => x.Name)
|
||||||
.ToList()
|
.ToList()
|
||||||
.AsReadOnly();
|
.AsReadOnly();
|
||||||
FolkloreBooks = dataManager.GetExcelSheet<GatheringSubCategory>()!
|
|
||||||
.Where(x => x.RowId > 0)
|
|
||||||
.Where(x => x.Item.Row != 0)
|
|
||||||
.Select(x => new
|
|
||||||
{
|
|
||||||
x.RowId,
|
|
||||||
ItemId = x.Item.Row,
|
|
||||||
ItemName = x.Item.Value!.Name.ToString()
|
|
||||||
})
|
|
||||||
.GroupBy(x => (x.ItemId, x.ItemName))
|
|
||||||
.Select(x =>
|
|
||||||
new FolkloreBook
|
|
||||||
{
|
|
||||||
ItemId = x.Key.ItemId,
|
|
||||||
Name = x.Key.ItemName,
|
|
||||||
GatheringSubCategories = x.Select(y => (ushort)y.RowId).ToList(),
|
|
||||||
})
|
|
||||||
.ToDictionary(x => x.ItemId, x => x);
|
|
||||||
|
|
||||||
var gatheringNodes = dataManager.GetExcelSheet<GatheringPointBase>()!
|
|
||||||
.Where(x => x.RowId > 0 && x.GatheringType.Row <= 3)
|
|
||||||
.Select(x =>
|
|
||||||
new
|
|
||||||
{
|
|
||||||
GatheringPointBaseId = x.RowId,
|
|
||||||
GatheringPoint =
|
|
||||||
dataManager.GetExcelSheet<GatheringPoint>()!.FirstOrDefault(y =>
|
|
||||||
y.GatheringPointBase.Row == x.RowId),
|
|
||||||
Items = x.Item.Where(y => y > 0).ToList()
|
|
||||||
})
|
|
||||||
.Where(x => x.GatheringPoint != null)
|
|
||||||
.Select(x =>
|
|
||||||
new
|
|
||||||
{
|
|
||||||
x.GatheringPointBaseId,
|
|
||||||
CategoryId = (ushort)x.GatheringPoint!.GatheringSubCategory.Row,
|
|
||||||
x.Items,
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
var itemsWithoutTomes = gatheringNodes
|
|
||||||
.Where(x => !FolkloreBooks.Values.Any(y => y.GatheringSubCategories.Contains(x.CategoryId)))
|
|
||||||
.SelectMany(x => x.Items)
|
|
||||||
.ToList();
|
|
||||||
var itemsWithTomes = gatheringNodes
|
|
||||||
.SelectMany(x => x.Items
|
|
||||||
.Where(y => !itemsWithoutTomes.Contains(y))
|
|
||||||
.Select(
|
|
||||||
y =>
|
|
||||||
new
|
|
||||||
{
|
|
||||||
x.CategoryId,
|
|
||||||
ItemId = (uint)y
|
|
||||||
}))
|
|
||||||
.GroupBy(x => x.CategoryId)
|
|
||||||
.ToDictionary(x => x.Key, x => x.Select(y => y.ItemId).ToList());
|
|
||||||
foreach (var book in FolkloreBooks.Values)
|
|
||||||
{
|
|
||||||
book.TomeId = dataManager.GetExcelSheet<Item>()!.GetRow(book.ItemId)!.ItemAction.Value!.Data[0];
|
|
||||||
foreach (var category in book.GatheringSubCategories)
|
|
||||||
{
|
|
||||||
if (itemsWithTomes.TryGetValue(category, out var itemsInCategory))
|
|
||||||
book.GatheringItemIds.AddRange(itemsInCategory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyDictionary<uint, string> Jobs { get; }
|
public IReadOnlyDictionary<uint, string> Jobs { get; }
|
||||||
public IReadOnlyList<Venture> Ventures { get; }
|
public IReadOnlyList<Venture> Ventures { get; }
|
||||||
public IReadOnlyList<ItemToGather> ItemsToGather { get; }
|
public IReadOnlyList<ItemToGather> ItemsToGather { get; }
|
||||||
public Dictionary<uint, FolkloreBook> FolkloreBooks { get; }
|
|
||||||
}
|
}
|
||||||
|
@ -1,207 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using ECommons;
|
|
||||||
using ImGuiNET;
|
|
||||||
|
|
||||||
namespace ARControl.Windows.Config;
|
|
||||||
|
|
||||||
internal sealed class CharacterGroupTab : ITab
|
|
||||||
{
|
|
||||||
private readonly ConfigWindow _configWindow;
|
|
||||||
private readonly Configuration _configuration;
|
|
||||||
private readonly Dictionary<Guid, TemporaryConfig> _currentEditPopups = new();
|
|
||||||
|
|
||||||
private TemporaryConfig _newGroup = new() { Name = string.Empty };
|
|
||||||
|
|
||||||
public CharacterGroupTab(ConfigWindow configWindow, Configuration configuration)
|
|
||||||
{
|
|
||||||
_configWindow = configWindow;
|
|
||||||
_configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Groups###TabGroups");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Configuration.CharacterGroup? groupToDelete = null;
|
|
||||||
foreach (var group in _configuration.CharacterGroups)
|
|
||||||
{
|
|
||||||
ImGui.PushID($"##Group{group.Id}");
|
|
||||||
|
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Cog))
|
|
||||||
{
|
|
||||||
_currentEditPopups[group.Id] = new TemporaryConfig
|
|
||||||
{
|
|
||||||
Name = group.Name,
|
|
||||||
};
|
|
||||||
ImGui.OpenPopup($"##EditGroup{group.Id}");
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawCharacterGroupEditorPopup(group, out var assignedCharacters, ref groupToDelete);
|
|
||||||
ImGui.SameLine();
|
|
||||||
DrawCharacterGroup(group, assignedCharacters);
|
|
||||||
|
|
||||||
ImGui.PopID();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupToDelete != null)
|
|
||||||
{
|
|
||||||
_configuration.CharacterGroups.Remove(groupToDelete);
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
DrawNewCharacterGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCharacterGroupEditorPopup(Configuration.CharacterGroup group,
|
|
||||||
out List<Configuration.CharacterConfiguration> assignedCharacters,
|
|
||||||
ref Configuration.CharacterGroup? groupToDelete)
|
|
||||||
{
|
|
||||||
assignedCharacters = _configuration.Characters
|
|
||||||
.Where(x => x.Type == Configuration.CharacterType.PartOfCharacterGroup &&
|
|
||||||
x.CharacterGroupId == group.Id)
|
|
||||||
.OrderBy(x => x.WorldName)
|
|
||||||
.ThenBy(x => x.LocalContentId)
|
|
||||||
.ToList();
|
|
||||||
if (_currentEditPopups.TryGetValue(group.Id, out TemporaryConfig? temporaryConfig) &&
|
|
||||||
ImGui.BeginPopup($"##EditGroup{group.Id}"))
|
|
||||||
{
|
|
||||||
(bool save, bool canSave) = DrawGroupEditor(temporaryConfig, group);
|
|
||||||
|
|
||||||
ImGui.BeginDisabled(!canSave || group.Name == temporaryConfig.Name);
|
|
||||||
save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save");
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
if (save && canSave)
|
|
||||||
{
|
|
||||||
group.Name = temporaryConfig.Name;
|
|
||||||
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.BeginDisabled(assignedCharacters.Count > 0);
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, "Delete"))
|
|
||||||
{
|
|
||||||
groupToDelete = group;
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
if (assignedCharacters.Count > 0 && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
||||||
{
|
|
||||||
ImGui.BeginTooltip();
|
|
||||||
ImGui.Text(
|
|
||||||
$"Remove the {assignedCharacters.Count} character(s) from this group before deleting it.");
|
|
||||||
foreach (var character in assignedCharacters)
|
|
||||||
ImGui.BulletText($"{character.CharacterName} @ {character.WorldName}");
|
|
||||||
ImGui.EndTooltip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCharacterGroup(Configuration.CharacterGroup group,
|
|
||||||
List<Configuration.CharacterConfiguration> assignedCharacters)
|
|
||||||
{
|
|
||||||
string countLabel = assignedCharacters.Count == 0 ? "no characters"
|
|
||||||
: assignedCharacters.Count == 1 ? "1 character"
|
|
||||||
: $"{assignedCharacters.Count} characters";
|
|
||||||
if (ImGui.CollapsingHeader($"{group.Name} ({countLabel})"))
|
|
||||||
{
|
|
||||||
ImGui.Indent(_configWindow.MainIndentSize);
|
|
||||||
using (var tabBar = ImRaii.TabBar("GroupOptions"))
|
|
||||||
{
|
|
||||||
if (tabBar)
|
|
||||||
{
|
|
||||||
using (var ventureListTab = ImRaii.TabItem("Venture Lists"))
|
|
||||||
{
|
|
||||||
if (ventureListTab)
|
|
||||||
_configWindow.DrawVentureListSelection(group.Id.ToString(), group.ItemListIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var charactersTab = ImRaii.TabItem("Characters"))
|
|
||||||
{
|
|
||||||
if (charactersTab)
|
|
||||||
{
|
|
||||||
ImGui.Text("Characters in this group:");
|
|
||||||
ImGui.Indent(_configWindow.MainIndentSize);
|
|
||||||
foreach (var character in assignedCharacters.OrderBy(x => x.WorldName)
|
|
||||||
.ThenBy(x => x.LocalContentId))
|
|
||||||
ImGui.TextUnformatted($"{character.CharacterName} @ {character.WorldName}");
|
|
||||||
ImGui.Unindent(_configWindow.MainIndentSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Unindent(_configWindow.MainIndentSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawNewCharacterGroup()
|
|
||||||
{
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Group"))
|
|
||||||
ImGui.OpenPopup("##AddGroup");
|
|
||||||
|
|
||||||
if (ImGui.BeginPopup("##AddGroup"))
|
|
||||||
{
|
|
||||||
(bool save, bool canSave) = DrawGroupEditor(_newGroup, null);
|
|
||||||
|
|
||||||
ImGui.BeginDisabled(!canSave);
|
|
||||||
save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save");
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
if (save && canSave)
|
|
||||||
{
|
|
||||||
_configuration.CharacterGroups.Add(new Configuration.CharacterGroup
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Name = _newGroup.Name,
|
|
||||||
ItemListIds = new(),
|
|
||||||
});
|
|
||||||
|
|
||||||
_newGroup = new() { Name = string.Empty };
|
|
||||||
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private (bool Save, bool CanSave) DrawGroupEditor(TemporaryConfig group,
|
|
||||||
Configuration.CharacterGroup? existingGroup)
|
|
||||||
{
|
|
||||||
string name = group.Name;
|
|
||||||
bool save = ImGui.InputTextWithHint("", "Group Name...", ref name, 64, ImGuiInputTextFlags.EnterReturnsTrue);
|
|
||||||
bool canSave = IsValidGroupName(name, existingGroup);
|
|
||||||
|
|
||||||
group.Name = name;
|
|
||||||
return (save, canSave);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsValidGroupName(string name, Configuration.CharacterGroup? existingGroup)
|
|
||||||
{
|
|
||||||
return name.Length >= 2 &&
|
|
||||||
!name.Contains('%', StringComparison.Ordinal) &&
|
|
||||||
!_configuration.CharacterGroups.Any(x => x != existingGroup && name.EqualsIgnoreCase(x.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class TemporaryConfig
|
|
||||||
{
|
|
||||||
public required string Name { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
namespace ARControl.Windows.Config;
|
|
||||||
|
|
||||||
internal interface ITab
|
|
||||||
{
|
|
||||||
public void Draw();
|
|
||||||
}
|
|
@ -1,295 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using ARControl.External;
|
|
||||||
using ARControl.GameData;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using ImGuiNET;
|
|
||||||
|
|
||||||
namespace ARControl.Windows.Config;
|
|
||||||
|
|
||||||
internal sealed class InventoryTab : ITab
|
|
||||||
{
|
|
||||||
private readonly Configuration _configuration;
|
|
||||||
private readonly AllaganToolsIpc _allaganToolsIpc;
|
|
||||||
private readonly GameCache _gameCache;
|
|
||||||
private readonly IPluginLog _pluginLog;
|
|
||||||
|
|
||||||
private List<TreeNode>? _listAsTrees;
|
|
||||||
private DateTime? _lastUpdate;
|
|
||||||
|
|
||||||
public InventoryTab(Configuration configuration, AllaganToolsIpc allaganToolsIpc, GameCache gameCache,
|
|
||||||
IPluginLog pluginLog)
|
|
||||||
{
|
|
||||||
_configuration = configuration;
|
|
||||||
_allaganToolsIpc = allaganToolsIpc;
|
|
||||||
_gameCache = gameCache;
|
|
||||||
_pluginLog = pluginLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Inventory###TabInventory");
|
|
||||||
if (!tab)
|
|
||||||
{
|
|
||||||
_listAsTrees = null;
|
|
||||||
_lastUpdate = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_listAsTrees == null)
|
|
||||||
RefreshInventory();
|
|
||||||
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Redo, "Refresh"))
|
|
||||||
RefreshInventory();
|
|
||||||
|
|
||||||
if (_lastUpdate != null)
|
|
||||||
{
|
|
||||||
string text = $"Last Update: {_lastUpdate:t}";
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X - ImGui.CalcTextSize(text).X + ImGui.GetCursorPosX());
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
if (_listAsTrees == null || _listAsTrees.Count == 0)
|
|
||||||
{
|
|
||||||
ImGui.Text("No items in inventory. Do you have AllaganTools installed?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var list in _configuration.ItemLists)
|
|
||||||
{
|
|
||||||
using var id = ImRaii.PushId($"List{list.Id}");
|
|
||||||
if (ImGui.CollapsingHeader($"{list.Name} {list.GetIcon()}"))
|
|
||||||
{
|
|
||||||
using var indent = ImRaii.PushIndent();
|
|
||||||
var rootNode = _listAsTrees.FirstOrDefault(x => x.Id == list.Id.ToString());
|
|
||||||
if (rootNode == null || rootNode.Children.Count == 0)
|
|
||||||
{
|
|
||||||
ImGui.Text("This list is empty.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var table = ImRaii.Table($"InventoryTable{list.Id}", 2, ImGuiTableFlags.NoSavedSettings);
|
|
||||||
if (!table)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.NoHide);
|
|
||||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 120 * ImGui.GetIO().FontGlobalScale);
|
|
||||||
|
|
||||||
foreach (var child in rootNode.Children)
|
|
||||||
child.Draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshInventory()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
List<CharacterInventory> inventories = new();
|
|
||||||
foreach (Configuration.CharacterConfiguration character in _configuration.Characters)
|
|
||||||
{
|
|
||||||
List<Guid> itemListIds = new();
|
|
||||||
if (character.Type == Configuration.CharacterType.Standalone)
|
|
||||||
{
|
|
||||||
itemListIds = character.ItemListIds;
|
|
||||||
}
|
|
||||||
else if (character.Type == Configuration.CharacterType.PartOfCharacterGroup)
|
|
||||||
{
|
|
||||||
var group = _configuration.CharacterGroups.SingleOrDefault(x => x.Id == character.CharacterGroupId);
|
|
||||||
if (group != null)
|
|
||||||
itemListIds = group.ItemListIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
var inventory = new CharacterInventory(character, itemListIds);
|
|
||||||
|
|
||||||
var itemIdsOnLists = itemListIds.Where(listId => listId != Guid.Empty)
|
|
||||||
.Select(listId => _configuration.ItemLists.SingleOrDefault(x => x.Id == listId))
|
|
||||||
.Where(list => list != null)
|
|
||||||
.SelectMany(list => list!.Items.Select(x => x.ItemId))
|
|
||||||
.Distinct()
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
UpdateOwnedItems(character.LocalContentId, inventory.Items, itemIdsOnLists);
|
|
||||||
foreach (var retainer in inventory.Retainers)
|
|
||||||
UpdateOwnedItems(retainer.Configuration.RetainerContentId, retainer.Items, itemIdsOnLists);
|
|
||||||
|
|
||||||
inventories.Add(inventory);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TreeNode> listAsTrees = [];
|
|
||||||
if (inventories.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var list in _configuration.ItemLists)
|
|
||||||
{
|
|
||||||
TreeNode rootNode = new TreeNode(list.Id.ToString(), string.Empty, -1);
|
|
||||||
listAsTrees.Add(rootNode);
|
|
||||||
|
|
||||||
var relevantCharacters = inventories.Where(x => x.ItemListIds.Contains(list.Id)).ToList();
|
|
||||||
foreach (var item in list.Items)
|
|
||||||
{
|
|
||||||
var venture = _gameCache.Ventures.FirstOrDefault(x => x.ItemId == item.ItemId);
|
|
||||||
var total = relevantCharacters.Sum(x => x.CountItems(item.ItemId, list.CheckRetainerInventory));
|
|
||||||
TreeNode itemNode = rootNode.AddChild(item.InternalId.ToString(), venture?.Name ?? string.Empty,
|
|
||||||
total);
|
|
||||||
|
|
||||||
foreach (var character in relevantCharacters)
|
|
||||||
{
|
|
||||||
string characterName =
|
|
||||||
$"{character.Configuration.CharacterName} @ {character.Configuration.WorldName}";
|
|
||||||
long? stockQuantity = list.Type == Configuration.ListType.KeepStocked
|
|
||||||
? item.RemainingQuantity
|
|
||||||
: null;
|
|
||||||
uint characterCount = character.CountItems(item.ItemId, list.CheckRetainerInventory);
|
|
||||||
if (characterCount == 0)
|
|
||||||
continue;
|
|
||||||
var characterNode = itemNode.AddChild(
|
|
||||||
character.Configuration.LocalContentId.ToString(CultureInfo.InvariantCulture),
|
|
||||||
characterName, characterCount, stockQuantity);
|
|
||||||
|
|
||||||
if (list.CheckRetainerInventory)
|
|
||||||
{
|
|
||||||
characterNode.AddChild("Self", "In Inventory",
|
|
||||||
character.CountItems(item.ItemId, false));
|
|
||||||
|
|
||||||
foreach (var retainer in character.Retainers)
|
|
||||||
{
|
|
||||||
uint retainerCount = retainer.CountItems(item.ItemId);
|
|
||||||
if (retainerCount == 0)
|
|
||||||
continue;
|
|
||||||
characterNode.AddChild(
|
|
||||||
retainer.Configuration.RetainerContentId.ToString(CultureInfo.InvariantCulture),
|
|
||||||
retainer.Configuration.Name, retainerCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_listAsTrees = listAsTrees;
|
|
||||||
_lastUpdate = DateTime.Now;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_listAsTrees = [];
|
|
||||||
_pluginLog.Error(e, "Failed to load inventories via AllaganTools");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateOwnedItems(ulong localContentId, List<Item> items, HashSet<uint> itemIdsOnLists)
|
|
||||||
{
|
|
||||||
var ownedItems = _allaganToolsIpc.GetCharacterItems(localContentId);
|
|
||||||
foreach (var ownedItem in ownedItems)
|
|
||||||
{
|
|
||||||
if (!itemIdsOnLists.Contains(ownedItem.ItemId))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
items.Add(new Item(ownedItem.ItemId, ownedItem.Quantity));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class CharacterInventory
|
|
||||||
{
|
|
||||||
public CharacterInventory(Configuration.CharacterConfiguration configuration, List<Guid> itemListIds)
|
|
||||||
{
|
|
||||||
Configuration = configuration;
|
|
||||||
ItemListIds = itemListIds;
|
|
||||||
Retainers = configuration.Retainers.Where(x => x is { Job: > 0, Managed: true })
|
|
||||||
.OrderBy(x => x.DisplayOrder)
|
|
||||||
.ThenBy(x => x.RetainerContentId)
|
|
||||||
.Select(x => new RetainerInventory(x))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Configuration.CharacterConfiguration Configuration { get; }
|
|
||||||
public List<Guid> ItemListIds { get; }
|
|
||||||
public List<RetainerInventory> Retainers { get; }
|
|
||||||
public List<Item> Items { get; } = [];
|
|
||||||
|
|
||||||
public uint CountItems(uint itemId, bool checkRetainerInventory)
|
|
||||||
{
|
|
||||||
uint sum = (uint)Items.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
|
|
||||||
if (checkRetainerInventory)
|
|
||||||
sum += (uint)Retainers.Sum(x => x.CountItems(itemId));
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class RetainerInventory(Configuration.RetainerConfiguration configuration)
|
|
||||||
{
|
|
||||||
public Configuration.RetainerConfiguration Configuration { get; } = configuration;
|
|
||||||
public List<Item> Items { get; } = [];
|
|
||||||
|
|
||||||
public uint CountItems(uint itemId) => (uint)Items.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed record Item(uint ItemId, uint Quantity);
|
|
||||||
|
|
||||||
private sealed record TreeNode(string Id, string Label, long Quantity, long? StockQuantity = null)
|
|
||||||
{
|
|
||||||
public List<TreeNode> Children { get; } = [];
|
|
||||||
|
|
||||||
public TreeNode AddChild(string id, string label, long quantity, long? stockQuantity = null)
|
|
||||||
{
|
|
||||||
TreeNode child = new TreeNode(id, label, quantity, stockQuantity);
|
|
||||||
Children.Add(child);
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
ImGui.TableNextRow();
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
if (Children.Count > 0)
|
|
||||||
{
|
|
||||||
bool open = ImGui.TreeNodeEx(Label, ImGuiTreeNodeFlags.SpanFullWidth);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
DrawCount();
|
|
||||||
|
|
||||||
if (open)
|
|
||||||
{
|
|
||||||
foreach (var child in Children)
|
|
||||||
child.Draw();
|
|
||||||
|
|
||||||
ImGui.TreePop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TreeNodeEx(Label,
|
|
||||||
ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.SpanFullWidth);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
DrawCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCount()
|
|
||||||
{
|
|
||||||
if (StockQuantity != null)
|
|
||||||
{
|
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.Text, Quantity >= StockQuantity.Value
|
|
||||||
? ImGuiColors.HealerGreen
|
|
||||||
: ImGuiColors.DalamudRed);
|
|
||||||
ImGui.TextUnformatted(string.Create(CultureInfo.CurrentCulture,
|
|
||||||
$"{Quantity:N0} / {StockQuantity.Value:N0}"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ImGui.TextUnformatted(Quantity.ToString("N0", CultureInfo.CurrentCulture));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,266 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using ARControl.GameData;
|
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using ImGuiNET;
|
|
||||||
|
|
||||||
namespace ARControl.Windows.Config;
|
|
||||||
|
|
||||||
internal sealed class LockedItemsTab : ITab
|
|
||||||
{
|
|
||||||
private static readonly Vector4 ColorGreen = ImGuiColors.HealerGreen;
|
|
||||||
private static readonly Vector4 ColorRed = ImGuiColors.DalamudRed;
|
|
||||||
private static readonly Vector4 ColorGrey = ImGuiColors.DalamudGrey;
|
|
||||||
private static readonly string CurrentCharPrefix = FontAwesomeIcon.Male.ToIconString();
|
|
||||||
|
|
||||||
private readonly ConfigWindow _configWindow;
|
|
||||||
private readonly Configuration _configuration;
|
|
||||||
private readonly IClientState _clientState;
|
|
||||||
private readonly ICommandManager _commandManager;
|
|
||||||
private readonly GameCache _gameCache;
|
|
||||||
|
|
||||||
public LockedItemsTab(ConfigWindow configWindow, Configuration configuration, IClientState clientState,
|
|
||||||
ICommandManager commandManager, GameCache gameCache)
|
|
||||||
{
|
|
||||||
_configWindow = configWindow;
|
|
||||||
_configuration = configuration;
|
|
||||||
_clientState = clientState;
|
|
||||||
_commandManager = commandManager;
|
|
||||||
_gameCache = gameCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Locked Items###TabLockedItems");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
bool checkPerCharacter = _configuration.ConfigUiOptions.CheckGatheredItemsPerCharacter;
|
|
||||||
if (ImGui.Checkbox("Group by character", ref checkPerCharacter))
|
|
||||||
{
|
|
||||||
_configuration.ConfigUiOptions.CheckGatheredItemsPerCharacter = checkPerCharacter;
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool onlyShowMissing = _configuration.ConfigUiOptions.OnlyShowMissingGatheredItems;
|
|
||||||
if (ImGui.Checkbox("Only show missing items", ref onlyShowMissing))
|
|
||||||
{
|
|
||||||
_configuration.ConfigUiOptions.OnlyShowMissingGatheredItems = onlyShowMissing;
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
var itemsToCheck =
|
|
||||||
_configuration.ItemLists
|
|
||||||
.SelectMany(x => x.Items)
|
|
||||||
.Select(x => x.ItemId)
|
|
||||||
.Distinct()
|
|
||||||
.Select(itemId => new
|
|
||||||
{
|
|
||||||
GatheredItem = _gameCache.ItemsToGather.SingleOrDefault(x => x.ItemId == itemId),
|
|
||||||
Ventures = _gameCache.Ventures.Where(x => x.ItemId == itemId).ToList()
|
|
||||||
})
|
|
||||||
.Where(x => x.GatheredItem != null && x.Ventures.Count > 0)
|
|
||||||
.Select(x => new CheckedItem
|
|
||||||
{
|
|
||||||
GatheredItem = x.GatheredItem!,
|
|
||||||
Ventures = x.Ventures,
|
|
||||||
ItemId = x.Ventures.First().ItemId,
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var charactersToCheck = _configuration.Characters
|
|
||||||
.Where(x => x.Type != Configuration.CharacterType.NotManaged)
|
|
||||||
.OrderBy(x => x.WorldName)
|
|
||||||
.ThenBy(x => x.LocalContentId)
|
|
||||||
.Select(x => new CheckedCharacter(_configuration, x, itemsToCheck))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (checkPerCharacter)
|
|
||||||
{
|
|
||||||
foreach (var ch in charactersToCheck.Where(x => x.ToCheck(onlyShowMissing).Count != 0))
|
|
||||||
{
|
|
||||||
bool currentCharacter = _clientState.LocalContentId == ch.Character.LocalContentId;
|
|
||||||
ImGui.BeginDisabled(currentCharacter);
|
|
||||||
if (ImGuiComponents.IconButton($"SwitchCharacters{ch.Character.LocalContentId}",
|
|
||||||
FontAwesomeIcon.DoorOpen))
|
|
||||||
{
|
|
||||||
_commandManager.ProcessCommand(
|
|
||||||
$"/ays relog {ch.Character.CharacterName}@{ch.Character.WorldName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (currentCharacter)
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
|
|
||||||
|
|
||||||
bool expanded = ImGui.CollapsingHeader($"{ch.Character}###GatheredCh{ch.Character.LocalContentId}");
|
|
||||||
if (currentCharacter)
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
|
|
||||||
if (expanded)
|
|
||||||
{
|
|
||||||
ImGui.Indent(_configWindow.MainIndentSize + ImGui.GetStyle().FramePadding.X);
|
|
||||||
foreach (var item in itemsToCheck.Where(x =>
|
|
||||||
ch.ToCheck(onlyShowMissing).ContainsKey(x.ItemId)))
|
|
||||||
{
|
|
||||||
var color = ch.Items[item.ItemId];
|
|
||||||
if (color != ColorGrey)
|
|
||||||
{
|
|
||||||
string itemName = item.GatheredItem.Name;
|
|
||||||
var folkloreBook = _gameCache.FolkloreBooks.Values.FirstOrDefault(x =>
|
|
||||||
x.GatheringItemIds.Contains(item.GatheredItem.GatheredItemId));
|
|
||||||
if (folkloreBook != null && !ch.Character.UnlockedFolkloreBooks.Contains(folkloreBook.ItemId))
|
|
||||||
itemName += $" ({SeIconChar.Prohibited.ToIconString()} {folkloreBook.Name})";
|
|
||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
|
||||||
if (currentCharacter && color == ColorRed)
|
|
||||||
{
|
|
||||||
ImGui.Selectable(itemName);
|
|
||||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
|
||||||
{
|
|
||||||
uint classJob = _clientState.LocalPlayer!.ClassJob.Id;
|
|
||||||
if (classJob == 16)
|
|
||||||
_commandManager.ProcessCommand($"/gathermin {item.GatheredItem.Name}");
|
|
||||||
else if (classJob == 17)
|
|
||||||
_commandManager.ProcessCommand($"/gatherbtn {item.GatheredItem.Name}");
|
|
||||||
else if (classJob == 18)
|
|
||||||
_commandManager.ProcessCommand($"/gatherfish {item.GatheredItem.Name}");
|
|
||||||
else
|
|
||||||
_commandManager.ProcessCommand($"/gather {item.GatheredItem.Name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.Text(itemName);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Unindent(_configWindow.MainIndentSize + ImGui.GetStyle().FramePadding.X);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var item in itemsToCheck.Where(x =>
|
|
||||||
charactersToCheck.Any(y => y.ToCheck(onlyShowMissing).ContainsKey(x.ItemId))))
|
|
||||||
{
|
|
||||||
var folkloreBook = _gameCache.FolkloreBooks.Values.FirstOrDefault(x =>
|
|
||||||
x.GatheringItemIds.Contains(item.GatheredItem.GatheredItemId));
|
|
||||||
if (ImGui.CollapsingHeader($"{item.GatheredItem.Name}##Gathered{item.GatheredItem.ItemId}"))
|
|
||||||
{
|
|
||||||
ImGui.Indent(_configWindow.MainIndentSize + ImGui.GetStyle().FramePadding.X);
|
|
||||||
foreach (var ch in charactersToCheck)
|
|
||||||
{
|
|
||||||
var color = ch.Items[item.ItemId];
|
|
||||||
if (color == ColorRed || (color == ColorGreen && !onlyShowMissing))
|
|
||||||
{
|
|
||||||
bool currentCharacter = _clientState.LocalContentId == ch.Character.LocalContentId;
|
|
||||||
if (currentCharacter)
|
|
||||||
{
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
var pos = ImGui.GetCursorPos();
|
|
||||||
ImGui.SetCursorPos(pos with
|
|
||||||
{
|
|
||||||
X = pos.X - ImGui.CalcTextSize(CurrentCharPrefix).X - 5
|
|
||||||
});
|
|
||||||
ImGui.TextUnformatted(CurrentCharPrefix);
|
|
||||||
ImGui.SetCursorPos(pos);
|
|
||||||
ImGui.PopFont();
|
|
||||||
}
|
|
||||||
|
|
||||||
string characterName = ch.Character.ToString();
|
|
||||||
if (folkloreBook != null && !ch.Character.UnlockedFolkloreBooks.Contains(folkloreBook.ItemId))
|
|
||||||
characterName += $" ({SeIconChar.Prohibited.ToIconString()} {folkloreBook.Name})";
|
|
||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
|
||||||
ImGui.TextUnformatted(characterName);
|
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Unindent(_configWindow.MainIndentSize + ImGui.GetStyle().FramePadding.X);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class CheckedCharacter
|
|
||||||
{
|
|
||||||
public CheckedCharacter(Configuration configuration, Configuration.CharacterConfiguration character,
|
|
||||||
List<CheckedItem> itemsToCheck)
|
|
||||||
{
|
|
||||||
Character = character;
|
|
||||||
|
|
||||||
List<Guid> itemListIds = new();
|
|
||||||
if (character.Type == Configuration.CharacterType.Standalone)
|
|
||||||
{
|
|
||||||
itemListIds = character.ItemListIds;
|
|
||||||
}
|
|
||||||
else if (character.Type == Configuration.CharacterType.PartOfCharacterGroup)
|
|
||||||
{
|
|
||||||
var group = configuration.CharacterGroups.SingleOrDefault(x => x.Id == character.CharacterGroupId);
|
|
||||||
if (group != null)
|
|
||||||
itemListIds = group.ItemListIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemIdsOnLists = itemListIds.Where(listId => listId != Guid.Empty)
|
|
||||||
.Select(listId => configuration.ItemLists.SingleOrDefault(x => x.Id == listId))
|
|
||||||
.Where(list => list != null)
|
|
||||||
.SelectMany(list => list!.Items)
|
|
||||||
.Select(x => x.ItemId)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var item in itemsToCheck)
|
|
||||||
{
|
|
||||||
// check if the item is on any relevant list
|
|
||||||
if (!itemIdsOnLists.Contains(item.ItemId))
|
|
||||||
{
|
|
||||||
Items[item.ItemId] = ColorGrey;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if we are the correct job
|
|
||||||
bool enabled = character.Retainers.Any(x => item.Ventures.Any(v => v.MatchesJob(x.Job)));
|
|
||||||
if (enabled)
|
|
||||||
{
|
|
||||||
// do we have it gathered on this char?
|
|
||||||
if (character.GatheredItems.Contains(item.GatheredItem.GatheredItemId))
|
|
||||||
Items[item.ItemId] = ColorGreen;
|
|
||||||
else
|
|
||||||
Items[item.ItemId] = ColorRed;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Items[item.ItemId] = ColorGrey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Configuration.CharacterConfiguration Character { get; }
|
|
||||||
public Dictionary<uint, Vector4> Items { get; } = new();
|
|
||||||
|
|
||||||
public Dictionary<uint, Vector4> ToCheck(bool onlyShowMissing)
|
|
||||||
{
|
|
||||||
return Items
|
|
||||||
.Where(x => x.Value == ColorRed || (x.Value == ColorGreen && !onlyShowMissing))
|
|
||||||
.ToDictionary(x => x.Key, x => x.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class CheckedItem
|
|
||||||
{
|
|
||||||
public required ItemToGather GatheredItem { get; init; }
|
|
||||||
public required List<Venture> Ventures { get; init; }
|
|
||||||
public required uint ItemId { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using ImGuiNET;
|
|
||||||
|
|
||||||
namespace ARControl.Windows.Config;
|
|
||||||
|
|
||||||
internal sealed class MiscTab : ITab
|
|
||||||
{
|
|
||||||
private readonly ConfigWindow _configWindow;
|
|
||||||
private readonly Configuration _configuration;
|
|
||||||
|
|
||||||
public MiscTab(ConfigWindow configWindow, Configuration configuration)
|
|
||||||
{
|
|
||||||
_configWindow = configWindow;
|
|
||||||
_configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Misc###TabMisc");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ImGui.Text("Venture Settings");
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(130);
|
|
||||||
int venturesToKeep = _configuration.Misc.VenturesToKeep;
|
|
||||||
if (ImGui.InputInt("Minimum Ventures needed to assign retainers", ref venturesToKeep))
|
|
||||||
{
|
|
||||||
_configuration.Misc.VenturesToKeep = Math.Max(0, Math.Min(65000, venturesToKeep));
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGuiComponents.HelpMarker(
|
|
||||||
$"If you have less than {venturesToKeep} ventures, retainers will only be sent out for Quick Ventures (instead of picking the next item from the Venture List).");
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGui.Text("User Interface Settings");
|
|
||||||
|
|
||||||
bool showAssignmentChatMessages = _configuration.ConfigUiOptions.ShowAssignmentChatMessages;
|
|
||||||
if (ImGui.Checkbox("Show chat message when assigning a venture to a retainer",
|
|
||||||
ref showAssignmentChatMessages))
|
|
||||||
{
|
|
||||||
_configuration.ConfigUiOptions.ShowAssignmentChatMessages = showAssignmentChatMessages;
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool showContents = _configuration.ConfigUiOptions.ShowVentureListContents;
|
|
||||||
if (ImGui.Checkbox("Show Venture List preview in Groups/Retainer tabs", ref showContents))
|
|
||||||
{
|
|
||||||
_configuration.ConfigUiOptions.ShowVentureListContents = showContents;
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Textures.TextureWraps;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
|
||||||
using ImGuiNET;
|
|
||||||
using LLib;
|
|
||||||
|
|
||||||
namespace ARControl.Windows.Config;
|
|
||||||
|
|
||||||
internal sealed class RetainersTab : ITab
|
|
||||||
{
|
|
||||||
private readonly ConfigWindow _configWindow;
|
|
||||||
private readonly Configuration _configuration;
|
|
||||||
private readonly IconCache _iconCache;
|
|
||||||
|
|
||||||
public RetainersTab(ConfigWindow configWindow, Configuration configuration, IconCache iconCache)
|
|
||||||
{
|
|
||||||
_configWindow = configWindow;
|
|
||||||
_configuration = configuration;
|
|
||||||
_iconCache = iconCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Retainers###TabRetainers");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var world in _configuration.Characters
|
|
||||||
.Where(x => x.Retainers.Any(y => y.Job != 0))
|
|
||||||
.OrderBy(x => x.LocalContentId)
|
|
||||||
.GroupBy(x => x.WorldName))
|
|
||||||
{
|
|
||||||
ImGui.CollapsingHeader(world.Key,
|
|
||||||
ImGuiTreeNodeFlags.DefaultOpen | ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.Bullet);
|
|
||||||
foreach (var character in world)
|
|
||||||
{
|
|
||||||
ImGui.PushID($"Char{character.LocalContentId}");
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(ImGui.GetFontSize() * 30);
|
|
||||||
Vector4 buttonColor = ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.FrameBg));
|
|
||||||
if (character is { Type: not Configuration.CharacterType.NotManaged, Retainers.Count: > 0 })
|
|
||||||
{
|
|
||||||
if (character.Retainers.All(x => x.Managed))
|
|
||||||
buttonColor = ImGuiColors.HealerGreen;
|
|
||||||
else if (character.Retainers.All(x => !x.Managed))
|
|
||||||
buttonColor = ImGuiColors.DalamudRed;
|
|
||||||
else
|
|
||||||
buttonColor = ImGuiColors.DalamudOrange;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Book, buttonColor))
|
|
||||||
{
|
|
||||||
if (character.Type == Configuration.CharacterType.NotManaged)
|
|
||||||
{
|
|
||||||
character.Type = Configuration.CharacterType.Standalone;
|
|
||||||
character.CharacterGroupId = Guid.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
character.Type = Configuration.CharacterType.NotManaged;
|
|
||||||
character.CharacterGroupId = Guid.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (ImGui.CollapsingHeader(
|
|
||||||
$"{character.CharacterName} {(character.Type != Configuration.CharacterType.NotManaged ? $"({character.Retainers.Count(x => x.Managed)} / {character.Retainers.Count})" : "")}###{character.LocalContentId}"))
|
|
||||||
{
|
|
||||||
ImGui.Indent(_configWindow.MainIndentSize);
|
|
||||||
|
|
||||||
List<(Guid Id, string Name)> groups =
|
|
||||||
new List<(Guid Id, string Name)> { (Guid.Empty, "No Group (manually assign lists)") }
|
|
||||||
.Concat(_configuration.CharacterGroups.Select(x => (x.Id, x.Name)))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
using (var tabBar = ImRaii.TabBar("CharOptions"))
|
|
||||||
{
|
|
||||||
if (tabBar)
|
|
||||||
{
|
|
||||||
if (character.Type != Configuration.CharacterType.NotManaged)
|
|
||||||
DrawVentureListTab(character, groups);
|
|
||||||
|
|
||||||
DrawCharacterRetainersTab(character);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ImGui.Unindent(_configWindow.MainIndentSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.PopID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawVentureListTab(Configuration.CharacterConfiguration character, List<(Guid Id, string Name)> groups)
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Venture Lists");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int groupIndex = 0;
|
|
||||||
if (character.Type == Configuration.CharacterType.PartOfCharacterGroup)
|
|
||||||
groupIndex = groups.FindIndex(x => x.Id == character.CharacterGroupId);
|
|
||||||
if (ImGui.Combo("Character Group", ref groupIndex, groups.Select(x => x.Name).ToArray(),
|
|
||||||
groups.Count))
|
|
||||||
{
|
|
||||||
if (groupIndex == 0)
|
|
||||||
{
|
|
||||||
character.Type = Configuration.CharacterType.Standalone;
|
|
||||||
character.CharacterGroupId = Guid.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
character.Type = Configuration.CharacterType.PartOfCharacterGroup;
|
|
||||||
character.CharacterGroupId = groups[groupIndex].Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
if (groupIndex == 0)
|
|
||||||
{
|
|
||||||
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
|
|
||||||
character.ItemListIds ??= new();
|
|
||||||
_configWindow.DrawVentureListSelection(
|
|
||||||
character.LocalContentId.ToString(CultureInfo.InvariantCulture),
|
|
||||||
character.ItemListIds);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.TextWrapped($"Retainers will participate in the following lists:");
|
|
||||||
ImGui.Indent(_configWindow.MainIndentSize);
|
|
||||||
|
|
||||||
var group = _configuration.CharacterGroups.Single(
|
|
||||||
x => x.Id == groups[groupIndex].Id);
|
|
||||||
var lists = group.ItemListIds
|
|
||||||
.Where(listId => listId != Guid.Empty)
|
|
||||||
.Select(listId => _configuration.ItemLists.SingleOrDefault(x => x.Id == listId))
|
|
||||||
.Where(list => list != null)
|
|
||||||
.Cast<Configuration.ItemList>()
|
|
||||||
.ToList();
|
|
||||||
if (lists.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var list in lists)
|
|
||||||
ImGui.BulletText($"{list.Name}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed, "(None)");
|
|
||||||
|
|
||||||
ImGui.Unindent(_configWindow.MainIndentSize);
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCharacterRetainersTab(Configuration.CharacterConfiguration character)
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Retainers");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var retainer in character.Retainers.Where(x => x.Job > 0)
|
|
||||||
.OrderBy(x => x.DisplayOrder)
|
|
||||||
.ThenBy(x => x.RetainerContentId))
|
|
||||||
{
|
|
||||||
ImGui.BeginDisabled(retainer is { Managed: false, Level: < ConfigWindow.MinLevel });
|
|
||||||
|
|
||||||
bool managed = retainer.Managed;
|
|
||||||
|
|
||||||
IDalamudTextureWrap? icon = _iconCache.GetIcon(62000 + retainer.Job);
|
|
||||||
if (icon != null)
|
|
||||||
{
|
|
||||||
ImGui.Image(icon.ImGuiHandle, new Vector2(ImGui.GetFrameHeight()));
|
|
||||||
ImGui.SameLine(0, ImGui.GetStyle().FramePadding.X);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.Checkbox(
|
|
||||||
$"{retainer.Name}###Retainer{retainer.Name}{retainer.RetainerContentId}",
|
|
||||||
ref managed))
|
|
||||||
{
|
|
||||||
retainer.Managed = managed;
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,632 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using ARControl.External;
|
|
||||||
using ARControl.GameData;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Textures.TextureWraps;
|
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using ECommons;
|
|
||||||
using ECommons.ImGuiMethods;
|
|
||||||
using ImGuiNET;
|
|
||||||
using LLib;
|
|
||||||
|
|
||||||
namespace ARControl.Windows.Config;
|
|
||||||
|
|
||||||
internal sealed class VentureListTab : ITab
|
|
||||||
{
|
|
||||||
private static readonly string[] StockingTypeLabels = ["Collect Once", "Keep in Stock"];
|
|
||||||
|
|
||||||
private static readonly string[] PriorityLabels =
|
|
||||||
{ "Collect in order of the list", "Collect item with lowest inventory first" };
|
|
||||||
|
|
||||||
private static readonly Regex CountAndName = new(@"^(\d{1,5})x?\s+(.*)$", RegexOptions.Compiled);
|
|
||||||
private const FontAwesomeIcon WarningIcon = FontAwesomeIcon.ExclamationCircle;
|
|
||||||
private const FontAwesomeIcon ExcessCrystalsIcon = FontAwesomeIcon.Diamond;
|
|
||||||
|
|
||||||
private readonly ConfigWindow _configWindow;
|
|
||||||
private readonly Configuration _configuration;
|
|
||||||
private readonly GameCache _gameCache;
|
|
||||||
private readonly IconCache _iconCache;
|
|
||||||
private readonly DiscardHelperIpc _discardHelperIpc;
|
|
||||||
private readonly IPluginLog _pluginLog;
|
|
||||||
|
|
||||||
private string _searchString = string.Empty;
|
|
||||||
private (Guid, Guid)? _draggedItem;
|
|
||||||
|
|
||||||
private readonly Dictionary<Guid, TemporaryConfig> _currentEditPopups = new();
|
|
||||||
|
|
||||||
private TemporaryConfig _newList = new()
|
|
||||||
{
|
|
||||||
Name = string.Empty,
|
|
||||||
ListType = Configuration.ListType.CollectOneTime,
|
|
||||||
ListPriority = Configuration.ListPriority.InOrder,
|
|
||||||
CheckRetainerInventory = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
public VentureListTab(ConfigWindow configWindow, Configuration configuration, GameCache gameCache,
|
|
||||||
IconCache iconCache, DiscardHelperIpc discardHelperIpc, IPluginLog pluginLog)
|
|
||||||
{
|
|
||||||
_configWindow = configWindow;
|
|
||||||
_configuration = configuration;
|
|
||||||
_gameCache = gameCache;
|
|
||||||
_iconCache = iconCache;
|
|
||||||
_discardHelperIpc = discardHelperIpc;
|
|
||||||
_pluginLog = pluginLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Venture Lists###TabVentureLists");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Configuration.ItemList? listToDelete = null;
|
|
||||||
IReadOnlySet<uint> itemsToDiscard = _discardHelperIpc.GetItemsToDiscard();
|
|
||||||
foreach (var list in _configuration.ItemLists)
|
|
||||||
{
|
|
||||||
ImGui.PushID($"List{list.Id}");
|
|
||||||
|
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Cog))
|
|
||||||
{
|
|
||||||
_currentEditPopups[list.Id] = new TemporaryConfig
|
|
||||||
{
|
|
||||||
Name = list.Name,
|
|
||||||
ListType = list.Type,
|
|
||||||
ListPriority = list.Priority,
|
|
||||||
CheckRetainerInventory = list.CheckRetainerInventory,
|
|
||||||
};
|
|
||||||
ImGui.OpenPopup($"##EditList{list.Id}");
|
|
||||||
}
|
|
||||||
|
|
||||||
DrawVentureListEditorPopup(list, ref listToDelete);
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
string label = $"{list.Name} {list.GetIcon()}";
|
|
||||||
|
|
||||||
if (ImGui.CollapsingHeader(label))
|
|
||||||
{
|
|
||||||
ImGui.Indent(_configWindow.MainIndentSize);
|
|
||||||
DrawVentureListItemSelection(list, itemsToDiscard);
|
|
||||||
ImGui.Unindent(_configWindow.MainIndentSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.PopID();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listToDelete != null)
|
|
||||||
{
|
|
||||||
_configuration.ItemLists.Remove(listToDelete);
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
DrawNewVentureList();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void DrawVentureListEditorPopup(Configuration.ItemList list, ref Configuration.ItemList? listToDelete)
|
|
||||||
{
|
|
||||||
var assignedCharacters = _configuration.Characters
|
|
||||||
.Where(x => x.Type == Configuration.CharacterType.Standalone && x.ItemListIds.Contains(list.Id))
|
|
||||||
.OrderBy(x => x.WorldName)
|
|
||||||
.ThenBy(x => x.LocalContentId)
|
|
||||||
.ToList();
|
|
||||||
var assignedGroups = _configuration.CharacterGroups
|
|
||||||
.Where(x => x.ItemListIds.Contains(list.Id))
|
|
||||||
.ToList();
|
|
||||||
if (_currentEditPopups.TryGetValue(list.Id, out TemporaryConfig? temporaryConfig) &&
|
|
||||||
ImGui.BeginPopup($"##EditList{list.Id}"))
|
|
||||||
{
|
|
||||||
var (save, canSave) = DrawVentureListEditor(temporaryConfig, list);
|
|
||||||
ImGui.BeginDisabled(!canSave || (list.Name == temporaryConfig.Name &&
|
|
||||||
list.Type == temporaryConfig.ListType &&
|
|
||||||
list.Priority == temporaryConfig.ListPriority &&
|
|
||||||
list.CheckRetainerInventory ==
|
|
||||||
temporaryConfig.CheckRetainerInventory));
|
|
||||||
save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save");
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
if (save && canSave)
|
|
||||||
{
|
|
||||||
list.Name = temporaryConfig.Name;
|
|
||||||
list.Type = temporaryConfig.ListType;
|
|
||||||
|
|
||||||
if (list.Type == Configuration.ListType.CollectOneTime)
|
|
||||||
{
|
|
||||||
list.Priority = Configuration.ListPriority.InOrder;
|
|
||||||
list.CheckRetainerInventory = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
list.Priority = temporaryConfig.ListPriority;
|
|
||||||
list.CheckRetainerInventory = temporaryConfig.CheckRetainerInventory;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.BeginDisabled(assignedCharacters.Count > 0 || assignedGroups.Count > 0);
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, "Delete"))
|
|
||||||
{
|
|
||||||
listToDelete = list;
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
if ((assignedCharacters.Count > 0 || assignedGroups.Count > 0) &&
|
|
||||||
ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
||||||
{
|
|
||||||
ImGui.BeginTooltip();
|
|
||||||
ImGui.Text(
|
|
||||||
$"Remove this list from the {assignedCharacters.Count} character(s) and {assignedGroups.Count} group(s) using it before deleting it.");
|
|
||||||
foreach (var character in assignedCharacters)
|
|
||||||
ImGui.BulletText($"{character.CharacterName} @ {character.WorldName}");
|
|
||||||
foreach (var group in assignedGroups)
|
|
||||||
ImGui.BulletText($"{group.Name}");
|
|
||||||
ImGui.EndTooltip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawVentureListItemSelection(Configuration.ItemList list, IReadOnlySet<uint> itemsToDiscard)
|
|
||||||
{
|
|
||||||
DrawVentureListItemFilter(list);
|
|
||||||
|
|
||||||
Configuration.QueuedItem? itemToRemove = null;
|
|
||||||
Configuration.QueuedItem? itemToAdd = null;
|
|
||||||
int indexToAdd = 0;
|
|
||||||
|
|
||||||
float width = ImGui.GetContentRegionAvail().X;
|
|
||||||
List<(Vector2 TopLeft, Vector2 BottomRight)> itemPositions = [];
|
|
||||||
|
|
||||||
for (int i = 0; i < list.Items.Count; ++i)
|
|
||||||
{
|
|
||||||
Vector2 topLeft = ImGui.GetCursorScreenPos() +
|
|
||||||
new Vector2(-_configWindow.MainIndentSize, -ImGui.GetStyle().ItemSpacing.Y / 2);
|
|
||||||
|
|
||||||
var item = list.Items[i];
|
|
||||||
ImGui.PushID($"QueueItem{item.InternalId}");
|
|
||||||
var ventures = _gameCache.Ventures.Where(x => x.ItemId == item.ItemId).ToList();
|
|
||||||
var venture = ventures.First();
|
|
||||||
|
|
||||||
if (itemsToDiscard.Contains(venture.ItemId))
|
|
||||||
DrawWarning(WarningIcon, "This item will be automatically discarded by 'Discard Helper'.");
|
|
||||||
else if (item.ItemId is >= 2 and <= 13 && item.RemainingQuantity >= 10000)
|
|
||||||
{
|
|
||||||
if (list.Type == Configuration.ListType.CollectOneTime || list.CheckRetainerInventory)
|
|
||||||
DrawWarning(ExcessCrystalsIcon,
|
|
||||||
"You are responsible for manually moving shards or crystals to your retainers - ARC won't do that for you.\nIf you don't, this may lead to wasted ventures.",
|
|
||||||
ImGuiColors.ParsedBlue);
|
|
||||||
else
|
|
||||||
DrawWarning(WarningIcon, "You can never have this many of a shard or crystal in your inventory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
IDalamudTextureWrap? icon = _iconCache.GetIcon(venture.IconId);
|
|
||||||
if (icon != null)
|
|
||||||
{
|
|
||||||
ImGui.Image(icon.ImGuiHandle, new Vector2(ImGui.GetFrameHeight()));
|
|
||||||
ImGui.SameLine(0, ImGui.GetStyle().FramePadding.X);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(130 * ImGuiHelpers.GlobalScale);
|
|
||||||
int quantity = item.RemainingQuantity;
|
|
||||||
if (ImGui.InputInt($"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})",
|
|
||||||
ref quantity, 100))
|
|
||||||
{
|
|
||||||
item.RemainingQuantity = quantity;
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.Items.Count > 1)
|
|
||||||
{
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X +
|
|
||||||
_configWindow.MainIndentSize +
|
|
||||||
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 != null && _draggedItem.Value.Item1 == list.Id &&
|
|
||||||
_draggedItem.Value.Item2 == 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 = (list.Id, item.InternalId);
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X +
|
|
||||||
_configWindow.MainIndentSize +
|
|
||||||
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;
|
|
||||||
|
|
||||||
ImGui.PopID();
|
|
||||||
|
|
||||||
Vector2 bottomRight = new Vector2(topLeft.X + width + _configWindow.MainIndentSize,
|
|
||||||
ImGui.GetCursorScreenPos().Y - ImGui.GetStyle().ItemSpacing.Y + 2);
|
|
||||||
//ImGui.GetWindowDrawList().AddRect(topLeft, bottomRight, ImGui.GetColorU32(i % 2 == 0 ? ImGuiColors.DalamudRed : ImGuiColors.HealerGreen));
|
|
||||||
itemPositions.Add((topLeft, bottomRight));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ImGui.IsMouseDragging(ImGuiMouseButton.Left))
|
|
||||||
_draggedItem = null;
|
|
||||||
else if (_draggedItem != null && _draggedItem.Value.Item1 == list.Id)
|
|
||||||
{
|
|
||||||
var draggedItem = list.Items.Single(x => x.InternalId == _draggedItem.Value.Item2);
|
|
||||||
int oldIndex = list.Items.IndexOf(draggedItem);
|
|
||||||
|
|
||||||
var (topLeft, bottomRight) = itemPositions[oldIndex];
|
|
||||||
if (!itemsToDiscard.Contains(draggedItem.ItemId))
|
|
||||||
topLeft += new Vector2(_configWindow.MainIndentSize, 0);
|
|
||||||
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 = list.Items.Single(x => x.InternalId == _draggedItem.Value.Item2);
|
|
||||||
indexToAdd = newIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemToRemove != null)
|
|
||||||
{
|
|
||||||
list.Items.Remove(itemToRemove);
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemToAdd != null)
|
|
||||||
{
|
|
||||||
_pluginLog.Information($"Updating {itemToAdd.ItemId} → {indexToAdd}");
|
|
||||||
list.Items.Remove(itemToAdd);
|
|
||||||
list.Items.Insert(indexToAdd, itemToAdd);
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
List<Configuration.QueuedItem> clipboardItems = ParseClipboardItems();
|
|
||||||
ImportFromClipboardButton(list, clipboardItems);
|
|
||||||
RemoveFinishedItemsButton(list);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DrawWarning(FontAwesomeIcon icon, string tooltip, Vector4? color = null)
|
|
||||||
{
|
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
|
||||||
var pos = ImGui.GetCursorPos();
|
|
||||||
ImGui.SetCursorPos(new Vector2(pos.X - ImGui.CalcTextSize(icon.ToIconString()).X - 5, pos.Y + 2));
|
|
||||||
ImGui.TextColored(color ?? ImGuiColors.DalamudYellow, icon.ToIconString());
|
|
||||||
ImGui.SetCursorPos(pos);
|
|
||||||
ImGui.PopFont();
|
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip(tooltip);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawNewVentureList()
|
|
||||||
{
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Venture List"))
|
|
||||||
ImGui.OpenPopup("##AddList");
|
|
||||||
|
|
||||||
if (ImGui.BeginPopup("##AddList"))
|
|
||||||
{
|
|
||||||
(bool save, bool canSave) = DrawVentureListEditor(_newList, null);
|
|
||||||
|
|
||||||
ImGui.BeginDisabled(!canSave);
|
|
||||||
save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save");
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
if (save && canSave)
|
|
||||||
{
|
|
||||||
_configuration.ItemLists.Add(new Configuration.ItemList
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Name = _newList.Name,
|
|
||||||
Type = _newList.ListType,
|
|
||||||
Priority = _newList.ListPriority,
|
|
||||||
CheckRetainerInventory = _newList.CheckRetainerInventory,
|
|
||||||
});
|
|
||||||
|
|
||||||
_newList = new()
|
|
||||||
{
|
|
||||||
Name = string.Empty,
|
|
||||||
ListType = Configuration.ListType.CollectOneTime,
|
|
||||||
ListPriority = Configuration.ListPriority.InOrder,
|
|
||||||
CheckRetainerInventory = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private (bool Save, bool CanSave) DrawVentureListEditor(TemporaryConfig temporaryConfig,
|
|
||||||
Configuration.ItemList? list)
|
|
||||||
{
|
|
||||||
ImGui.SetNextItemWidth(375 * ImGuiHelpers.GlobalScale);
|
|
||||||
string listName = temporaryConfig.Name;
|
|
||||||
bool save = ImGui.InputTextWithHint("", "List Name...", ref listName, 64,
|
|
||||||
ImGuiInputTextFlags.EnterReturnsTrue);
|
|
||||||
bool canSave = IsValidListName(listName, list);
|
|
||||||
temporaryConfig.Name = listName;
|
|
||||||
|
|
||||||
ImGui.PushID($"Type{list?.Id ?? Guid.Empty}");
|
|
||||||
ImGui.SetNextItemWidth(375 * ImGuiHelpers.GlobalScale);
|
|
||||||
int type = (int)temporaryConfig.ListType;
|
|
||||||
if (ImGui.Combo("", ref type, StockingTypeLabels, StockingTypeLabels.Length))
|
|
||||||
{
|
|
||||||
temporaryConfig.ListType = (Configuration.ListType)type;
|
|
||||||
if (temporaryConfig.ListType == Configuration.ListType.CollectOneTime)
|
|
||||||
temporaryConfig.ListPriority = Configuration.ListPriority.InOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.PopID();
|
|
||||||
|
|
||||||
if (temporaryConfig.ListType == Configuration.ListType.KeepStocked)
|
|
||||||
{
|
|
||||||
ImGui.PushID($"Priority{list?.Id ?? Guid.Empty}");
|
|
||||||
ImGui.SetNextItemWidth(375 * ImGuiHelpers.GlobalScale);
|
|
||||||
int priority = (int)temporaryConfig.ListPriority;
|
|
||||||
if (ImGui.Combo("", ref priority, PriorityLabels, PriorityLabels.Length))
|
|
||||||
temporaryConfig.ListPriority = (Configuration.ListPriority)priority;
|
|
||||||
ImGui.PopID();
|
|
||||||
|
|
||||||
ImGui.PushID($"CheckRetainerInventory{list?.Id ?? Guid.Empty}");
|
|
||||||
bool checkRetainerInventory = temporaryConfig.CheckRetainerInventory;
|
|
||||||
if (ImGui.Checkbox("Check Retainer Inventory for items (requires AllaganTools)",
|
|
||||||
ref checkRetainerInventory))
|
|
||||||
temporaryConfig.CheckRetainerInventory = checkRetainerInventory;
|
|
||||||
ImGui.PopID();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (save, canSave);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawVentureListItemFilter(Configuration.ItemList list)
|
|
||||||
{
|
|
||||||
ImGuiEx.SetNextItemFullWidth();
|
|
||||||
if (ImGui.BeginCombo($"##VentureSelection{list.Id}", "Add Venture...", ImGuiComboFlags.HeightLarge))
|
|
||||||
{
|
|
||||||
ImGuiEx.SetNextItemFullWidth();
|
|
||||||
|
|
||||||
bool addFirst = ImGui.InputTextWithHint("", "Filter...", ref _searchString, 256,
|
|
||||||
ImGuiInputTextFlags.AutoSelectAll | ImGuiInputTextFlags.EnterReturnsTrue);
|
|
||||||
|
|
||||||
int quantity;
|
|
||||||
string itemName;
|
|
||||||
var regexMatch = CountAndName.Match(_searchString);
|
|
||||||
if (regexMatch.Success)
|
|
||||||
{
|
|
||||||
quantity = int.Parse(regexMatch.Groups[1].Value, CultureInfo.InvariantCulture);
|
|
||||||
itemName = regexMatch.Groups[2].Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
quantity = 0;
|
|
||||||
itemName = _searchString;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var filtered in _gameCache.Ventures
|
|
||||||
.Where(x => x.Name.Contains(itemName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
.OrderBy(x => x.Level)
|
|
||||||
.ThenBy(x => x.Name)
|
|
||||||
.ThenBy(x => x.ItemId)
|
|
||||||
.GroupBy(x => x.ItemId)
|
|
||||||
.Select(x => new
|
|
||||||
{
|
|
||||||
Venture = x.First(),
|
|
||||||
CategoryNames = x.Select(y => y.CategoryName)
|
|
||||||
}))
|
|
||||||
{
|
|
||||||
IDalamudTextureWrap? icon = _iconCache.GetIcon(filtered.Venture.IconId);
|
|
||||||
Vector2 pos = ImGui.GetCursorPos();
|
|
||||||
Vector2 iconSize = new Vector2(ImGui.GetTextLineHeight() + ImGui.GetStyle().ItemSpacing.Y);
|
|
||||||
|
|
||||||
if (icon != null)
|
|
||||||
{
|
|
||||||
ImGui.SetCursorPos(pos + new Vector2(iconSize.X + ImGui.GetStyle().FramePadding.X,
|
|
||||||
ImGui.GetStyle().ItemSpacing.Y / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool addThis = ImGui.Selectable(
|
|
||||||
$"{filtered.Venture.Name} ({string.Join(" ", filtered.CategoryNames)})##SelectVenture{filtered.Venture.RowId}");
|
|
||||||
|
|
||||||
|
|
||||||
if (icon != null)
|
|
||||||
{
|
|
||||||
ImGui.SameLine(0, 0);
|
|
||||||
ImGui.SetCursorPos(pos);
|
|
||||||
ImGui.Image(icon.ImGuiHandle, iconSize);
|
|
||||||
|
|
||||||
icon.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addThis || addFirst)
|
|
||||||
{
|
|
||||||
list.Items.Add(new Configuration.QueuedItem
|
|
||||||
{
|
|
||||||
ItemId = filtered.Venture.ItemId,
|
|
||||||
RemainingQuantity = quantity,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (addFirst)
|
|
||||||
{
|
|
||||||
addFirst = false;
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndCombo();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ImportFromClipboardButton(Configuration.ItemList list, List<Configuration.QueuedItem> clipboardItems)
|
|
||||||
{
|
|
||||||
ImGui.BeginDisabled(clipboardItems.Count == 0);
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Download, "Import from Clipboard"))
|
|
||||||
{
|
|
||||||
_pluginLog.Information($"Importing {clipboardItems.Count} clipboard items");
|
|
||||||
foreach (var item in clipboardItems)
|
|
||||||
{
|
|
||||||
var existingItem = list.Items.FirstOrDefault(x => x.ItemId == item.ItemId);
|
|
||||||
if (existingItem != null)
|
|
||||||
existingItem.RemainingQuantity += item.RemainingQuantity;
|
|
||||||
else
|
|
||||||
list.Items.Add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
|
||||||
ImGui.BeginTooltip();
|
|
||||||
ImGui.Text("Supports importing a list in a Teamcraft-compatible format.");
|
|
||||||
ImGui.Spacing();
|
|
||||||
if (clipboardItems.Count > 0)
|
|
||||||
{
|
|
||||||
ImGui.Text("Clicking this button now would add the following items:");
|
|
||||||
ImGui.Indent();
|
|
||||||
foreach (var item in clipboardItems)
|
|
||||||
ImGui.TextUnformatted(
|
|
||||||
$"{item.RemainingQuantity}x {_gameCache.Ventures.First(x => item.ItemId == x.ItemId).Name}");
|
|
||||||
ImGui.Unindent();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.Text("For example:");
|
|
||||||
ImGui.Indent();
|
|
||||||
ImGui.Text("2000x Cobalt Ore");
|
|
||||||
ImGui.Text("1000x Gold Ore");
|
|
||||||
ImGui.Unindent();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndTooltip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveFinishedItemsButton(Configuration.ItemList list)
|
|
||||||
{
|
|
||||||
if (list.Items.Count > 0 && list.Type == Configuration.ListType.CollectOneTime)
|
|
||||||
{
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.BeginDisabled(list.Items.All(x => x.RemainingQuantity > 0));
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Remove all finished items"))
|
|
||||||
{
|
|
||||||
list.Items.RemoveAll(q => q.RemainingQuantity <= 0);
|
|
||||||
_configWindow.ShouldSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndDisabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Configuration.QueuedItem> ParseClipboardItems()
|
|
||||||
{
|
|
||||||
List<Configuration.QueuedItem> clipboardItems = new List<Configuration.QueuedItem>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string? clipboardText = GetClipboardText();
|
|
||||||
if (!string.IsNullOrWhiteSpace(clipboardText))
|
|
||||||
{
|
|
||||||
foreach (var clipboardLine in clipboardText.ReplaceLineEndings().Split(Environment.NewLine))
|
|
||||||
{
|
|
||||||
var match = CountAndName.Match(clipboardLine);
|
|
||||||
if (!match.Success)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var venture = _gameCache.Ventures.FirstOrDefault(x =>
|
|
||||||
x.Name.Equals(match.Groups[2].Value, StringComparison.OrdinalIgnoreCase));
|
|
||||||
if (venture != null && int.TryParse(match.Groups[1].Value, out int quantity))
|
|
||||||
{
|
|
||||||
clipboardItems.Add(new Configuration.QueuedItem
|
|
||||||
{
|
|
||||||
ItemId = venture.ItemId,
|
|
||||||
RemainingQuantity = quantity,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_pluginLog.Warning(e, "Unable to extract clipboard text");
|
|
||||||
}
|
|
||||||
|
|
||||||
return clipboardItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsValidListName(string name, Configuration.ItemList? existingList)
|
|
||||||
{
|
|
||||||
return name.Length >= 2 &&
|
|
||||||
!name.Contains('%', StringComparison.Ordinal) &&
|
|
||||||
!_configuration.ItemLists.Any(x => x != existingList && name.EqualsIgnoreCase(x.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The default implementation for <see cref="ImGui.GetClipboardText"/> throws an NullReferenceException if the clipboard is empty, maybe also if it doesn't contain text.
|
|
||||||
/// </summary>
|
|
||||||
private unsafe string? GetClipboardText()
|
|
||||||
{
|
|
||||||
byte* ptr = ImGuiNative.igGetClipboardText();
|
|
||||||
if (ptr == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
int byteCount = 0;
|
|
||||||
while (ptr[byteCount] != 0)
|
|
||||||
++byteCount;
|
|
||||||
return Encoding.UTF8.GetString(ptr, byteCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed class TemporaryConfig
|
|
||||||
{
|
|
||||||
public required string Name { get; set; }
|
|
||||||
public Configuration.ListType ListType { get; set; }
|
|
||||||
public Configuration.ListPriority ListPriority { get; set; }
|
|
||||||
public bool CheckRetainerInventory { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,95 +1,24 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"net8.0-windows7.0": {
|
"net7.0-windows7.0": {
|
||||||
"DalamudPackager": {
|
"DalamudPackager": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.1.13, )",
|
"requested": "[2.1.12, )",
|
||||||
"resolved": "2.1.13",
|
"resolved": "2.1.12",
|
||||||
"contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
|
"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"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"autoretainerapi": {
|
"autoretainerapi": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ECommons": "[2.1.0.7, )"
|
"ECommons": "[2.1.0.1, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ecommons": {
|
"ecommons": {
|
||||||
"type": "Project"
|
"type": "Project"
|
||||||
},
|
},
|
||||||
"llib": {
|
"llib": {
|
||||||
"type": "Project",
|
"type": "Project"
|
||||||
"dependencies": {
|
|
||||||
"DalamudPackager": "[2.1.13, )"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit a63c8e7154e272374ffa03d5c801736d4229e38a
|
Subproject commit 7cb54772e3a4a60ad02520e898d1ed0e82b2a751
|
2
ECommons
2
ECommons
@ -1 +1 @@
|
|||||||
Subproject commit 677e28c0696eb13351d90d13ff27adb667b2c862
|
Subproject commit f1c688a0599b41d70230021328a575da7351cf91
|
2
LLib
2
LLib
@ -1 +1 @@
|
|||||||
Subproject commit fa3d19dde18bfd00237e66ea1eb48a60caa01b8a
|
Subproject commit 7f83ece6a0c07d0fbc1902502050563b5a8c09e4
|
@ -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