(2.x) Pick appropriate venture for new list types/priorities
This commit is contained in:
parent
59d6f50c18
commit
d8cfb75c33
@ -48,6 +48,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>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace ARControl;
|
namespace ARControl;
|
||||||
|
|
||||||
@ -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)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using ARControl.GameData;
|
using ARControl.GameData;
|
||||||
using ARControl.Windows;
|
using ARControl.Windows;
|
||||||
using AutoRetainerAPI;
|
using AutoRetainerAPI;
|
||||||
@ -10,6 +12,7 @@ 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;
|
||||||
@ -17,6 +20,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;
|
||||||
@ -70,72 +74,199 @@ 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.Type == Configuration.CharacterType.NotManaged)
|
|
||||||
|
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
|
||||||
/* FIXME
|
|
||||||
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 : 0),
|
||||||
{
|
})
|
||||||
_chatGui.Print(
|
.Where(x => x.InventoryCount <= x.RequestedCount)
|
||||||
$"[ARC] Sending retainer {retainerName} to collect {reward.Quantity}x {venture!.Name}.");
|
.ToList()
|
||||||
_pluginLog.Information(
|
.AsReadOnly();
|
||||||
$"Setting AR to use venture {venture.RowId}, which should retrieve {reward.Quantity}x {venture.Name}");
|
|
||||||
_autoRetainerApi.SetVenture(venture.RowId);
|
|
||||||
|
|
||||||
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(
|
||||||
|
$"[ARC] Sending retainer {retainerName} to collect {reward.Quantity}x {venture.Name}.");
|
||||||
|
_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($"[ARC] No tasks left for retainer {retainerName}, sending to Quick Venture.");
|
||||||
|
_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)
|
||||||
@ -174,6 +305,24 @@ 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();
|
||||||
}
|
}
|
||||||
@ -191,4 +340,16 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin
|
|||||||
_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,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; }
|
||||||
|
@ -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")
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user