Merge branch 'venture-2.x'

This commit is contained in:
Liza 2023-10-12 01:40:36 +02:00
commit c7b5a7bee2
Signed by: liza
GPG Key ID: 7199F8D727D55F67
12 changed files with 1194 additions and 190 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "LLib"]
path = LLib
url = git@git.carvel.li:liza/LLib.git

View File

@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ARControl", "ARControl\ARControl.csproj", "{B33BF820-56C2-45A1-AEEC-3DCF526DBF42}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ARControl", "ARControl\ARControl.csproj", "{B33BF820-56C2-45A1-AEEC-3DCF526DBF42}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLib", "LLib\LLib.csproj", "{C00249D7-E550-4A3F-937B-D938D1D46B8A}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -12,5 +14,9 @@ Global
{B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Debug|Any CPU.Build.0 = Debug|Any CPU {B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Release|Any CPU.ActiveCfg = Release|Any CPU {B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Release|Any CPU.Build.0 = Release|Any CPU {B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Release|Any CPU.Build.0 = Release|Any CPU
{C00249D7-E550-4A3F-937B-D938D1D46B8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C00249D7-E550-4A3F-937B-D938D1D46B8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C00249D7-E550-4A3F-937B-D938D1D46B8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C00249D7-E550-4A3F-937B-D938D1D46B8A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<Version>1.0</Version> <Version>2.0</Version>
<LangVersion>11.0</LangVersion> <LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@ -16,13 +16,17 @@
<PropertyGroup> <PropertyGroup>
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath> <DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
<AutoRetainerLibPath>$(appdata)\XIVLauncher\installedPlugins\AutoRetainer\4.2.0.6\</AutoRetainerLibPath> <AutoRetainerLibPath>$(appdata)\XIVLauncher\installedPlugins\AutoRetainer\4.2.1.0\</AutoRetainerLibPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'"> <PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
<DalamudLibPath>$(DALAMUD_HOME)/</DalamudLibPath> <DalamudLibPath>$(DALAMUD_HOME)/</DalamudLibPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LLib\LLib.csproj"/>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.12"/> <PackageReference Include="DalamudPackager" Version="2.1.12"/>
</ItemGroup> </ItemGroup>
@ -48,6 +52,10 @@
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath> <HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="AutoRetainerAPI"> <Reference Include="AutoRetainerAPI">
<HintPath>$(AutoRetainerLibPath)AutoRetainerAPI.dll</HintPath> <HintPath>$(AutoRetainerLibPath)AutoRetainerAPI.dll</HintPath>
</Reference> </Reference>
@ -57,7 +65,6 @@
</ItemGroup> </ItemGroup>
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin"> <Target Name="RenameLatestZip" AfterTargets="PackagePlugin">
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/> <Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/>
</Target> </Target>

View File

@ -1,4 +1,5 @@
using System.Linq; using System.Collections.Generic;
using System.Linq;
namespace ARControl; namespace ARControl;
@ -24,7 +25,7 @@ partial class AutoRetainerControlPlugin
LocalContentId = registeredCharacterId, LocalContentId = registeredCharacterId,
CharacterName = offlineCharacterData.Name, CharacterName = offlineCharacterData.Name,
WorldName = offlineCharacterData.World, WorldName = offlineCharacterData.World,
Managed = false, Type = Configuration.CharacterType.NotManaged,
}; };
save = true; save = true;
@ -37,6 +38,7 @@ partial class AutoRetainerControlPlugin
save = true; save = true;
} }
List<string> seenRetainers = new();
foreach (var retainerData in offlineCharacterData.RetainerData) foreach (var retainerData in offlineCharacterData.RetainerData)
{ {
var retainer = character.Retainers.SingleOrDefault(x => x.Name == retainerData.Name); var retainer = character.Retainers.SingleOrDefault(x => x.Name == retainerData.Name);
@ -52,6 +54,8 @@ partial class AutoRetainerControlPlugin
character.Retainers.Add(retainer); character.Retainers.Add(retainer);
} }
seenRetainers.Add(retainer.Name);
if (retainer.DisplayOrder != retainerData.DisplayOrder) if (retainer.DisplayOrder != retainerData.DisplayOrder)
{ {
retainer.DisplayOrder = retainerData.DisplayOrder; retainer.DisplayOrder = retainerData.DisplayOrder;
@ -70,6 +74,12 @@ partial class AutoRetainerControlPlugin
save = true; save = true;
} }
if (retainer.HasVenture != retainerData.HasVenture)
{
retainer.HasVenture = retainerData.HasVenture;
save = true;
}
if (retainer.LastVenture != retainerData.VentureID) if (retainer.LastVenture != retainerData.VentureID)
{ {
retainer.LastVenture = retainerData.VentureID; retainer.LastVenture = retainerData.VentureID;
@ -96,6 +106,9 @@ partial class AutoRetainerControlPlugin
save = true; save = true;
} }
} }
if (character.Retainers.RemoveAll(x => !seenRetainers.Contains(x.Name)) > 0)
save = true;
} }
if (save) if (save)

View File

@ -1,16 +1,20 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using ARControl.GameData; using ARControl.GameData;
using ARControl.Windows; using ARControl.Windows;
using AutoRetainerAPI; using AutoRetainerAPI;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Components;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using ECommons; using ECommons;
using FFXIVClientStructs.FFXIV.Client.Game;
using ImGuiNET; using ImGuiNET;
namespace ARControl; namespace ARControl;
@ -18,6 +22,7 @@ namespace ARControl;
[SuppressMessage("ReSharper", "UnusedType.Global")] [SuppressMessage("ReSharper", "UnusedType.Global")]
public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
{ {
private const int QuickVentureId = 395;
private readonly WindowSystem _windowSystem = new(nameof(AutoRetainerControlPlugin)); private readonly WindowSystem _windowSystem = new(nameof(AutoRetainerControlPlugin));
private readonly DalamudPluginInterface _pluginInterface; private readonly DalamudPluginInterface _pluginInterface;
@ -28,12 +33,14 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
private readonly Configuration _configuration; private readonly Configuration _configuration;
private readonly GameCache _gameCache; private readonly GameCache _gameCache;
private readonly IconCache _iconCache;
private readonly VentureResolver _ventureResolver; private readonly VentureResolver _ventureResolver;
private readonly ConfigWindow _configWindow; private readonly ConfigWindow _configWindow;
private readonly AutoRetainerApi _autoRetainerApi; private readonly AutoRetainerApi _autoRetainerApi;
public AutoRetainerControlPlugin(DalamudPluginInterface pluginInterface, IDataManager dataManager, public AutoRetainerControlPlugin(DalamudPluginInterface pluginInterface, IDataManager dataManager,
IClientState clientState, IChatGui chatGui, ICommandManager commandManager, IPluginLog pluginLog) IClientState clientState, IChatGui chatGui, ICommandManager commandManager, ITextureProvider textureProvider,
IPluginLog pluginLog)
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
_clientState = clientState; _clientState = clientState;
@ -41,11 +48,15 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
_commandManager = commandManager; _commandManager = commandManager;
_pluginLog = pluginLog; _pluginLog = pluginLog;
_configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration(); _configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration { Version = 2 };
_gameCache = new GameCache(dataManager); _gameCache = new GameCache(dataManager);
_iconCache = new IconCache(textureProvider);
_ventureResolver = new VentureResolver(_gameCache, _pluginLog); _ventureResolver = new VentureResolver(_gameCache, _pluginLog);
_configWindow = new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _pluginLog); _configWindow =
new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _iconCache,
_pluginLog)
{ IsOpen = true };
_windowSystem.AddWindow(_configWindow); _windowSystem.AddWindow(_configWindow);
ECommonsMain.Init(_pluginInterface, this); ECommonsMain.Init(_pluginInterface, this);
@ -66,77 +77,237 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
} }
private void SendRetainerToVenture(string retainerName) private void SendRetainerToVenture(string retainerName)
{
var venture = GetNextVenture(retainerName, false);
if (venture == QuickVentureId)
_autoRetainerApi.SetVenture(0);
else if (venture.HasValue)
_autoRetainerApi.SetVenture(venture.Value);
}
private unsafe uint? GetNextVenture(string retainerName, bool dryRun)
{ {
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)
{ {
_pluginLog.Information("No character information found"); _pluginLog.Information("No character information found");
return null;
} }
else if (!ch.Managed)
if (ch.Type == Configuration.CharacterType.NotManaged)
{ {
_pluginLog.Information("Character is not managed"); _pluginLog.Information("Character is not managed");
return null;
} }
var retainer = ch.Retainers.SingleOrDefault(x => x.Name == retainerName);
if (retainer == null)
{
_pluginLog.Information("No retainer information found");
return null;
}
if (!retainer.Managed)
{
_pluginLog.Information("Retainer is not managed");
return null;
}
_pluginLog.Information("Checking tasks...");
Sync();
var venturesInProgress = CalculateVenturesInProgress(ch);
foreach (var inpr in venturesInProgress)
{
_pluginLog.Information($"In Progress: {inpr.Key} → {inpr.Value}");
}
IReadOnlyList<Guid> itemListIds;
if (ch.Type == Configuration.CharacterType.Standalone)
itemListIds = ch.ItemListIds;
else else
{ {
var retainer = ch.Retainers.SingleOrDefault(x => x.Name == retainerName); var group = _configuration.CharacterGroups.SingleOrDefault(x => x.Id == ch.CharacterGroupId);
if (retainer == null) if (group == null)
{ {
_pluginLog.Information("No retainer information found"); _pluginLog.Error($"Unable to resolve character group {ch.CharacterGroupId}.");
return null;
} }
else if (!retainer.Managed)
itemListIds = group.ItemListIds;
}
var itemLists = itemListIds.Where(listId => listId != Guid.Empty)
.Select(listId => _configuration.ItemLists.SingleOrDefault(x => x.Id == listId))
.Where(list => list != null)
.Cast<Configuration.ItemList>()
.ToList();
InventoryManager* inventoryManager = InventoryManager.Instance();
foreach (var list in itemLists)
{
_pluginLog.Information($"Checking ventures in list '{list.Name}'");
IReadOnlyList<StockedItem> itemsOnList;
if (list.Type == Configuration.ListType.CollectOneTime)
{ {
_pluginLog.Information("Retainer is not managed"); itemsOnList = list.Items
.Select(x => new StockedItem
{
QueuedItem = x,
InventoryCount = 0,
})
.Where(x => x.RequestedCount > 0)
.ToList()
.AsReadOnly();
} }
else else
{ {
_pluginLog.Information("Checking tasks..."); itemsOnList = list.Items
Sync(); .Select(x => new StockedItem
foreach (var queuedItem in _configuration.QueuedItems.Where(x => x.RemainingQuantity > 0))
{
_pluginLog.Information($"Checking venture info for itemId {queuedItem.ItemId}");
var (venture, reward) = _ventureResolver.ResolveVenture(ch, retainer, queuedItem);
if (reward == null)
{ {
_pluginLog.Information("Retainer can't complete venture"); QueuedItem = x,
} InventoryCount = inventoryManager->GetInventoryItemCount(x.ItemId) +
else (venturesInProgress.TryGetValue(x.ItemId, out int inProgress)
{ ? inProgress
_chatGui.Print( : 0),
$"[ARC] Sending retainer {retainerName} to collect {reward.Quantity}x {venture!.Name}."); })
_pluginLog.Information( .Where(x => x.InventoryCount <= x.RequestedCount)
$"Setting AR to use venture {venture.RowId}, which should retrieve {reward.Quantity}x {venture.Name}"); .ToList()
_autoRetainerApi.SetVenture(venture.RowId); .AsReadOnly();
retainer.LastVenture = venture.RowId; // collect items with the least current inventory first
queuedItem.RemainingQuantity = if (list.Priority == Configuration.ListPriority.Balanced)
Math.Max(0, queuedItem.RemainingQuantity - reward.Quantity); itemsOnList = itemsOnList.OrderBy(x => x.InventoryCount).ToList().AsReadOnly();
_pluginInterface.SavePluginConfig(_configuration); }
return;
}
}
// fallback: managed but no venture found _pluginLog.Information($"Found {itemsOnList.Count} items on current list");
if (retainer.LastVenture != 395) if (itemsOnList.Count == 0)
continue;
foreach (var itemOnList in itemsOnList)
{
_pluginLog.Information($"Checking venture info for itemId {itemOnList.ItemId}");
var (venture, reward) = _ventureResolver.ResolveVenture(ch, retainer, itemOnList.ItemId);
if (venture == null || reward == null)
{ {
_chatGui.Print($"[ARC] No tasks left for retainer {retainerName}, sending to Quick Venture."); _pluginLog.Information($"Retainer can't complete venture '{venture?.Name}'");
_pluginLog.Information($"No tasks left (previous venture = {retainer.LastVenture}), using QC");
_autoRetainerApi.SetVenture(395);
retainer.LastVenture = 395;
_pluginInterface.SavePluginConfig(_configuration);
} }
else else
_pluginLog.Information("Not changing venture plan, already 395"); {
_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}");
if (!dryRun)
{
retainer.HasVenture = true;
retainer.LastVenture = venture.RowId;
if (list.Type == Configuration.ListType.CollectOneTime)
{
itemOnList.RequestedCount =
Math.Max(0, itemOnList.RequestedCount - reward.Quantity);
}
_pluginInterface.SavePluginConfig(_configuration);
}
return venture.RowId;
}
} }
} }
// fallback: managed but no venture found
if (retainer.LastVenture != QuickVentureId)
{
_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 QC");
if (!dryRun)
{
retainer.HasVenture = true;
retainer.LastVenture = QuickVentureId;
_pluginInterface.SavePluginConfig(_configuration);
}
return QuickVentureId;
}
else
{
_pluginLog.Information("Not changing venture, already a quick venture");
return null;
}
}
/// <remarks>
/// 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.
/// </remarks>
private Dictionary<uint, int> CalculateVenturesInProgress(Configuration.CharacterConfiguration character)
{
Dictionary<uint, int> inProgress = new Dictionary<uint, int>();
foreach (var retainer in character.Retainers)
{
if (retainer.Managed && retainer.HasVenture && retainer.LastVenture != 0)
{
uint ventureId = retainer.LastVenture;
if (ventureId == 0)
continue;
var ventureForId = _gameCache.Ventures.SingleOrDefault(x => x.RowId == ventureId);
if (ventureForId == null)
continue;
uint itemId = ventureForId.ItemId;
var (venture, reward) = _ventureResolver.ResolveVenture(character, retainer, itemId);
if (venture == null || reward == null)
continue;
if (inProgress.TryGetValue(itemId, out int existingQuantity))
inProgress[itemId] = reward.Quantity + existingQuantity;
else
inProgress[itemId] = reward.Quantity;
}
}
return inProgress;
} }
private void RetainerTaskButtonDraw(ulong characterId, string retainerName) private void RetainerTaskButtonDraw(ulong characterId, string retainerName)
{ {
Configuration.CharacterConfiguration? characterConfiguration = Configuration.CharacterConfiguration? characterConfiguration =
_configuration.Characters.FirstOrDefault(x => x.LocalContentId == characterId); _configuration.Characters.FirstOrDefault(x => x.LocalContentId == characterId);
if (characterConfiguration is not { Managed: true }) if (characterConfiguration is not { Type: not Configuration.CharacterType.NotManaged })
return; return;
Configuration.RetainerConfiguration? retainer = Configuration.RetainerConfiguration? retainer =
@ -145,7 +316,19 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
return; return;
ImGui.SameLine(); ImGui.SameLine();
ImGuiComponents.IconButton(FontAwesomeIcon.Book); ImGui.Text(SeIconChar.Collectible.ToIconString());
if (ImGui.IsItemHovered())
{
string text = "This retainer is managed by ARC.";
if (characterConfiguration.Type == Configuration.CharacterType.PartOfCharacterGroup)
{
var group = _configuration.CharacterGroups.Single(x => x.Id == characterConfiguration.CharacterGroupId);
text += $"\n\nCharacter Group: {group.Name}";
}
ImGui.SetTooltip(text);
}
} }
private void TerritoryChanged(ushort e) => Sync(); private void TerritoryChanged(ushort e) => Sync();
@ -154,6 +337,25 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
{ {
if (arguments == "sync") if (arguments == "sync")
Sync(); Sync();
else if (arguments == "d")
{
var ch = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == _clientState.LocalContentId);
if (ch == null || ch.Type == Configuration.CharacterType.NotManaged || ch.Retainers.Count == 0)
{
_chatGui.PrintError("No character to debug.");
return;
}
string retainerName = ch.Retainers.OrderBy(x => x.DisplayOrder).First().Name;
var venture = GetNextVenture(retainerName, true);
if (venture == QuickVentureId)
_chatGui.Print($"Next venture for {retainerName} is Quick Venture.");
else if (venture.HasValue)
_chatGui.Print(
$"Next venture for {retainerName} is {_gameCache.Ventures.First(x => x.RowId == venture.Value).Name}.");
else
_chatGui.Print($"Next venture for {retainerName} is (none).");
}
else else
_configWindow.Toggle(); _configWindow.Toggle();
} }
@ -167,7 +369,21 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
_pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle; _pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle;
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw; _pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
_iconCache.Dispose();
_autoRetainerApi.Dispose(); _autoRetainerApi.Dispose();
ECommonsMain.Dispose(); ECommonsMain.Dispose();
} }
private sealed class StockedItem
{
public required Configuration.QueuedItem QueuedItem { get; set; }
public required int InventoryCount { get; set; }
public uint ItemId => QueuedItem.ItemId;
public int RequestedCount
{
get => QueuedItem.RemainingQuantity;
set => QueuedItem.RemainingQuantity = value;
}
}
} }

View File

@ -1,14 +1,49 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using Dalamud.Configuration; using Dalamud.Configuration;
using Dalamud.Game.Text;
namespace ARControl; namespace ARControl;
internal sealed class Configuration : IPluginConfiguration internal sealed class Configuration : IPluginConfiguration
{ {
public int Version { get; set; } = 1; public int Version { get; set; }
public List<QueuedItem> QueuedItems { get; set; } = new();
public List<CharacterConfiguration> Characters { get; set; } = new(); public List<CharacterConfiguration> Characters { get; set; } = new();
public List<ItemList> ItemLists { get; set; } = new();
public List<CharacterGroup> CharacterGroups { get; set; } = new();
public sealed class ItemList
{
public required Guid Id { get; set; }
public required string Name { get; set; }
public required ListType Type { get; set; } = ListType.CollectOneTime;
public required ListPriority Priority { get; set; } = ListPriority.InOrder;
public List<QueuedItem> Items { get; set; } = new();
public string GetIcon()
{
return Type switch
{
ListType.CollectOneTime => SeIconChar.BoxedNumber1.ToIconString(),
ListType.KeepStocked when Priority == ListPriority.Balanced => SeIconChar.EurekaLevel.ToIconString(),
ListType.KeepStocked => SeIconChar.Circle.ToIconString(),
_ => string.Empty
};
}
}
public enum ListType
{
CollectOneTime,
KeepStocked,
}
public enum ListPriority
{
InOrder,
Balanced,
}
public sealed class QueuedItem public sealed class QueuedItem
{ {
@ -16,12 +51,22 @@ internal sealed class Configuration : IPluginConfiguration
public required int RemainingQuantity { get; set; } public required int RemainingQuantity { get; set; }
} }
public sealed class CharacterGroup
{
public required Guid Id { get; set; }
public required string Name { get; set; }
public List<Guid> ItemListIds { get; set; } = new();
}
public sealed class CharacterConfiguration public sealed class CharacterConfiguration
{ {
public required ulong LocalContentId { get; set; } public required ulong LocalContentId { get; set; }
public required string CharacterName { get; set; } public required string CharacterName { get; set; }
public required string WorldName { get; set; } public required string WorldName { get; set; }
public required bool Managed { get; set; }
public CharacterType Type { get; set; } = CharacterType.NotManaged;
public Guid CharacterGroupId { get; set; }
public List<Guid> ItemListIds { get; set; } = new();
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();
@ -29,6 +74,21 @@ internal sealed class Configuration : IPluginConfiguration
public override string ToString() => $"{CharacterName} @ {WorldName}"; public override string ToString() => $"{CharacterName} @ {WorldName}";
} }
public enum CharacterType
{
NotManaged,
/// <summary>
/// The character's item list(s) are manually selected.
/// </summary>
Standalone,
/// <summary>
/// All item lists are managed through the character group.
/// </summary>
PartOfCharacterGroup
}
public sealed class RetainerConfiguration public sealed class RetainerConfiguration
{ {
public required string Name { get; set; } public required string Name { get; set; }
@ -36,6 +96,7 @@ internal sealed class Configuration : IPluginConfiguration
public int DisplayOrder { get; set; } public int DisplayOrder { get; set; }
public int Level { get; set; } public int Level { get; set; }
public uint Job { get; set; } public uint Job { get; set; }
public bool HasVenture { get; set; }
public uint LastVenture { get; set; } public uint LastVenture { get; set; }
public int ItemLevel { get; set; } public int ItemLevel { get; set; }
public int Gathering { get; set; } public int Gathering { get; set; }

View File

@ -14,6 +14,7 @@ internal sealed class Venture
var taskDetails = dataManager.GetExcelSheet<RetainerTaskNormal>()!.GetRow(retainerTask.Task)!; var taskDetails = dataManager.GetExcelSheet<RetainerTaskNormal>()!.GetRow(retainerTask.Task)!;
var taskParameters = retainerTask.RetainerTaskParameter.Value!; var taskParameters = retainerTask.RetainerTaskParameter.Value!;
ItemId = taskDetails.Item.Row; ItemId = taskDetails.Item.Row;
IconId = taskDetails.Item.Value!.Icon;
Name = taskDetails.Item.Value!.Name.ToString(); Name = taskDetails.Item.Value!.Name.ToString();
Level = retainerTask.RetainerLevel; Level = retainerTask.RetainerLevel;
ItemLevelCombat = retainerTask.RequiredItemLevel; ItemLevelCombat = retainerTask.RequiredItemLevel;
@ -76,6 +77,7 @@ internal sealed class Venture
} }
public uint ItemId { get; } public uint ItemId { get; }
public ushort IconId { get; }
public string Name { get; } public string Name { get; }
public byte Level { get; } public byte Level { get; }
public ushort ItemLevelCombat { get; } public ushort ItemLevelCombat { get; }

View File

@ -15,18 +15,18 @@ internal sealed class VentureResolver
} }
public (Venture?, VentureReward?) ResolveVenture(Configuration.CharacterConfiguration character, public (Venture?, VentureReward?) ResolveVenture(Configuration.CharacterConfiguration character,
Configuration.RetainerConfiguration retainer, Configuration.QueuedItem queuedItem) Configuration.RetainerConfiguration retainer, uint itemId)
{ {
var venture = _gameCache.Ventures var venture = _gameCache.Ventures
.Where(x => retainer.Level >= x.Level) .Where(x => retainer.Level >= x.Level)
.FirstOrDefault(x => x.ItemId == queuedItem.ItemId && x.MatchesJob(retainer.Job)); .FirstOrDefault(x => x.ItemId == itemId && x.MatchesJob(retainer.Job));
if (venture == null) if (venture == null)
{ {
_pluginLog.Information($"No applicable venture found for itemId {queuedItem.ItemId}"); _pluginLog.Information($"No applicable venture found for itemId {itemId}");
return (null, null); return (null, null);
} }
var itemToGather = _gameCache.ItemsToGather.FirstOrDefault(x => x.ItemId == queuedItem.ItemId); var itemToGather = _gameCache.ItemsToGather.FirstOrDefault(x => x.ItemId == itemId);
if (itemToGather != null && !character.GatheredItems.Contains(itemToGather.GatheredItemId)) if (itemToGather != null && !character.GatheredItems.Contains(itemToGather.GatheredItemId))
{ {
_pluginLog.Information($"Character hasn't gathered {venture.Name} yet"); _pluginLog.Information($"Character hasn't gathered {venture.Name} yet");
@ -34,7 +34,7 @@ internal sealed class VentureResolver
} }
_pluginLog.Information( _pluginLog.Information(
$"Found venture {venture.Name}, row = {venture.RowId}, checking if it is suitable"); $"Found venture {venture.Name}, row = {venture.RowId}, checking if we have high enough stats");
VentureReward? reward = null; VentureReward? reward = null;
if (venture.CategoryName is "MIN" or "BTN") if (venture.CategoryName is "MIN" or "BTN")
{ {

53
ARControl/IconCache.cs Normal file
View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using Dalamud.Interface.Internal;
using Dalamud.Plugin.Services;
namespace ARControl;
internal sealed class IconCache : IDisposable
{
private readonly ITextureProvider _textureProvider;
private readonly Dictionary<uint, TextureContainer> _textureWraps = new();
public IconCache(ITextureProvider textureProvider)
{
_textureProvider = textureProvider;
}
public IDalamudTextureWrap? GetIcon(uint iconId)
{
if (_textureWraps.TryGetValue(iconId, out TextureContainer? container))
return container.Texture;
var iconTex = _textureProvider.GetIcon(iconId);
if (iconTex != null)
{
if (iconTex.ImGuiHandle != nint.Zero)
{
_textureWraps[iconId] = new TextureContainer { Texture = iconTex };
return iconTex;
}
iconTex.Dispose();
}
_textureWraps[iconId] = new TextureContainer { Texture = null };
return null;
}
public void Dispose()
{
foreach (TextureContainer container in _textureWraps.Values)
container.Dispose();
_textureWraps.Clear();
}
private sealed class TextureContainer : IDisposable
{
public required IDalamudTextureWrap? Texture { get; init; }
public void Dispose() => Texture?.Dispose();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,9 @@
"requested": "[2.1.12, )", "requested": "[2.1.12, )",
"resolved": "2.1.12", "resolved": "2.1.12",
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg==" "contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
},
"llib": {
"type": "Project"
} }
} }
} }

1
LLib Submodule

@ -0,0 +1 @@
Subproject commit abbbec4f26b1a8903b0cd7aa04f00d557602eaf3