Initial Commit
This commit is contained in:
commit
161bf7e39c
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/.idea
|
||||||
|
*.user
|
16
ARControl.sln
Normal file
16
ARControl.sln
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ARControl", "ARControl\ARControl.csproj", "{B33BF820-56C2-45A1-AEEC-3DCF526DBF42}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Debug|Any CPU.ActiveCfg = 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.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
3
ARControl/.gitignore
vendored
Normal file
3
ARControl/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/dist
|
||||||
|
/obj
|
||||||
|
/bin
|
83
ARControl/ARControl.csproj
Normal file
83
ARControl/ARControl.csproj
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
|
<Version>1.0</Version>
|
||||||
|
<LangVersion>11.0</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
|
<OutputPath>dist</OutputPath>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
|
||||||
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
|
||||||
|
<AutoRetainerLibPath>$(appdata)\XIVLauncher\installedPlugins\AutoRetainer\4.1.2.5\</AutoRetainerLibPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
|
||||||
|
<DalamudLibPath>$(DALAMUD_HOME)/</DalamudLibPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Dalamud.ContextMenu" Version="1.2.3"/>
|
||||||
|
<PackageReference Include="DalamudPackager" Version="2.1.11"/>
|
||||||
|
</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="ImGuiScene">
|
||||||
|
<HintPath>$(DalamudLibPath)ImGuiScene.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>
|
||||||
|
<Reference Include="FFXIVClientStructs">
|
||||||
|
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="AutoRetainerAPI">
|
||||||
|
<HintPath>$(AutoRetainerLibPath)AutoRetainerAPI.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ECommons">
|
||||||
|
<HintPath>$(AutoRetainerLibPath)ECommons.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ClickLib">
|
||||||
|
<HintPath>$(AutoRetainerLibPath)ClickLib.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="External\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin">
|
||||||
|
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
7
ARControl/ARControl.json
Normal file
7
ARControl/ARControl.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"Name": "ARC",
|
||||||
|
"Author": "Liza Carvelli",
|
||||||
|
"Punchline": "Better AutoRetainer Venture Distribution",
|
||||||
|
"Description": "",
|
||||||
|
"RepoUrl": "https://git.carvel.li/liza/ARControl"
|
||||||
|
}
|
303
ARControl/AutoRetainerControlPlugin.cs
Normal file
303
ARControl/AutoRetainerControlPlugin.cs
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using ARControl.GameData;
|
||||||
|
using ARControl.Windows;
|
||||||
|
using AutoRetainerAPI;
|
||||||
|
using Dalamud.Data;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
|
using Dalamud.Game.Command;
|
||||||
|
using Dalamud.Game.Gui;
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Components;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
|
using Dalamud.Logging;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using ECommons;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace ARControl;
|
||||||
|
|
||||||
|
public sealed class AutoRetainerControlPlugin : IDalamudPlugin
|
||||||
|
{
|
||||||
|
private readonly WindowSystem _windowSystem = new(nameof(AutoRetainerControlPlugin));
|
||||||
|
|
||||||
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
|
private readonly DataManager _dataManager;
|
||||||
|
private readonly ClientState _clientState;
|
||||||
|
private readonly ChatGui _chatGui;
|
||||||
|
private readonly CommandManager _commandManager;
|
||||||
|
|
||||||
|
private readonly Configuration _configuration;
|
||||||
|
private readonly GameCache _gameCache;
|
||||||
|
private readonly ConfigWindow _configWindow;
|
||||||
|
private readonly AutoRetainerApi _autoRetainerApi;
|
||||||
|
|
||||||
|
public AutoRetainerControlPlugin(DalamudPluginInterface pluginInterface, DataManager dataManager,
|
||||||
|
ClientState clientState, ChatGui chatGui, CommandManager commandManager)
|
||||||
|
{
|
||||||
|
_pluginInterface = pluginInterface;
|
||||||
|
_dataManager = dataManager;
|
||||||
|
_clientState = clientState;
|
||||||
|
_chatGui = chatGui;
|
||||||
|
_commandManager = commandManager;
|
||||||
|
|
||||||
|
_configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration();
|
||||||
|
|
||||||
|
_gameCache = new GameCache(_dataManager);
|
||||||
|
_configWindow = new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager);
|
||||||
|
_windowSystem.AddWindow(_configWindow);
|
||||||
|
|
||||||
|
ECommonsMain.Init(_pluginInterface, this);
|
||||||
|
_autoRetainerApi = new();
|
||||||
|
|
||||||
|
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
|
||||||
|
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
|
||||||
|
_autoRetainerApi.OnSendRetainerToVenture += SendRetainerToVenture;
|
||||||
|
_autoRetainerApi.OnRetainerPostVentureTaskDraw += RetainerTaskButtonDraw;
|
||||||
|
_clientState.TerritoryChanged += TerritoryChanged;
|
||||||
|
_commandManager.AddHandler("/arc", new CommandInfo(ProcessCommand));
|
||||||
|
|
||||||
|
if (_autoRetainerApi.Ready)
|
||||||
|
Sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name => "ARC";
|
||||||
|
|
||||||
|
private void SendRetainerToVenture(string retainerName)
|
||||||
|
{
|
||||||
|
var ch = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == _clientState.LocalContentId);
|
||||||
|
if (ch == null)
|
||||||
|
{
|
||||||
|
PluginLog.Information("No character information found");
|
||||||
|
}
|
||||||
|
else if (!ch.Managed)
|
||||||
|
{
|
||||||
|
PluginLog.Information("Character is not managed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var retainer = ch.Retainers.SingleOrDefault(x => x.Name == retainerName);
|
||||||
|
if (retainer == null)
|
||||||
|
{
|
||||||
|
PluginLog.Information("No retainer information found");
|
||||||
|
}
|
||||||
|
else if (!retainer.Managed)
|
||||||
|
{
|
||||||
|
PluginLog.Information("Retainer is not managed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PluginLog.Information("Checking tasks...");
|
||||||
|
Sync();
|
||||||
|
foreach (var queuedItem in _configuration.QueuedItems.Where(x => x.RemainingQuantity > 0))
|
||||||
|
{
|
||||||
|
PluginLog.Information($"Checking venture info for itemId {queuedItem.ItemId}");
|
||||||
|
var venture = _gameCache.Ventures
|
||||||
|
.Where(x => retainer.Level >= x.Level)
|
||||||
|
.FirstOrDefault(x => x.ItemId == queuedItem.ItemId && x.MatchesJob(retainer.Job));
|
||||||
|
if (venture == null)
|
||||||
|
{
|
||||||
|
PluginLog.Information($"No applicable venture found for itemId {queuedItem.ItemId}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var itemToGather = _gameCache.ItemsToGather.FirstOrDefault(x => x.ItemId == queuedItem.ItemId);
|
||||||
|
if (itemToGather != null && !ch.GatheredItems.Contains(itemToGather.GatheredItemId))
|
||||||
|
{
|
||||||
|
PluginLog.Information($"Character hasn't gathered {venture.Name} yet");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PluginLog.Information(
|
||||||
|
$"Found venture {venture.Name}, row = {venture.RowId}, checking if it is suitable");
|
||||||
|
VentureReward? reward = null;
|
||||||
|
if (venture.CategoryName is "MIN" or "BTN")
|
||||||
|
{
|
||||||
|
if (retainer.Gathering >= venture.RequiredGathering)
|
||||||
|
reward = venture.Rewards.Last(
|
||||||
|
x => retainer.Perception >= x.PerceptionMinerBotanist);
|
||||||
|
}
|
||||||
|
else if (venture.CategoryName == "FSH")
|
||||||
|
{
|
||||||
|
if (retainer.Gathering >= venture.RequiredGathering)
|
||||||
|
reward = venture.Rewards.Last(
|
||||||
|
x => retainer.Perception >= x.PerceptionFisher);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (retainer.ItemLevel >= venture.ItemLevelCombat)
|
||||||
|
reward = venture.Rewards.Last(
|
||||||
|
x => retainer.ItemLevel >= x.ItemLevelCombat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reward == null)
|
||||||
|
{
|
||||||
|
PluginLog.Information(
|
||||||
|
"Retainer doesn't have enough stats for the venture and would return no items");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_chatGui.Print(
|
||||||
|
$"ARC → Overriding venture to collect {reward.Quantity}x {venture.Name}.");
|
||||||
|
PluginLog.Information(
|
||||||
|
$"Setting AR to use venture {venture.RowId}, which should retrieve {reward.Quantity}x {venture.Name}");
|
||||||
|
_autoRetainerApi.SetVenture(venture.RowId);
|
||||||
|
|
||||||
|
queuedItem.RemainingQuantity =
|
||||||
|
Math.Max(0, queuedItem.RemainingQuantity - reward.Quantity);
|
||||||
|
_pluginInterface.SavePluginConfig(_configuration);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback: managed but no venture found
|
||||||
|
if (retainer.LastVenture != 395)
|
||||||
|
{
|
||||||
|
_chatGui.Print("ARC → No tasks left, using QC");
|
||||||
|
PluginLog.Information($"No tasks left (previous venture = {retainer.LastVenture}), using QC");
|
||||||
|
_autoRetainerApi.SetVenture(395);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
PluginLog.Information("Not changing venture plan, already 395");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RetainerTaskButtonDraw(ulong characterId, string retainerName)
|
||||||
|
{
|
||||||
|
Configuration.CharacterConfiguration? characterConfiguration = _configuration.Characters.FirstOrDefault(x => x.LocalContentId == characterId);
|
||||||
|
if (characterConfiguration is not { Managed: true })
|
||||||
|
return;
|
||||||
|
|
||||||
|
Configuration.RetainerConfiguration? retainer = characterConfiguration.Retainers.FirstOrDefault(x => x.Name == retainerName);
|
||||||
|
if (retainer is not { Managed: true })
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiComponents.IconButton(FontAwesomeIcon.Book);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TerritoryChanged(object? sender, ushort e) => Sync();
|
||||||
|
|
||||||
|
public void Sync()
|
||||||
|
{
|
||||||
|
bool save = false;
|
||||||
|
|
||||||
|
// FIXME This should have a way to get blacklisted character ids
|
||||||
|
foreach (ulong registeredCharacterId in _autoRetainerApi.GetRegisteredCharacters())
|
||||||
|
{
|
||||||
|
PluginLog.Information($"ch → {registeredCharacterId:X}");
|
||||||
|
var offlineCharacterData = _autoRetainerApi.GetOfflineCharacterData(registeredCharacterId);
|
||||||
|
if (offlineCharacterData.ExcludeRetainer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var character = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == registeredCharacterId);
|
||||||
|
if (character == null)
|
||||||
|
{
|
||||||
|
character = new Configuration.CharacterConfiguration
|
||||||
|
{
|
||||||
|
LocalContentId = registeredCharacterId,
|
||||||
|
CharacterName = offlineCharacterData.Name,
|
||||||
|
WorldName = offlineCharacterData.World,
|
||||||
|
Managed = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
save = true;
|
||||||
|
_configuration.Characters.Add(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character.GatheredItems != offlineCharacterData.UnlockedGatheringItems)
|
||||||
|
{
|
||||||
|
character.GatheredItems = offlineCharacterData.UnlockedGatheringItems;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var retainerData in offlineCharacterData.RetainerData)
|
||||||
|
{
|
||||||
|
var retainer = character.Retainers.SingleOrDefault(x => x.Name == retainerData.Name);
|
||||||
|
if (retainer == null)
|
||||||
|
{
|
||||||
|
retainer = new Configuration.RetainerConfiguration
|
||||||
|
{
|
||||||
|
Name = retainerData.Name,
|
||||||
|
Managed = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
save = true;
|
||||||
|
character.Retainers.Add(retainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retainer.DisplayOrder != retainerData.DisplayOrder)
|
||||||
|
{
|
||||||
|
retainer.DisplayOrder = retainerData.DisplayOrder;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retainer.Level != retainerData.Level)
|
||||||
|
{
|
||||||
|
retainer.Level = retainerData.Level;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retainer.Job != retainerData.Job)
|
||||||
|
{
|
||||||
|
retainer.Job = retainerData.Job;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retainer.LastVenture != retainerData.VentureID)
|
||||||
|
{
|
||||||
|
retainer.LastVenture = retainerData.VentureID;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var additionalData =
|
||||||
|
_autoRetainerApi.GetAdditionalRetainerData(registeredCharacterId, retainerData.Name);
|
||||||
|
if (retainer.ItemLevel != additionalData.Ilvl)
|
||||||
|
{
|
||||||
|
retainer.ItemLevel = additionalData.Ilvl;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retainer.Gathering != additionalData.Gathering)
|
||||||
|
{
|
||||||
|
retainer.Gathering = additionalData.Gathering;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retainer.Perception != additionalData.Perception)
|
||||||
|
{
|
||||||
|
retainer.Perception = additionalData.Perception;
|
||||||
|
save = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save)
|
||||||
|
_pluginInterface.SavePluginConfig(_configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessCommand(string command, string arguments)
|
||||||
|
{
|
||||||
|
if (arguments == "sync")
|
||||||
|
Sync();
|
||||||
|
else
|
||||||
|
_configWindow.Toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_commandManager.RemoveHandler("/arc");
|
||||||
|
_clientState.TerritoryChanged -= TerritoryChanged;
|
||||||
|
_autoRetainerApi.OnRetainerPostVentureTaskDraw -= RetainerTaskButtonDraw;
|
||||||
|
_autoRetainerApi.OnSendRetainerToVenture -= SendRetainerToVenture;
|
||||||
|
_pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle;
|
||||||
|
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
|
||||||
|
|
||||||
|
_autoRetainerApi.Dispose();
|
||||||
|
ECommonsMain.Dispose();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
45
ARControl/Configuration.cs
Normal file
45
ARControl/Configuration.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Dalamud.Configuration;
|
||||||
|
|
||||||
|
namespace ARControl;
|
||||||
|
|
||||||
|
internal sealed class Configuration : IPluginConfiguration
|
||||||
|
{
|
||||||
|
public int Version { get; set; } = 1;
|
||||||
|
|
||||||
|
public List<QueuedItem> QueuedItems { get; set; } = new();
|
||||||
|
public List<CharacterConfiguration> Characters { get; set; } = new();
|
||||||
|
|
||||||
|
public sealed class QueuedItem
|
||||||
|
{
|
||||||
|
public required uint ItemId { get; set; }
|
||||||
|
public required int RemainingQuantity { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CharacterConfiguration
|
||||||
|
{
|
||||||
|
public required ulong LocalContentId { get; set; }
|
||||||
|
public required string CharacterName { get; set; }
|
||||||
|
public required string WorldName { get; set; }
|
||||||
|
public required bool Managed { get; set; }
|
||||||
|
|
||||||
|
public List<RetainerConfiguration> Retainers { get; set; } = new();
|
||||||
|
public HashSet<uint> GatheredItems { get; set; } = new();
|
||||||
|
|
||||||
|
public override string ToString() => $"{CharacterName} @ {WorldName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class RetainerConfiguration
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required bool Managed { get; set; }
|
||||||
|
public int DisplayOrder { get; set; }
|
||||||
|
public int Level { get; set; }
|
||||||
|
public uint Job { get; set; }
|
||||||
|
public uint LastVenture { get; set; }
|
||||||
|
public int ItemLevel { get; set; }
|
||||||
|
public int Gathering { get; set; }
|
||||||
|
public int Perception { get; set; }
|
||||||
|
}
|
||||||
|
}
|
21
ARControl/DalamudPackager.targets
Normal file
21
ARControl/DalamudPackager.targets
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project>
|
||||||
|
<Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Debug'">
|
||||||
|
<DalamudPackager
|
||||||
|
ProjectDir="$(ProjectDir)"
|
||||||
|
OutputPath="$(OutputPath)"
|
||||||
|
AssemblyName="$(AssemblyName)"
|
||||||
|
MakeZip="false"
|
||||||
|
VersionComponents="2"/>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
|
||||||
|
<DalamudPackager
|
||||||
|
ProjectDir="$(ProjectDir)"
|
||||||
|
OutputPath="$(OutputPath)"
|
||||||
|
AssemblyName="$(AssemblyName)"
|
||||||
|
MakeZip="true"
|
||||||
|
VersionComponents="2"
|
||||||
|
Exclude="ARDiscard.deps.json;AutoRetainerAPI.pdb;ClickLib.pdb;ClickLib.xml;ECommons.pdb;ECommons.xml"/>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
30
ARControl/GameData/GameCache.cs
Normal file
30
ARControl/GameData/GameCache.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Data;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
|
namespace ARControl.GameData;
|
||||||
|
|
||||||
|
internal sealed class GameCache
|
||||||
|
{
|
||||||
|
public GameCache(DataManager dataManager)
|
||||||
|
{
|
||||||
|
Jobs = dataManager.GetExcelSheet<ClassJob>()!.ToDictionary(x => x.RowId, x => x.Abbreviation.ToString());
|
||||||
|
Ventures = dataManager.GetExcelSheet<RetainerTask>()!
|
||||||
|
.Where(x => x.RowId > 0 && !x.IsRandom && x.Task != 0)
|
||||||
|
.Select(x => new Venture(dataManager, x))
|
||||||
|
.ToList()
|
||||||
|
.AsReadOnly();
|
||||||
|
ItemsToGather = dataManager.GetExcelSheet<GatheringItem>()!
|
||||||
|
.Where(x => x.RowId > 0 && x.RowId < 10_000 && x.Item != 0 && x.Quest.Row == 0)
|
||||||
|
.Where(x => Ventures.Any(y => y.ItemId == x.Item))
|
||||||
|
.Select(x => new ItemToGather(dataManager, x))
|
||||||
|
.OrderBy(x => x.Name)
|
||||||
|
.ToList()
|
||||||
|
.AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<uint, string> Jobs { get; }
|
||||||
|
public IReadOnlyList<Venture> Ventures { get; }
|
||||||
|
public IReadOnlyList<ItemToGather> ItemsToGather { get; }
|
||||||
|
}
|
19
ARControl/GameData/ItemToGather.cs
Normal file
19
ARControl/GameData/ItemToGather.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using Dalamud.Data;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
|
namespace ARControl.GameData;
|
||||||
|
|
||||||
|
internal sealed class ItemToGather
|
||||||
|
{
|
||||||
|
public ItemToGather(DataManager dataManager, GatheringItem item)
|
||||||
|
{
|
||||||
|
GatheredItemId = item.RowId;
|
||||||
|
ItemId = item.Item;
|
||||||
|
Name = dataManager.GetExcelSheet<Item>()!.GetRow((uint)item.Item)!.Name.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public uint GatheredItemId { get; }
|
||||||
|
public int ItemId { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
}
|
93
ARControl/GameData/Venture.cs
Normal file
93
ARControl/GameData/Venture.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Dalamud.Data;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
|
namespace ARControl.GameData;
|
||||||
|
|
||||||
|
internal sealed class Venture
|
||||||
|
{
|
||||||
|
public Venture(DataManager dataManager, RetainerTask retainerTask)
|
||||||
|
{
|
||||||
|
RowId = retainerTask.RowId;
|
||||||
|
Category = retainerTask.ClassJobCategory.Value!;
|
||||||
|
|
||||||
|
var taskDetails = dataManager.GetExcelSheet<RetainerTaskNormal>()!.GetRow(retainerTask.Task)!;
|
||||||
|
var taskParameters = retainerTask.RetainerTaskParameter.Value!;
|
||||||
|
ItemId = taskDetails.Item.Row;
|
||||||
|
Name = taskDetails.Item.Value!.Name.ToString();
|
||||||
|
Level = retainerTask.RetainerLevel;
|
||||||
|
ItemLevelCombat = retainerTask.RequiredItemLevel;
|
||||||
|
RequiredGathering = retainerTask.RequiredGathering;
|
||||||
|
Rewards = new List<VentureReward>
|
||||||
|
{
|
||||||
|
new VentureReward
|
||||||
|
{
|
||||||
|
Quantity = taskDetails.Quantity[0],
|
||||||
|
ItemLevelCombat = 0,
|
||||||
|
PerceptionMinerBotanist = 0,
|
||||||
|
PerceptionFisher = 0,
|
||||||
|
},
|
||||||
|
new VentureReward
|
||||||
|
{
|
||||||
|
Quantity = taskDetails.Quantity[1],
|
||||||
|
ItemLevelCombat = taskParameters.ItemLevelDoW[0],
|
||||||
|
PerceptionMinerBotanist = taskParameters.PerceptionDoL[0],
|
||||||
|
PerceptionFisher = taskParameters.PerceptionFSH[0],
|
||||||
|
},
|
||||||
|
new VentureReward
|
||||||
|
{
|
||||||
|
Quantity = taskDetails.Quantity[2],
|
||||||
|
ItemLevelCombat = taskParameters.ItemLevelDoW[1],
|
||||||
|
PerceptionMinerBotanist = taskParameters.PerceptionDoL[1],
|
||||||
|
PerceptionFisher = taskParameters.PerceptionFSH[1],
|
||||||
|
},
|
||||||
|
new VentureReward
|
||||||
|
{
|
||||||
|
Quantity = taskDetails.Quantity[3],
|
||||||
|
ItemLevelCombat = taskParameters.ItemLevelDoW[2],
|
||||||
|
PerceptionMinerBotanist = taskParameters.PerceptionDoL[2],
|
||||||
|
PerceptionFisher = taskParameters.PerceptionFSH[2],
|
||||||
|
},
|
||||||
|
new VentureReward
|
||||||
|
{
|
||||||
|
Quantity = taskDetails.Quantity[4],
|
||||||
|
ItemLevelCombat = taskParameters.ItemLevelDoW[3],
|
||||||
|
PerceptionMinerBotanist = taskParameters.PerceptionDoL[3],
|
||||||
|
PerceptionFisher = taskParameters.PerceptionFSH[3],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint RowId { get; }
|
||||||
|
public ClassJobCategory Category { get; }
|
||||||
|
|
||||||
|
public string? CategoryName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Category.RowId switch
|
||||||
|
{
|
||||||
|
17 => "MIN",
|
||||||
|
18 => "BTN",
|
||||||
|
19 => "FSH",
|
||||||
|
_ => "DoWM",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint ItemId { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
public byte Level { get; }
|
||||||
|
public ushort ItemLevelCombat { get; }
|
||||||
|
public ushort RequiredGathering { get; set; }
|
||||||
|
|
||||||
|
public List<VentureReward> Rewards { get; }
|
||||||
|
|
||||||
|
public bool MatchesJob(uint job)
|
||||||
|
{
|
||||||
|
if (Category.RowId >= 17 && Category.RowId <= 19)
|
||||||
|
return Category.RowId == job + 1;
|
||||||
|
else
|
||||||
|
return job is < 16 or > 18;
|
||||||
|
}
|
||||||
|
}
|
9
ARControl/GameData/VentureReward.cs
Normal file
9
ARControl/GameData/VentureReward.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace ARControl.GameData;
|
||||||
|
|
||||||
|
internal sealed class VentureReward
|
||||||
|
{
|
||||||
|
public required byte Quantity { get; init; }
|
||||||
|
public required short ItemLevelCombat { get; init; }
|
||||||
|
public required short PerceptionMinerBotanist { get; init; }
|
||||||
|
public required short PerceptionFisher { get; init; }
|
||||||
|
}
|
408
ARControl/Windows/ConfigWindow.cs
Normal file
408
ARControl/Windows/ConfigWindow.cs
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using ARControl.GameData;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
|
using Dalamud.Game.Command;
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Components;
|
||||||
|
using Dalamud.Interface.Style;
|
||||||
|
using Dalamud.Interface.Windowing;
|
||||||
|
using Dalamud.Logging;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using ECommons.ImGuiMethods;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace ARControl.Windows;
|
||||||
|
|
||||||
|
internal sealed class ConfigWindow : Window
|
||||||
|
{
|
||||||
|
private const byte MaxLevel = 90;
|
||||||
|
|
||||||
|
private static readonly Vector4 ColorGreen = ImGuiColors.HealerGreen;
|
||||||
|
private static readonly Vector4 ColorRed = ImGuiColors.DalamudRed;
|
||||||
|
private static readonly Vector4 ColorGrey = ImGuiColors.DalamudGrey;
|
||||||
|
|
||||||
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
|
private readonly Configuration _configuration;
|
||||||
|
private readonly GameCache _gameCache;
|
||||||
|
private readonly ClientState _clientState;
|
||||||
|
private readonly CommandManager _commandManager;
|
||||||
|
|
||||||
|
private string _searchString = string.Empty;
|
||||||
|
private Configuration.QueuedItem? _dragDropSource;
|
||||||
|
private bool _enableDragDrop;
|
||||||
|
private bool _checkPerCharacter = true;
|
||||||
|
private bool _onlyShowMissing = true;
|
||||||
|
|
||||||
|
public ConfigWindow(
|
||||||
|
DalamudPluginInterface pluginInterface,
|
||||||
|
Configuration configuration,
|
||||||
|
GameCache gameCache,
|
||||||
|
ClientState clientState,
|
||||||
|
CommandManager commandManager)
|
||||||
|
: base("ARC###ARControlConfig")
|
||||||
|
{
|
||||||
|
_pluginInterface = pluginInterface;
|
||||||
|
_configuration = configuration;
|
||||||
|
_gameCache = gameCache;
|
||||||
|
_clientState = clientState;
|
||||||
|
_commandManager = commandManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
if (ImGui.BeginTabBar("ARConfigTabs"))
|
||||||
|
{
|
||||||
|
DrawItemQueue();
|
||||||
|
DrawCharacters();
|
||||||
|
DrawGatheredItemsToCheck();
|
||||||
|
ImGui.EndTabBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void DrawItemQueue()
|
||||||
|
{
|
||||||
|
if (ImGui.BeginTabItem("Venture Queue"))
|
||||||
|
{
|
||||||
|
if (ImGui.BeginCombo("Venture...##VentureSelection", ""))
|
||||||
|
{
|
||||||
|
ImGuiEx.SetNextItemFullWidth();
|
||||||
|
ImGui.InputTextWithHint("", "Filter...", ref _searchString, 256);
|
||||||
|
|
||||||
|
foreach (var ventures in _gameCache.Ventures
|
||||||
|
.Where(x => x.Name.ToLower().Contains(_searchString.ToLower()))
|
||||||
|
.OrderBy(x => x.Level)
|
||||||
|
.ThenBy(x => x.Name)
|
||||||
|
.ThenBy(x => x.ItemId)
|
||||||
|
.GroupBy(x => x.ItemId))
|
||||||
|
{
|
||||||
|
var venture = ventures.First();
|
||||||
|
if (ImGui.Selectable(
|
||||||
|
$"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})##SelectVenture{venture.RowId}"))
|
||||||
|
{
|
||||||
|
_configuration.QueuedItems.Add(new Configuration.QueuedItem
|
||||||
|
{
|
||||||
|
ItemId = venture.ItemId,
|
||||||
|
RemainingQuantity = 0,
|
||||||
|
});
|
||||||
|
_searchString = string.Empty;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Checkbox("Enable Drag&Drop", ref _enableDragDrop);
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
ImGui.Indent(30);
|
||||||
|
|
||||||
|
Configuration.QueuedItem? itemToRemove = null;
|
||||||
|
Configuration.QueuedItem? itemToAdd = null;
|
||||||
|
int indexToAdd = 0;
|
||||||
|
for (int i = 0; i < _configuration.QueuedItems.Count; ++i)
|
||||||
|
{
|
||||||
|
var item = _configuration.QueuedItems[i];
|
||||||
|
ImGui.PushID($"QueueItem{i}");
|
||||||
|
var ventures = _gameCache.Ventures.Where(x => x.ItemId == item.ItemId).ToList();
|
||||||
|
var venture = ventures.First();
|
||||||
|
|
||||||
|
if (!_enableDragDrop)
|
||||||
|
{
|
||||||
|
ImGui.SetNextItemWidth(130);
|
||||||
|
int quantity = item.RemainingQuantity;
|
||||||
|
if (ImGui.InputInt($"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})",
|
||||||
|
ref quantity, 100))
|
||||||
|
{
|
||||||
|
item.RemainingQuantity = quantity;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.Selectable($"{item.RemainingQuantity}x {venture.Name}");
|
||||||
|
|
||||||
|
if (ImGui.BeginDragDropSource())
|
||||||
|
{
|
||||||
|
ImGui.SetDragDropPayload("ArcDragDrop", nint.Zero, 0);
|
||||||
|
_dragDropSource = item;
|
||||||
|
|
||||||
|
ImGui.EndDragDropSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.BeginDragDropTarget())
|
||||||
|
{
|
||||||
|
if (_dragDropSource != null && ImGui.AcceptDragDropPayload("ArcDragDrop").NativePtr != null)
|
||||||
|
{
|
||||||
|
itemToAdd = _dragDropSource;
|
||||||
|
indexToAdd = i;
|
||||||
|
|
||||||
|
_dragDropSource = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndDragDropTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.OpenPopupOnItemClick($"###ctx{i}", ImGuiPopupFlags.MouseButtonRight);
|
||||||
|
if (ImGui.BeginPopup($"###ctx{i}"))
|
||||||
|
{
|
||||||
|
if (ImGui.Selectable($"Remove {venture.Name}"))
|
||||||
|
itemToRemove = item;
|
||||||
|
|
||||||
|
ImGui.EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemToRemove != null)
|
||||||
|
{
|
||||||
|
_configuration.QueuedItems.Remove(itemToRemove);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemToAdd != null)
|
||||||
|
{
|
||||||
|
PluginLog.Information($"Updating {itemToAdd.ItemId} → {indexToAdd}");
|
||||||
|
_configuration.QueuedItems.Remove(itemToAdd);
|
||||||
|
_configuration.QueuedItems.Insert(indexToAdd, itemToAdd);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Unindent(30);
|
||||||
|
ImGui.EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawCharacters()
|
||||||
|
{
|
||||||
|
if (ImGui.BeginTabItem("Retainers"))
|
||||||
|
{
|
||||||
|
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.PushItemWidth(ImGui.GetFontSize() * 30);
|
||||||
|
Vector4 buttonColor = new Vector4();
|
||||||
|
if (character.Managed && character.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))
|
||||||
|
{
|
||||||
|
character.Managed = !character.Managed;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
if (ImGui.CollapsingHeader(
|
||||||
|
$"{character.CharacterName} {(character.Managed ? $"({character.Retainers.Count(x => x.Managed)} / {character.Retainers.Count})" : "")}###{character.LocalContentId}"))
|
||||||
|
{
|
||||||
|
ImGui.Indent(30);
|
||||||
|
foreach (var retainer in character.Retainers.Where(x => x.Job > 0).OrderBy(x => x.DisplayOrder))
|
||||||
|
{
|
||||||
|
ImGui.BeginDisabled(retainer.Level < MaxLevel);
|
||||||
|
|
||||||
|
bool managed = retainer.Managed && retainer.Level == MaxLevel;
|
||||||
|
ImGui.Text(_gameCache.Jobs[retainer.Job]);
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Checkbox($"{retainer.Name}###Retainer{retainer.Name}{retainer.DisplayOrder}",
|
||||||
|
ref managed))
|
||||||
|
{
|
||||||
|
retainer.Managed = managed;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Unindent(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGatheredItemsToCheck()
|
||||||
|
{
|
||||||
|
if (ImGui.BeginTabItem("Locked Items"))
|
||||||
|
{
|
||||||
|
ImGui.Checkbox("Group by character", ref _checkPerCharacter);
|
||||||
|
ImGui.Checkbox("Only show missing items", ref _onlyShowMissing);
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
var itemsToCheck =
|
||||||
|
_configuration.QueuedItems
|
||||||
|
.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.Managed)
|
||||||
|
.OrderBy(x => x.WorldName)
|
||||||
|
.ThenBy(x => x.LocalContentId)
|
||||||
|
.Select(x => new CheckedCharacter(x, itemsToCheck))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (_checkPerCharacter)
|
||||||
|
{
|
||||||
|
foreach (var ch in charactersToCheck.Where(x => x.ToCheck(_onlyShowMissing).Any()))
|
||||||
|
{
|
||||||
|
bool currentCharacter = _clientState.LocalContentId == ch.Character.LocalContentId;
|
||||||
|
ImGui.BeginDisabled(currentCharacter);
|
||||||
|
if (ImGuiComponents.IconButton($"SwitchChacters{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(30);
|
||||||
|
foreach (var item in itemsToCheck.Where(x =>
|
||||||
|
ch.ToCheck(_onlyShowMissing).ContainsKey(x.ItemId)))
|
||||||
|
{
|
||||||
|
var color = ch.Items[item.ItemId];
|
||||||
|
if (color != ColorGrey)
|
||||||
|
{
|
||||||
|
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
||||||
|
if (currentCharacter && color == ColorRed)
|
||||||
|
{
|
||||||
|
ImGui.Selectable(item.GatheredItem.Name);
|
||||||
|
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(item.GatheredItem.Name);
|
||||||
|
}
|
||||||
|
ImGui.PopStyleColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Unindent(30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var item in itemsToCheck.Where(x =>
|
||||||
|
charactersToCheck.Any(y => y.ToCheck(_onlyShowMissing).ContainsKey(x.ItemId))))
|
||||||
|
{
|
||||||
|
if (ImGui.CollapsingHeader($"{item.GatheredItem.Name}##Gathered{item.GatheredItem.ItemId}"))
|
||||||
|
{
|
||||||
|
ImGui.Indent(30);
|
||||||
|
foreach (var ch in charactersToCheck)
|
||||||
|
{
|
||||||
|
var color = ch.Items[item.ItemId];
|
||||||
|
if (color == ColorRed || (color == ColorGreen && !_onlyShowMissing))
|
||||||
|
ImGui.TextColored(color, ch.Character.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Unindent(30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Save()
|
||||||
|
{
|
||||||
|
_pluginInterface.SavePluginConfig(_configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class CheckedCharacter
|
||||||
|
{
|
||||||
|
public CheckedCharacter(Configuration.CharacterConfiguration character,
|
||||||
|
List<CheckedItem> itemsToCheck)
|
||||||
|
{
|
||||||
|
Character = character;
|
||||||
|
|
||||||
|
foreach (var item in itemsToCheck)
|
||||||
|
{
|
||||||
|
bool enabled = character.Retainers.Any(x => item.Ventures.Any(v => v.MatchesJob(x.Job)));
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
19
ARControl/packages.lock.json
Normal file
19
ARControl/packages.lock.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"net7.0-windows7.0": {
|
||||||
|
"Dalamud.ContextMenu": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[1.2.3, )",
|
||||||
|
"resolved": "1.2.3",
|
||||||
|
"contentHash": "ydemplF7DNcA/LLeongDVzWUD/JV0Fw3EwA2+P0jYq3Le2ZYSt4U8qyJq4FyoChqt0lFG8BxYCAzfeWp4Jmnqw=="
|
||||||
|
},
|
||||||
|
"DalamudPackager": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[2.1.11, )",
|
||||||
|
"resolved": "2.1.11",
|
||||||
|
"contentHash": "9qlAWoRRTiL/geAvuwR/g6Bcbrd/bJJgVnB/RurBiyKs6srsP0bvpoo8IK+Eg8EA6jWeM6/YJWs66w4FIAzqPw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# ARC
|
||||||
|
|
||||||
|
Instead of manually managing retainer plans for each individual character,
|
||||||
|
which can get fairly tedious fairly quickly, we optimize this a bit and
|
||||||
|
only manage "high-level" plans.
|
||||||
|
|
||||||
|
This means we create a list a la:
|
||||||
|
|
||||||
|
- 5000 Cobalt Ore
|
||||||
|
- 2000 Gold Ore
|
||||||
|
|
||||||
|
... and ARC distributes each venture automatically amongst all characters
|
||||||
|
which are included.
|
7
global.json
Normal file
7
global.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"sdk": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"rollForward": "latestMinor",
|
||||||
|
"allowPrerelease": false
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user