This commit is contained in:
Liza 2023-10-05 00:07:36 +02:00
parent 91b59e749c
commit 76d5db1542
Signed by: liza
GPG Key ID: 7199F8D727D55F67
12 changed files with 128 additions and 97 deletions

View File

@ -1,11 +1,8 @@
using Dalamud.Game;
using Dalamud.Logging;
using Dalamud.Plugin;
using Dalamud.Plugin;
using System;
using System.Collections.Generic;
using System.Reflection;
using Dalamud.Plugin.Services;
namespace Workshoppa.External;
@ -17,14 +14,16 @@ namespace Workshoppa.External;
internal sealed class DalamudReflector : IDisposable
{
private readonly DalamudPluginInterface _pluginInterface;
private readonly Framework _framework;
private readonly IFramework _framework;
private readonly IPluginLog _pluginLog;
private readonly Dictionary<string, IDalamudPlugin> _pluginCache = new();
private bool _pluginsChanged = false;
public DalamudReflector(DalamudPluginInterface pluginInterface, Framework framework)
public DalamudReflector(DalamudPluginInterface pluginInterface, IFramework framework, IPluginLog pluginLog)
{
_pluginInterface = pluginInterface;
_framework = framework;
_pluginLog = pluginLog;
var pm = GetPluginManager();
pm.GetType().GetEvent("OnInstalledPluginsChanged")!.AddEventHandler(pm, OnInstalledPluginsChanged);
@ -39,7 +38,7 @@ internal sealed class DalamudReflector : IDisposable
pm.GetType().GetEvent("OnInstalledPluginsChanged")!.RemoveEventHandler(pm, OnInstalledPluginsChanged);
}
private void FrameworkUpdate(Framework framework)
private void FrameworkUpdate(IFramework framework)
{
if (_pluginsChanged)
{
@ -80,7 +79,7 @@ internal sealed class DalamudReflector : IDisposable
.GetField("instance", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(t);
if (plugin == null)
{
PluginLog.Warning($"[DalamudReflector] Found requested plugin {internalName} but it was null");
_pluginLog.Warning($"[DalamudReflector] Found requested plugin {internalName} but it was null");
}
else
{
@ -98,7 +97,7 @@ internal sealed class DalamudReflector : IDisposable
{
if (!suppressErrors)
{
PluginLog.Error(e, $"Can't find {internalName} plugin: {e.Message}");
_pluginLog.Error(e, $"Can't find {internalName} plugin: {e.Message}");
}
instance = null;
@ -108,7 +107,7 @@ internal sealed class DalamudReflector : IDisposable
private void OnInstalledPluginsChanged()
{
PluginLog.Verbose("Installed plugins changed event fired");
_pluginLog.Verbose("Installed plugins changed event fired");
_pluginsChanged = true;
}
}

View File

@ -1,5 +1,4 @@
using System.Reflection;
using Dalamud.Logging;
namespace Workshoppa.External;

View File

@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Logging;
using Dalamud.Memory;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace Workshoppa.GameData;

View File

@ -2,15 +2,14 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Data;
using Dalamud.Logging;
using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets;
namespace Workshoppa.GameData;
internal sealed class WorkshopCache
{
public WorkshopCache(DataManager dataManager)
public WorkshopCache(IDataManager dataManager, IPluginLog pluginLog)
{
Task.Run(() =>
{
@ -57,7 +56,7 @@ internal sealed class WorkshopCache
}
catch (Exception e)
{
PluginLog.Error(e, "Unable to load cached items");
pluginLog.Error(e, "Unable to load cached items");
}
});
}

View File

@ -1,11 +1,13 @@
using System;
using System.Linq;
using System.Numerics;
using Dalamud.Game.ClientState;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using ImGuiNET;
using Workshoppa.GameData;
@ -15,13 +17,13 @@ internal sealed class MainWindow : Window
{
private readonly WorkshopPlugin _plugin;
private readonly DalamudPluginInterface _pluginInterface;
private readonly ClientState _clientState;
private readonly IClientState _clientState;
private readonly Configuration _configuration;
private readonly WorkshopCache _workshopCache;
private string _searchString = string.Empty;
public MainWindow(WorkshopPlugin plugin, DalamudPluginInterface pluginInterface, ClientState clientState, Configuration configuration, WorkshopCache workshopCache)
public MainWindow(WorkshopPlugin plugin, DalamudPluginInterface pluginInterface, IClientState clientState, Configuration configuration, WorkshopCache workshopCache)
: base("Workshoppa###WorkshoppaMainWindow")
{
_plugin = plugin;
@ -56,9 +58,13 @@ internal sealed class MainWindow : Window
var currentCraft = _workshopCache.Crafts.Single(x => x.WorkshopItemId == currentItem.WorkshopItemId);
ImGui.Text($"Currently Crafting: {currentCraft.Name}");
ImGui.BeginDisabled(!NearFabricationStation);
if (_plugin.CurrentStage == Stage.Stopped)
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Search, "Check Material"))
ImGui.OpenPopup(nameof(CheckMaterial));
ImGui.SameLine();
ImGui.BeginDisabled(!NearFabricationStation);
ImGui.BeginDisabled(!IsDiscipleOfHand);
if (currentItem.StartedCrafting)
{
@ -85,6 +91,10 @@ internal sealed class MainWindow : Window
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled) && !ImGui.GetIO().KeyCtrl)
ImGui.SetTooltip(
$"Hold CTRL to remove this as craft. You have to manually use the fabrication station to cancel or finish this craft before you can continue using the queue.");
ImGui.EndDisabled();
if (!IsDiscipleOfHand)
ImGui.TextColored(ImGuiColors.DalamudRed, "You need to be a Disciple of the Hand to start crafting.");
}
else
{
@ -94,16 +104,28 @@ internal sealed class MainWindow : Window
ImGui.EndDisabled();
}
ImGui.EndDisabled();
}
else
{
ImGui.Text("Currently Crafting: ---");
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Search, "Check Material"))
ImGui.OpenPopup(nameof(CheckMaterial));
ImGui.SameLine();
ImGui.BeginDisabled(!NearFabricationStation || _configuration.ItemQueue.Sum(x => x.Quantity) == 0 || _plugin.CurrentStage != Stage.Stopped || !IsDiscipleOfHand);
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Play, "Start Crafting"))
State = ButtonState.Start;
ImGui.EndDisabled();
if (!IsDiscipleOfHand)
ImGui.TextColored(ImGuiColors.DalamudRed, "You need to be a Disciple of the Hand to start crafting.");
}
if (ImGui.BeginPopup(nameof(CheckMaterial)))
{
CheckMaterial();
ImGui.EndPopup();
}
ImGui.Separator();
@ -176,6 +198,36 @@ internal sealed class MainWindow : Window
_pluginInterface.SavePluginConfig(_configuration);
}
private unsafe void CheckMaterial()
{
if (_configuration.CurrentlyCraftedItem != null)
ImGui.Text("Items needed for all crafts in queue (not including current in-progress craft):");
else
ImGui.Text("Items needed for all crafts in queue:");
var items = _configuration.ItemQueue
.SelectMany(x =>
Enumerable.Range(0, x.Quantity).Select(_ =>
_workshopCache.Crafts.Single(y => y.WorkshopItemId == x.WorkshopItemId)))
.SelectMany(x => x.Phases)
.SelectMany(x => x.Items)
.GroupBy(x => new { x.Name, x.ItemId })
.OrderBy(x => x.Key.Name);
ImGui.Indent(20);
InventoryManager* inventoryManager = InventoryManager.Instance();
foreach (var item in items)
{
int inInventory = inventoryManager->GetInventoryItemCount(item.Key.ItemId, true, false, false) +
inventoryManager->GetInventoryItemCount(item.Key.ItemId, false, false, false);
int required = item.Sum(x => x.TotalQuantity);
ImGui.TextColored(inInventory >= required ? ImGuiColors.HealerGreen : ImGuiColors.DalamudRed,
$"{item.Key.Name} ({inInventory} / {required})");
}
ImGui.Unindent(20);
}
public enum ButtonState
{
None,

View File

@ -1,6 +1,5 @@
using System;
using System.Linq;
using Dalamud.Logging;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Workshoppa.GameData;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
@ -20,19 +19,19 @@ partial class WorkshopPlugin
}
else if (SelectSelectString("advance", 0, s => s.StartsWith("Advance to the next phase of production.")))
{
PluginLog.Information("Phase is complete");
_pluginLog.Information("Phase is complete");
CurrentStage = Stage.TargetFabricationStation;
_continueAt = DateTime.Now.AddSeconds(3);
}
else if (SelectSelectString("complete", 0, s => s.StartsWith("Complete the construction of")))
{
PluginLog.Information("Item is almost complete, confirming last cutscene");
_pluginLog.Information("Item is almost complete, confirming last cutscene");
CurrentStage = Stage.TargetFabricationStation;
_continueAt = DateTime.Now.AddSeconds(3);
}
else if (SelectSelectString("collect", 0, s => s == "Collect finished product."))
{
PluginLog.Information("Item is complete");
_pluginLog.Information("Item is complete");
CurrentStage = Stage.ConfirmCollectProduct;
_continueAt = DateTime.Now.AddSeconds(0.25);
}
@ -47,7 +46,7 @@ partial class WorkshopPlugin
CraftState? craftState = ReadCraftState(addonMaterialDelivery);
if (craftState == null || craftState.ResultItem == 0)
{
PluginLog.Warning("Could not parse craft state");
_pluginLog.Warning("Could not parse craft state");
_continueAt = DateTime.Now.AddSeconds(1);
return;
}
@ -60,12 +59,12 @@ partial class WorkshopPlugin
if (!HasItemInSingleSlot(item.ItemId, item.ItemCountPerStep))
{
PluginLog.Error($"Can't contribute item {item.ItemId} to craft, couldn't find {item.ItemCountPerStep}x in a single inventory slot");
_pluginLog.Error($"Can't contribute item {item.ItemId} to craft, couldn't find {item.ItemCountPerStep}x in a single inventory slot");
CurrentStage = Stage.RequestStop;
break;
}
PluginLog.Information($"Contributing {item.ItemCountPerStep}x {item.ItemName}");
_pluginLog.Information($"Contributing {item.ItemCountPerStep}x {item.ItemName}");
_contributingItemId = item.ItemId;
var contributeMaterial = stackalloc AtkValue[]
{
@ -90,11 +89,19 @@ partial class WorkshopPlugin
CraftState? craftState = ReadCraftState(addonMaterialDelivery);
if (craftState == null || craftState.ResultItem == 0)
{
PluginLog.Warning("Could not parse craft state");
_pluginLog.Warning("Could not parse craft state");
_continueAt = DateTime.Now.AddSeconds(1);
return;
}
if (SelectSelectYesno(0, s => s == "Do you really want to trade a high-quality item?"))
{
_pluginLog.Information("Confirming HQ item turn in");
CurrentStage = Stage.ConfirmMaterialDelivery;
_continueAt = DateTime.Now.AddSeconds(0.1);
return;
}
if (SelectSelectYesno(0, s => s.StartsWith("Contribute") && s.EndsWith("to the company project?")))
{
var item = craftState.Items.Single(x => x.ItemId == _contributingItemId);
@ -112,7 +119,7 @@ partial class WorkshopPlugin
}
else if (DateTime.Now > _continueAt.AddSeconds(20))
{
PluginLog.Warning("No confirmation dialog, falling back to previous stage");
_pluginLog.Warning("No confirmation dialog, falling back to previous stage");
CurrentStage = Stage.ContributeMaterials;
}
}

View File

@ -1,7 +1,6 @@
using System;
using System.Linq;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using FFXIVClientStructs.FFXIV.Component.GUI;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
@ -61,7 +60,7 @@ partial class WorkshopPlugin
return;
var craft = GetCurrentCraft();
PluginLog.Information($"Selecting category {craft.Category} and type {craft.Type}");
_pluginLog.Information($"Selecting category {craft.Category} and type {craft.Type}");
var selectCategory = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 2 },
@ -98,12 +97,12 @@ partial class WorkshopPlugin
if (visibleItems.All(x => x.WorkshopItemId != craft.WorkshopItemId))
{
PluginLog.Error($"Could not find {craft.Name} in current list, is it unlocked?");
_pluginLog.Error($"Could not find {craft.Name} in current list, is it unlocked?");
CurrentStage = Stage.RequestStop;
return;
}
PluginLog.Information($"Selecting craft {craft.WorkshopItemId}");
_pluginLog.Information($"Selecting craft {craft.WorkshopItemId}");
var selectCraft = stackalloc AtkValue[]
{
new() { Type = ValueType.Int, Int = 1 },

View File

@ -5,7 +5,6 @@ using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Dalamud.Memory;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Control;
@ -20,7 +19,7 @@ partial class WorkshopPlugin
{
private unsafe void InteractWithTarget(GameObject obj)
{
PluginLog.Information($"Setting target to {obj}");
_pluginLog.Information($"Setting target to {obj}");
/*
if (_targetManager.Target == null || _targetManager.Target != obj)
{
@ -109,12 +108,9 @@ partial class WorkshopPlugin
var unitManagers = &AtkStage.GetSingleton()->RaptureAtkUnitManager->AtkUnitManager.DepthLayerOneList;
for (var i = 0; i < 18; i++)
{
var unitManager = &unitManagers[i];
var unitBaseArray = &(unitManager->AtkUnitEntries);
for (var j = 0; j < unitManager->Count; j++)
foreach (AtkUnitBase* unitBase in unitManagers[i].EntriesSpan)
{
var unitBase = unitBaseArray[j];
if (unitBase->ID == id)
if (unitBase != null && unitBase->ID == id)
{
return unitBase;
}
@ -138,7 +134,7 @@ partial class WorkshopPlugin
return false;
var text = MemoryHelper.ReadSeStringNullTerminated((nint)textPointer).ToString();
PluginLog.Information($"SelectSelectString for {marker}, Choice would be '{text}'");
_pluginLog.Verbose($"SelectSelectString for {marker}, Choice would be '{text}'");
if (predicate(text))
{
addonSelectString->AtkUnitBase.FireCallbackInt(choice);
@ -158,13 +154,13 @@ partial class WorkshopPlugin
text = text.Replace("\n", "").Replace("\r", "");
if (predicate(text))
{
PluginLog.Information($"Selecting choice {choice} for '{text}'");
_pluginLog.Information($"Selecting choice {choice} for '{text}'");
addonSelectYesno->AtkUnitBase.FireCallbackInt(choice);
return true;
}
else
{
PluginLog.Warning($"Text {text} does not match");
_pluginLog.Verbose($"Text {text} does not match");
}
}
@ -218,7 +214,7 @@ partial class WorkshopPlugin
}
catch (Exception e)
{
PluginLog.Warning(e, "Could not parse CompanyCraftMaterial info");
_pluginLog.Warning(e, "Could not parse CompanyCraftMaterial info");
}
return null;

View File

@ -1,26 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Command;
using Dalamud.Game.Gui;
using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Dalamud.Memory;
using Dalamud.Plugin;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Dalamud.Plugin.Services;
using Workshoppa.External;
using Workshoppa.GameData;
using Workshoppa.Windows;
@ -35,12 +21,13 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
private readonly WindowSystem _windowSystem = new WindowSystem(nameof(WorkshopPlugin));
private readonly DalamudPluginInterface _pluginInterface;
private readonly GameGui _gameGui;
private readonly Framework _framework;
private readonly Condition _condition;
private readonly ClientState _clientState;
private readonly ObjectTable _objectTable;
private readonly CommandManager _commandManager;
private readonly IGameGui _gameGui;
private readonly IFramework _framework;
private readonly ICondition _condition;
private readonly IClientState _clientState;
private readonly IObjectTable _objectTable;
private readonly ICommandManager _commandManager;
private readonly IPluginLog _pluginLog;
private readonly Configuration _configuration;
private readonly YesAlreadyIpc _yesAlreadyIpc;
@ -51,9 +38,9 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
private DateTime _continueAt = DateTime.MinValue;
private (bool Saved, bool? PreviousState) _yesAlreadyState = (false, null);
public WorkshopPlugin(DalamudPluginInterface pluginInterface, GameGui gameGui, Framework framework,
Condition condition, ClientState clientState, ObjectTable objectTable, DataManager dataManager,
CommandManager commandManager)
public WorkshopPlugin(DalamudPluginInterface pluginInterface, IGameGui gameGui, IFramework framework,
ICondition condition, IClientState clientState, IObjectTable objectTable, IDataManager dataManager,
ICommandManager commandManager, IPluginLog pluginLog)
{
_pluginInterface = pluginInterface;
_gameGui = gameGui;
@ -62,11 +49,12 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
_clientState = clientState;
_objectTable = objectTable;
_commandManager = commandManager;
_pluginLog = pluginLog;
var dalamudReflector = new DalamudReflector(_pluginInterface, _framework);
var dalamudReflector = new DalamudReflector(_pluginInterface, _framework, _pluginLog);
_yesAlreadyIpc = new YesAlreadyIpc(dalamudReflector);
_configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration();
_workshopCache = new WorkshopCache(dataManager);
_workshopCache = new WorkshopCache(dataManager, _pluginLog);
_mainWindow = new(this, _pluginInterface, _clientState, _configuration, _workshopCache);
_windowSystem.AddWindow(_mainWindow);
@ -74,11 +62,12 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
_pluginInterface.UiBuilder.OpenMainUi += _mainWindow.Toggle;
_framework.Update += FrameworkUpdate;
_commandManager.AddHandler("/ws", new CommandInfo(ProcessCommand));
_commandManager.AddHandler("/ws", new CommandInfo(ProcessCommand)
{
HelpMessage = "Open UI"
});
}
public string Name => "Workshop Plugin";
internal Stage CurrentStage
{
get => _currentStageInternal;
@ -86,13 +75,13 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
{
if (_currentStageInternal != value)
{
PluginLog.Information($"Changing stage from {_currentStageInternal} to {value}");
_pluginLog.Information($"Changing stage from {_currentStageInternal} to {value}");
_currentStageInternal = value;
}
}
}
private void FrameworkUpdate(Framework framework)
private void FrameworkUpdate(IFramework framework)
{
if (!_clientState.IsLoggedIn ||
!_workshopTerritories.Contains(_clientState.TerritoryType) ||
@ -183,7 +172,7 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
break;
default:
PluginLog.Warning($"Unknown stage {CurrentStage}");
_pluginLog.Warning($"Unknown stage {CurrentStage}");
break;
}
}
@ -210,19 +199,19 @@ public sealed partial class WorkshopPlugin : IDalamudPlugin
{
if (_yesAlreadyState.Saved)
{
PluginLog.Information("Not overwriting yesalready state");
_pluginLog.Information("Not overwriting yesalready state");
return;
}
_yesAlreadyState = (true, _yesAlreadyIpc.DisableIfNecessary());
PluginLog.Information($"Previous yesalready state: {_yesAlreadyState.PreviousState}");
_pluginLog.Information($"Previous yesalready state: {_yesAlreadyState.PreviousState}");
}
private void RestoreYesAlready()
{
if (_yesAlreadyState.Saved)
{
PluginLog.Information($"Restoring previous yesalready state: {_yesAlreadyState.PreviousState}");
_pluginLog.Information($"Restoring previous yesalready state: {_yesAlreadyState.PreviousState}");
if (_yesAlreadyState.PreviousState == true)
_yesAlreadyIpc.Enable();
}

View File

@ -24,7 +24,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.11"/>
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
</ItemGroup>
<ItemGroup>
@ -36,10 +36,6 @@
<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>

View File

@ -1,7 +1,7 @@
{
"Name": "Workshop Turn-In",
"Name": "Workshoppa",
"Author": "Liza Carvelli",
"Punchline": "",
"Description": "",
"Punchline": "Better Company Workshop Turn-In",
"Description": "Requires Pandora's Box (or a similar plugin) with 'Auto-select Turn-ins' enabled",
"RepoUrl": "https://git.carvel.li/liza/Workshoppa"
}

View File

@ -4,9 +4,9 @@
"net7.0-windows7.0": {
"DalamudPackager": {
"type": "Direct",
"requested": "[2.1.11, )",
"resolved": "2.1.11",
"contentHash": "9qlAWoRRTiL/geAvuwR/g6Bcbrd/bJJgVnB/RurBiyKs6srsP0bvpoo8IK+Eg8EA6jWeM6/YJWs66w4FIAzqPw=="
"requested": "[2.1.12, )",
"resolved": "2.1.12",
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
}
}
}